成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

聊一個 MySQL 插入死鎖問題

數據庫 MySQL
本文就以一個簡單的功能示例為大家演示一下insert into ...... select 可能引發的問題和解決方案。

在進行MySQL數據備份遷移時,很多人為了避免網絡IO的開銷而選用insert into ...... select 進行數據遷移備份,但你是否知道這種做法會存在那些隱患呢? 所以本文就以一個簡單的功能示例為大家演示一下insert into ...... select 可能引發的問題和解決方案。

問題復現

這里為了演示問題,筆者生成了一張帶有500w數據的數據表,對應DDL語句如下:

CREATE TABLE `batch_insert_test` (
  `id` int NOT NULL AUTO_INCREMENT,
  `fileid_1` varchar(100) DEFAULT NULL,
  `fileid_2` varchar(100) DEFAULT NULL,
  `fileid_3` varchar(100) DEFAULT NULL,
  `fileid_4` varchar(100) DEFAULT NULL,
  `fileid_5` varchar(100) DEFAULT NULL,
  `fileid_6` varchar(100) DEFAULT NULL,
  `fileid_7` varchar(100) DEFAULT NULL,
  `fileid_8` varchar(100) DEFAULT NULL,
  `fileid_9` varchar(100) DEFAULT NULL,
  `create_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2520001 DEFAULT CHARSET=utf8mb3 COMMENT='測試批量插入,一行數據1k左右';

使用count語句查看數據量:

select count(*)  from batch_insert_test;

稍微久等了一小會,輸出語句如下,可以看到一張表數據剛剛好達到500w:

count(*)|
--------+
 5000000|

同樣的我們給出備份遷移表的DDL,表結構是一樣的,唯一區別就是表名后綴多了個bak:

CREATE TABLE `batch_insert_test_bak` (
  `id` int NOT NULL AUTO_INCREMENT,
  `fileid_1` varchar(100) DEFAULT NULL,
  `fileid_2` varchar(100) DEFAULT NULL,
  `fileid_3` varchar(100) DEFAULT NULL,
  `fileid_4` varchar(100) DEFAULT NULL,
  `fileid_5` varchar(100) DEFAULT NULL,
  `fileid_6` varchar(100) DEFAULT NULL,
  `fileid_7` varchar(100) DEFAULT NULL,
  `fileid_8` varchar(100) DEFAULT NULL,
  `fileid_9` varchar(100) DEFAULT NULL,
  `create_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2520001 DEFAULT CHARSET=utf8mb3 COMMENT='測試批量插入,一行數據1k左右';

此時我們使用數據庫連接工具在會話窗口,執行如下遷移語句:

insert into batch_insert_test_bak   select * from batch_insert_test;

然后我們再寫一段程序模擬插入:

 @Test
    public void insert() {
        while (true) {
            BatchInsertTest batchInsertTest = new BatchInsertTest();
            batchInsertTest.setFileid1(RandomUtil.randomString(1));
            batchInsertTest.setFileid2(RandomUtil.randomString(1));
            batchInsertTest.setFileid3(RandomUtil.randomString(1));
            batchInsertTest.setFileid4(RandomUtil.randomString(1));
            batchInsertTest.setFileid5(RandomUtil.randomString(1));
            batchInsertTest.setFileid6(RandomUtil.randomString(1));
            batchInsertTest.setFileid7(RandomUtil.randomString(1));
            batchInsertTest.setFileid8(RandomUtil.randomString(1));
            batchInsertTest.setFileid9(RandomUtil.randomString(1));
            batchInsertTest.setCreateDate(new Date());

            long begin = System.currentTimeMillis();
            batchInsertTestMapper.insert(batchInsertTest);
            long end = System.currentTimeMillis();

            log.info("插入耗時:{} ms", end - begin);

            ThreadUtil.sleep(10000L);
        }
    }

從輸出結果來看,一開始插入并不是很耗時,基本都是毫秒級完成,但是隨著時間的推移,插入的耗時逐漸增加,最慢的一次數據插入竟然花費了1分多鐘:

18.546 INFO  c.s.w.WebTemplateApplicationTests:117  main                    插入耗時:148 ms
28.778 INFO  c.s.w.WebTemplateApplicationTests:117  main                    插入耗時:221 ms
38.926 INFO  c.s.w.WebTemplateApplicationTests:117  main                    插入耗時:143 ms
49.588 INFO  c.s.w.WebTemplateApplicationTests:117  main                    插入耗時:652 ms
59.763 INFO  c.s.w.WebTemplateApplicationTests:117  main                    插入耗時:166 ms
09.820 INFO  c.s.w.WebTemplateApplicationTests:117  main                    插入耗時:56 ms
19.930 INFO  c.s.w.WebTemplateApplicationTests:117  main                    插入耗時:99 ms
30.027 INFO  c.s.w.WebTemplateApplicationTests:117  main                    插入耗時:86 ms
40.145 INFO  c.s.w.WebTemplateApplicationTests:117  main                    插入耗時:113 ms
50.238 INFO  c.s.w.WebTemplateApplicationTests:117  main                    插入耗時:79 ms
17.178 INFO  c.s.w.WebTemplateApplicationTests:117  main                    插入耗時:76927 ms

原因剖析

我們不妨使用如下語句查看一下執行計劃:

explain insert into batch_insert_test_bak   select * from batch_insert_test;

可以看出無論是insert還是select都是走全表掃描,因為select查詢沒有走索引導致select子句的執行過程會針對整張表從上到下的掃描進行一個逐步鎖(S鎖)的過程,隨著時間的推移它最終就會變為全表鎖。

而insert語句也因為對于插入數量未知而上全表鎖,進而長期持有auto-inc鎖,當然因為insert的表是用于遷移備份數據的,auto-inc鎖的長時間持有對于業務來說影響不大。

id|select_type|table                |partitions|type|possible_keys|key|key_len|ref|rows   |filtered|Extra|
--+-----------+---------------------+----------+----+-------------+---+-------+---+-------+--------+-----+
 1|INSERT     |batch_insert_test_bak|          |ALL |             |   |       |   |       |        |     |
 1|SIMPLE     |batch_insert_test    |          |ALL |             |   |       |   |4692967|   100.0|     |

而select則不同,select查詢操作上的是讀鎖也就是S鎖,這使得其他事務針對掃描到的數據只能上S鎖不能上X鎖即寫鎖。

通過執行計劃可以看到我們的操作是全表掃描ALL,這也就意味著該查詢逐步上S鎖,導致一段時間后,整張表都被鎖住,使得我們的新的會話的插入語句的事務無法提交。進而導致大量連接數阻塞積壓,各種超時問題也就隨之誕生,嚴重一點就很可能導致整個業務線癱瘓:

解決方案

所以如果我們希望遷移時不鎖住全表,可以在指定在每次遷移時指定一個范圍,所以我們針對時間字段增加索引,通過縮小范圍加索引查詢避免全表鎖:

ALTER TABLE db1.batch_insert_test DROP INDEX batch_insert_test_create_date_IDX;
CREATE INDEX batch_insert_test_create_date_IDX USING BTREE ON db1.batch_insert_test (create_date);

然后遷移的sql改為:

insert into batch_insert_test_bak   select * from batch_insert_test where create_date <now() ;

查看執行計劃發現,select走了range索引,避免全表掃描,解決了上述的風險:

id|select_type|table                |partitions|type |possible_keys                    |key                              |key_len|ref|rows|filtered|Extra                |
--+-----------+---------------------+----------+-----+---------------------------------+---------------------------------+-------+---+----+--------+---------------------+
 1|INSERT     |batch_insert_test_bak|          |ALL  |                                 |                                 |       |   |    |        |                     |
 1|SIMPLE     |batch_insert_test    |          |range|batch_insert_test_create_date_IDX|batch_insert_test_create_date_IDX|6      |   |   1|   100.0|Using index condition|

小結

由此可以得出,再使用數據insert into ...... select進行數據遷移時,無比考慮讀寫鎖的工作機制,以及遷移可能導致的鎖的粒度和范圍,只有精確的評估風險點才能保證功能上限不影響正常業務的工作。

責任編輯:趙寧寧 來源: 寫代碼的SharkChili
相關推薦

2025-02-13 07:49:18

2016-10-20 15:27:18

MySQLredo死鎖

2024-08-27 22:04:37

2023-07-03 07:27:41

進程線程Win32

2022-01-28 08:47:25

軟件系統重構

2020-10-15 06:56:51

MySQL排序

2023-12-07 07:26:04

2022-11-02 08:51:01

2022-12-26 08:13:54

子查詢MySQL

2021-04-23 10:31:18

MySQLRole數據庫

2021-07-30 10:33:57

MySQL觸發器數據

2021-03-01 18:37:15

MySQL存儲數據

2017-12-26 10:19:14

大數據問題缺陷

2022-01-11 15:44:15

JavaScript圖表庫數據

2020-09-15 12:45:48

系統LinuxUnix

2021-06-08 08:38:36

MySQL數據庫死鎖問題

2023-09-21 23:08:36

MySQL數據庫死鎖

2021-08-04 10:15:14

Go路徑語言

2022-08-25 10:37:00

CIOIT領導者

2018-04-27 09:22:21

數據存儲技巧
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: jlzzjlzz国产精品久久 | 国产一区不卡 | 成人亚洲视频 | 国产成人精品久久 | 国产精品久久久久一区二区三区 | 99热国产免费 | 国产激情在线 | 成人网av | 特级毛片爽www免费版 | 午夜在线视频 | av网址在线 | 精品一区二区三区四区五区 | 在线观看成年人视频 | 黄色网址免费在线观看 | 午夜精| 天天操欧美 | 很黄很污的网站 | 欧美国产精品一区二区 | 毛片网站免费观看 | 亚洲日本免费 | 欧美日韩三级 | 久久69精品久久久久久久电影好 | 91亚洲国产精品 | 99成人精品 | 国产91精品久久久久久久网曝门 | 日韩欧美一级片 | 狠狠亚洲 | 欧美一区成人 | 国产高潮好爽受不了了夜夜做 | 欧美精品中文 | 日韩一级免费观看 | 欧美激情视频一区二区三区免费 | 久热精品免费 | 日本不卡在线视频 | 最新国产精品精品视频 | 日韩精品一区二区三区中文在线 | 国产一区二区三区在线视频 | 欧美久久国产 | 99视频在线看| 日本成人一区二区 | 国产sm主人调教女m视频 |