Rumah > Java > JavaSoalan temu bual > Penemuduga: Bagaimanakah anda menanyakan 10 juta data?

Penemuduga: Bagaimanakah anda menanyakan 10 juta data?

Lepaskan: 2023-08-15 16:34:59
ke hadapan
1092 orang telah melayarinya

Baru-baru ini, saya telah melakukan temu bual olok-olok dan pengoptimuman semula untuk semua orang, dan saya mendapati ramai orang menjadi lemah pada lutut apabila melihat soalan seperti berpuluh juta data.

Mungkin sesetengah orang 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 latihan praktikal Kali ini berdasarkan MySQL 5.7.26 untuk ujian

Menyediakan data

Apa yang perlu dilakukan jika anda tiada data. ?

Anda tidak boleh menciptanya sendiri tanpa data?

Adakah sukar untuk mencipta data?

10 juta penciptaan kod?

Itu mustahil, ia terlalu perlahan, ia mungkin benar-benar membawa anda sehari suntuk untuk berlari. Anda boleh menggunakan skrip pangkalan data untuk melaksanakan dengan lebih pantas.

Create Table
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
e
Create Script Data Siscion Batch, kecekapan akan lebih cepat, dan setiap 1000 item akan dilakukan. sisipan kelompok juga akan menjadi 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

Mulakan ujian Konfigurasi komputer saya agak rendah: sanga tekanan standard win10 i5, baca dan tulis kira-kira 500MB SSD

hanya 3148000 keping data disediakan untuk ujian ini, menduduki cakera itu 5G (belum pengindeksan), dan ia berjalan selama 38 minit Pelajar dengan konfigurasi komputer yang baik boleh memasukkan berbilang titik data untuk ujian
SELECT count(1) FROM `user_operation_log`
Salin selepas log masuk

Kembalikan keputusan: 3148000

.

Tiga masa pertanyaan ialah:

  • 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 item10000 item ms1609ms16219ms
KuantitiKali pertamaKali keduaKali ketiga
10 item
50ms60ms 55ms
61ms74ms60ms
164ms180ms 100000 item
1741ms1764ms 1000000 item
16889ms17081ms

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

相同数据量,不同偏移量

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
偏移量第一次第二次第三次
10036ms40ms36ms
100031ms38ms32ms
1000053ms48ms51ms
100000622ms576ms627ms
10000004891ms5076ms4856ms

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

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

查询结果如下:

sql花费时间
第一条4818ms
第二条(无索引情况下)4329ms
第二条(有索引情况下)199ms
第三条(无索引情况下)4319ms
第三条(有索引情况下)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 dan klien MySQL saya berada pada mesin yang sama, jadi data pertanyaan serupa boleh menguji klien dan MySQL secara berasingan

PILIH *. Sedap tak baunya?

Dengan cara ini, saya ingin menambah di sini mengapa kita harus mengharamkan PILIH *. Sedap kan sebab ringkas dan tak masuk akal? SELECT *。难道简单无脑,它不香吗?

主要两点:

  1. 用 "SELECT * " 数据库需要解析更多的对象、字段、权限、属性等相关内容,在 SQL 语句复杂,硬解析较多的情况下,会对数据库造成沉重的负担。
  2. 增大网络开销,*
    Dua perkara utama:
    1. Gunakan "SELECT * " Pangkalan data perlu menghuraikan lebih banyak objek, medan, kebenaran, atribut, dsb. Kandungan, apabila pernyataan SQL adalah kompleks dan terdapat banyak penghuraian keras, ia akan menyebabkan beban berat pada pangkalan data.
  • Tingkatkan overhed rangkaian,* Kadangkala log, IconMD5 dan seumpamanya tersilap dimasukkan Useless and large medan teks, saiz penghantaran data akan meningkat secara eksponen. Terutama kerana MySQL dan aplikasi tidak berada pada mesin yang sama, overhed ini sangat jelas.

  • 🎜🎜🎜🎜

    Atas ialah kandungan terperinci Penemuduga: Bagaimanakah anda menanyakan 10 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