Penemubual: Bagaimana dengan cepat menanyakan berpuluh juta data?

Lepaskan: 2023-08-16 17:02:37
ke hadapan
1016 orang telah melayarinya

Mari kita lihat adegan temu bual dahulu:

  • Penemuduga: Mari kita bincangkan tentang 10 juta data, bagaimana anda menyoalnya?
  • Abang: Tanya terus dengan paging, gunakan paging limit.
  • Penemuduga: Pernahkah anda melakukannya secara praktikal?
  • Adik: Mesti ada

Mungkin sesetengah rakan tidak pernah menemui jadual dengan berpuluh juta data, dan mereka tidak tahu apa yang akan berlaku apabila menanyakan berpuluh juta data.

Hari ini saya akan membawa anda melalui operasi praktikal Kali ini berdasarkan versi MySQL 5.7.26 untuk ujian

Sediakan data

Apa yang perlu dilakukan jika anda tidak mempunyai data. ?

Buat

Kod untuk mencipta 10 juta? Itu mustahil, ia terlalu perlahan, dan mungkin mengambil masa sehari penuh. Anda boleh menggunakan skrip pangkalan data untuk melaksanakan dengan lebih pantas.

Buat jadual
CREATE TABLE `user_operation_log`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `ip` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `op_data` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `attr1` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `attr2` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `attr3` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `attr4` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `attr5` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `attr6` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `attr7` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `attr8` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `attr9` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `attr10` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `attr11` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `attr12` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
Salin selepas log masuk
Buat skrip data

Menggunakan sisipan kelompok, kecekapan akan menjadi lebih cepat, dan setiap 1000 item akan dilakukan Jumlah data terlalu besar, yang juga akan membawa kepada kecekapan sisipan kelompok yang perlahan

DELIMITER ;;
CREATE PROCEDURE batch_insert_log()
BEGIN
  DECLARE i INT DEFAULT 1;
  DECLARE userId INT DEFAULT 10000000;
 set @execSql = 'INSERT INTO `test`.`user_operation_log`(`user_id`, `ip`, `op_data`, `attr1`, `attr2`, `attr3`, `attr4`, `attr5`, `attr6`, `attr7`, `attr8`, `attr9`, `attr10`, `attr11`, `attr12`) VALUES';
 set @execData = '';
  WHILE i<=10000000 DO
   set @attr = "&#39;测试很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的属性&#39;";
  set @execData = concat(@execData, "(", userId + i, ", &#39;10.0.69.175&#39;, &#39;用户登录操作&#39;", ",", @attr, ",", @attr, ",", @attr, ",", @attr, ",", @attr, ",", @attr, ",", @attr, ",", @attr, ",", @attr, ",", @attr, ",", @attr, ",", @attr, ")");
  if i % 1000 = 0
  then
     set @stmtSql = concat(@execSql, @execData,";");
    prepare stmt from @stmtSql;
    execute stmt;
    DEALLOCATE prepare stmt;
    commit;
    set @execData = "";
   else
     set @execData = concat(@execData, ",");
   end if;
  SET i=i+1;
  END WHILE;

END;;
DELIMITER ;
Salin selepas log masuk

开始测试

田哥的电脑配置比较低:win10 标压渣渣i5 读写约500MB的SSD

由于配置低,本次测试只准备了3148000条数据,占用了磁盘5G(还没建索引的情况下),跑了38min,电脑配置好的同学,可以插入多点数据测试

SELECT count(1) FROM `user_operation_log`
Salin selepas log masuk

返回结果:3148000

三次查询时间分别为:

  • 14060 ms
  • 13755 ms
  • 13447 ms

普通分页查询

MySQL 支持 LIMIT 语句来选取指定的条数数据, Oracle 可以使用 ROWNUM 来选取。

MySQL分页查询语法如下:

SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset
Salin selepas log masuk
  • 第一个参数指定第一个返回记录行的偏移量
  • 第二个参数指定返回记录行的最大数目

下面我们开始测试查询结果:

SELECT * FROM `user_operation_log` LIMIT 10000, 10
Salin selepas log masuk

查询3次时间分别为:

  • 59 ms
  • 49 ms
  • 50 ms

这样看起来速度还行,不过是本地数据库,速度自然快点。

换个角度来测试

相同偏移量,不同数据量
SELECT * FROM `user_operation_log` LIMIT 10000, 10
SELECT * FROM `user_operation_log` LIMIT 10000, 100
SELECT * FROM `user_operation_log` LIMIT 10000, 1000
SELECT * FROM `user_operation_log` LIMIT 10000, 10000
SELECT * FROM `user_operation_log` LIMIT 10000, 100000
SELECT * FROM `user_operation_log` LIMIT 10000, 1000000
Salin selepas log masuk

查询时间如下:

5ms100 item1000 item1ms1ms1000000 item

Daripada keputusan di atas kita boleh membuat kesimpulan: Semakin besar jumlah data, semakin lama masa yang diperlukan

相同数据量,不同偏移量
SELECT * FROM `user_operation_log` LIMIT 100, 100
SELECT * FROM `user_operation_log` LIMIT 1000, 100
SELECT * FROM `user_operation_log` LIMIT 10000, 100
SELECT * FROM `user_operation_log` LIMIT 100000, 100
SELECT * FROM `user_operation_log` LIMIT 1000000, 100
Salin selepas log masuk
KuantitiKali pertamaKali keduaKali ketiga
10 item
50ms60ms 55ms
61ms74ms60ms
10000 item164ms180ms217ms
100000 item1609ms174ms174ms
16219ms16889ms17081ms
6ms

从上面结果可以得出结束:偏移量越大,花费时间越长

SELECT * FROM `user_operation_log` LIMIT 100, 100
SELECT id, attr FROM `user_operation_log` LIMIT 100, 100
Salin selepas log masuk

如何优化

既然我们经过上面一番的折腾,也得出了结论,针对上面两个问题:偏移大、数据量大,我们分别着手优化

优化偏移量大问题

采用子查询方式

我们可以先定位偏移位置的 id,然后再查询数据

SELECT * FROM `user_operation_log` LIMIT 1000000, 10

SELECT id FROM `user_operation_log` LIMIT 1000000, 1

SELECT * FROM `user_operation_log` WHERE id >= (SELECT id FROM `user_operation_log` LIMIT 1000000, 1) LIMIT 10
Salin selepas log masuk

查询结果如下:

.
偏移量第一次第二次第三次
10036ms40ms36ms
100031ms38ms32ms
1000053ms48ms51ms
100000622ms576ms627ms
1000000 4891ms508ms
Perkara 2 (dengan indeks) ) 199msArtikel ketiga (tanpa indeks)4319msArtikel ketiga (dengan indeks)201ms

从上面结果得出结论:

  • 第一条花费的时间最大,第三条比第一条稍微好点
  • 子查询使用索引速度更快

缺点:只适用于id递增的情况

id非递增的情况可以使用以下写法,但这种缺点是分页查询只能放在子查询里面

注意:某些 mysql 版本不支持在 in 子句中使用 limit,所以采用了多个嵌套select

SELECT * FROM `user_operation_log` WHERE id IN (SELECT t.id FROM (SELECT id FROM `user_operation_log` LIMIT 1000000, 10) AS t)
Salin selepas log masuk
采用 id 限定方式

这种方法要求更高些,id必须是连续递增,而且还得计算id的范围,然后使用 between,sql如下

SELECT * FROM `user_operation_log` WHERE id between 1000000 AND 1000100 LIMIT 100

SELECT * FROM `user_operation_log` WHERE id >= 1000000 LIMIT 100
Salin selepas log masuk

查询结果如下:

sql花费时间
第一条22ms
第二条21ms

从结果可以看出这种方式非常快

注意:这里的 LIMIT 是限制了条数,没有采用偏移量

优化数据量大问题

返回结果的数据量也会直接影响速度

SELECT * FROM `user_operation_log` LIMIT 1, 1000000

SELECT id FROM `user_operation_log` LIMIT 1, 1000000

SELECT id, user_id, ip, op_data, attr1, attr2, attr3, attr4, attr5, attr6, attr7, attr8, attr9, attr10, attr11, attr12 FROM `user_operation_log` LIMIT 1, 1000000
Salin selepas log masuk

查询结果如下:

sql 花费时间
第一条 15676ms
第二条 7298ms
第三条 15960ms

Dapat dilihat daripada keputusan bahawa dengan mengurangkan lajur yang tidak diperlukan, kecekapan pertanyaan juga boleh dipertingkatkan dengan ketara

Kelajuan pertanyaan pertama dan ketiga adalah hampir sama Pada masa ini, anda pasti akan mengeluh, jadi mengapa saya perlu tulis begitu banyak medan? , hanya * dan anda sudah selesai

Perhatikan bahawa pelayan MySQL dan klien saya berada pada mesin yang sama, jadi data pertanyaan adalah serupa boleh menguji klien dan MySQL secara berasingan

PILIH * Ianya tidak Adakah ia berbau harum?

Dengan cara ini, saya ingin menambah mengapa SELECT * harus diharamkan. Sedap kan sebab ringkas dan tak masuk akal?

Dua perkara utama:

  1. Menggunakan "SELECT *" pangkalan data perlu menghuraikan lebih banyak objek, medan, kebenaran, atribut dan kandungan lain yang berkaitan Apabila pernyataan SQL adalah kompleks dan terdapat banyak penghuraian yang sukar, ia akan menyebabkan a beban berat pada pangkalan data.
  2. Meningkatkan overhed rangkaian, * Kadangkala medan teks yang tidak berguna dan besar seperti log dan IconMD5 tersilap ditambah, dan saiz penghantaran data akan meningkat secara geometri. Terutama kerana MySQL dan aplikasi tidak berada pada mesin yang sama, overhed ini sangat jelas.

End

Akhir sekali, saya harap anda boleh mengamalkannya sendiri, dan anda pasti akan mendapat lebih banyak lagi!

Atas ialah kandungan terperinci Penemubual: Bagaimana dengan cepat menanyakan berpuluh juta data?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:Java后端技术全栈
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan