MySQL的insert into select 引發鎖表

2021-02-19 秦子帥

上周五HaC我要上線,有一個腳本需要執行,執行前需要備份一個表。

運維大佬:「這個表的備份為什麼要這麼久,,??」

1秒過去了……2秒過去了……

期間運營反饋系統出現大量訂單超時情況。

大佬找到我,問:「你怎麼備份的?」

我:「insert into select * from 呀!」

大佬:「??你是不是不想混了?」

又是被大佬嫌棄的一天,為了不捲鋪蓋走人,我決定去學習一下表備份的常見方法。

MySQL一般我們在生產上備份數據通常會用到 這兩種方法:

INSERT INTO SELECT

CREATE TABLE AS SELECT

註:本文僅針對MySQL innodb引擎,事務是可重複讀RR,資料庫版本為5.5

1.INSERT INTO SELECT

insert into Table2(field1,field2,...) select value1,value2,... from Table1

注意

(1)要求目標表Table2必須存在,並且欄位field,field2…也必須存在

(2)注意Table2的主鍵約束,如果Table2有主鍵而且不為空,則 field1, field2…中必須包括主鍵

在執行語句的時候,MySQL是逐行加鎖的(掃描一個鎖一個),直至鎖住所有符合條件的數據,執行完畢才釋放鎖。所以當業務在進行的時候,切忌使用這種方法。

在RR隔離級別下,還會加行鎖和間隙鎖

舉個慄子吧:

CREATE TABLE `t` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `c` int(11) DEFAULT NULL,
  `d` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `c` (`c`)
) ENGINE=InnoDB;

insert into t values(null, 1,1);
insert into t values(null, 2,2);
insert into t values(null, 3,3);
insert into t values(null, 4,4);

create table t2 like t;

執行

begin;
insert into t2(c,d) select c,d from t;

先不commit;這個語句對表 t 主鍵索引加了 (-∞,1] 這個 next-key lock

新開一個Navicat窗口,模擬新事務進入,此時執行下面這句sql就需要等待

insert into t values(-1,-1,-1);

鎖住了

真就鎖表了~無法寫進去了,我終於知道為什麼訂單超時了。

背鍋背鍋。

如果實在要使用 INSERT INTO SELECT 這種方法,可以使用下面的方法進行優化:

加條件,強制走索引,不要全表掃描,例如

INSERT INTO Table2 SELECT
    * 
FROM
    Table1 FORCE INDEX (create_time)
WHERE
    update_time <= '2020-03-08 00:00:00';

加上limit 100,100 這種,限制數量

2. CREATE TABLE AS SELECT

create table as select  會創建一個不存在的表,也可以用來複製一個表。

1. create table t3 as select * from t where 1=2;
-- 創建一個表結構與t一模一樣的表,只複製結構不複製數據;

2.create table t3 as select * from t ;
-- 創建一個表結構與t一模一樣的表,複製結構同時也複製數據;(索引不會創建)

3.create table t3(`id`,`a`)  as select `id`,`c` from t;
-- 創建一個表結構與t一模一樣的表,複製結構同時也複製數據,但是指定新表的列名;

後面兩種格式,如果後面跟上合適的查詢條件,可以只複製符合條件的數據到新的表中。比如:

create  table table1  as  select * from table2  where columns1>=1;

針對大表多欄位的表複製,考慮是否每一個欄位都是必需的,如果不是必需的,可以自定義選擇欄位嗎,這樣複製的時間會大大提升。

CREATE table table1 as SELECT id FROM table2; -- 只複製id這一列

注意此建表過程全程鎖表。語句執行完畢,才釋放元數據鎖

MDL全稱為metadata lock,即元數據鎖。MDL鎖主要作用是維護表元數據的數據一致性,在表上有活動事務(顯式或隱式)的時候,不可以對元數據進行寫入操作。因此從MySQL5.5版本開始引入了MDL鎖,來保護表的元數據信息,用於解決或者保證DDL操作與DML操作之間的一致性。

注意:

新表不會自動創建創建和原表相同的索引。(即複製表的索引會消失)

3 .區別

首先,最大的區別是二者屬於不同類型的語句,INSERT INTO SELECT 是DML語句(數據操作語言,SQL中處理數據等操作統稱為數據操縱語言),完成後需要提交才能生效,CREATE TABLE AS SELECT 是DDL語句(數據定義語言,用於定義和管理 SQL 資料庫中的所有對象的語言 ),執行完直接生效,不提供回滾,效率比較高。

其次,功能不同,INSERT INTO SELECT只是插入數據,必須先建表;CREATE TABLE AS SELECT 則建表和插入數據一塊完成。

當有大量數據的時候不推薦使用Insert into as,因為該語句的插入的效率很慢。

4.總結

以上對複製表來說,都不是很好的選擇,分享幾種平時常用的方法:

導出成excel,然後拼sql 成 insert into values(),(),()的形式。

定時任務,任務的邏輯是查詢100條記錄,然後多個線程分到幾個任務執行,比如是個線程,每個線程10條記錄,插入後,在查詢新的100條記錄處理。

mysqldumb方法,例如

mysqldump -h<span class="katex-html" aria-hidden="true" style="font-size: inherit;color: inherit;line-height: inherit;margin: 1px;overflow-wrap: inherit !important;word-break: inherit !important;"><span class="strut" style="height:0.69444em;vertical-align:0em;" style="font-size: inherit;color: inherit;line-height: inherit;margin: 1px;overflow-wrap: inherit !important;word-break: inherit !important;"><span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;margin: 1px;overflow-wrap: inherit !important;word-break: inherit !important;">h<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;margin: 1px;overflow-wrap: inherit !important;word-break: inherit !important;">o<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;margin: 1px;overflow-wrap: inherit !important;word-break: inherit !important;">s<span class="mord mathit" style="font-size: inherit;color: inherit;line-height: inherit;margin: 1px;overflow-wrap: inherit !important;word-break: inherit !important;">t<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;margin: 1px;overflow-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="margin-right:0.05764em;" style="font-size: inherit;color: inherit;line-height: inherit;margin: 1px;overflow-wrap: inherit !important;word-break: inherit !important;">E<span class="mord mathit" style="margin-right:0.13889em;" style="font-size: inherit;color: inherit;line-height: inherit;margin: 1px;overflow-wrap: inherit !important;word-break: inherit !important;">Pport -u$user --add-locks=0 --no-create-info --single-transaction  --set-gtid-purged=OFF db1 t --where="a>900" --result-file=/client_tmp/t.sql
</span class="mord mathit" style="margin-right:0.13889em;"></span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit" style="margin-right:0.05764em;"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="mord mathit"></span class="strut" style="height:0.69444em;vertical-align:0em;"></span class="katex-html" aria-hidden="true">

導出 CSV 文件

select * from db1.t where a>900 into outfile '/server_tmp/t.csv';

第3、4兩種方法適合整個表導出。

5. 業務少的情況(深夜什麼的)下,可以使用 create table as select  。

知識又增加了。

相關焦點

  • MySQL資料庫Insert語句7種寫法
    mysql> select * from t_test_2;Empty set (0.00 sec)mysql> insert into t_test_2 values(20,'name20','tname20',0);Query OK, 1 row affected (0.53 sec)mysql> select
  • MySQL INSERT的4種形態
    墨墨導讀:MySQL中常用的四種插入數據的語句:insert ,insert select,replace into
  • 因用了Insert into select語句,美女同事被開除了!
    # 前言Insert into select請慎用。這天xxx接到一個需求,需要將表A的數據遷移到表B中去做一個備份。本想通過程序先查詢查出來然後批量插入。但xxx覺得這樣有點慢,需要耗費大量的網絡I/O,決定採取別的方法進行實現。
  • 使用Insert into select語句時需要注意的事項
    這天xxx接到一個需求,需要將表A的數據遷移到表B中去做一個備份。本想通過程序先查詢查出來然後批量插入。但xxx覺得這樣有點慢,需要耗費大量的網絡I/O,決定採取別的方法進行實現。通過在Baidu的海洋裡遨遊,他發現了可以使用insert into select實現,這樣就可以避免使用網絡I/O,直接使用SQL依靠資料庫I/O完成,這樣簡直不要太棒了。然後他就被開除了。事故發生的經過。
  • insert唯一鍵衝突的加鎖情況分析
    今天分享的內容是MySQL裡面insert語句在發生衝突的時候加鎖情況,廢話就不多說了,直接從例子開始吧。    首先創建表t,其中id為主鍵,c為唯一索引,然後插入5條數據,mysql> show create table t\G*************************** 1. row ***************************       Table: tCreate Table: CREATE TABLE `t`
  • select for update引發的死鎖分析,太驚險了
    xjjdog提示:Gap鎖(間隙鎖)實質上是對索引前後的間隙上鎖,不對索引本身上鎖。間隙鎖的目的是為了防止幻讀。在MySQL中select稱為快照讀,不需要鎖,而insert、update、delete與select for update則稱為當前讀,需要給數據加鎖,幻讀中的「讀」即是針對當前讀。
  • MySQL InnoDB 引擎中的 7 種鎖類型,你都知道嗎?
    前言大概幾個月之前項目中用到事務,需要保證數據的強一致性,期間也用到了mysql的鎖,但當時對mysql的鎖機制只是管中窺豹,所以本文打算總結一下mysql的鎖機制。本文主要論述關於mysql鎖機制,mysql版本為5.7,引擎為innodb,由於實際中關於innodb鎖相關的知識及加鎖方式很多,所以沒有那麼多精力羅列所有場景下的加鎖過程並加以分析,僅根據現在了解的知識,結合官方文檔,說說自己的理解,如果發現有不對的地方,歡迎指正。概述總的來說,InnoDB共有七種類型的鎖:mysql鎖詳解1.
  • 因用了Insert into select語句,同事被開除了!
    Insert into select 請慎用,同事因為使用了 Insert into select 語句引發了重大生產事故,最後被開除。某天 xxx 接到一個需求,需要將表 A 的數據遷移到表 B 中去做一個備份。
  • INSERT...ON DUPLICATE KEY UPDATE歷險記
    二、舉個🌰先看下當前環境mysql> select version();+--+| @@version  |+--+| 5.6.16-log |+--+1 row in set (0.00 sec)mysql> select @@tx_isolation;+--+| @@tx_isolation
  • 真刺激,用了Insert into select語句,差點被勸退!
    2、☞ 《Java面試手冊》.PDF    點擊查看作者:不一樣的科技宅https://juejin.im/post/5e670f0151882549274a65efInsert into select 請慎用,同事因為使用了 Insert into select 語句引發了重大生產事故,最後被開除。
  • 為什麼要避免使用「CREATE TABLE AS SELECT」語句
    ([有關元數據鎖定的更多信息](https://dev.mysql.com/doc/refman/5.7/en/metadata-locking.html))。請注意:MySQL元數據鎖與InnoDB死鎖、行級鎖、表級鎖是不同的。
  • MySQL的SQL語句 - 數據操作語句(7)- INSERT SELECT 語句
    SELECT 語句,可以從 SELECT 語句的結果中快速地將許多行插入到表中,該語句可以從一個或多個表中進行選擇。● INSERT 語句的目標表可以出現在查詢的 SELECT 部分的 FROM 子句中,也可以是 TABLE 指定的表。但是,不能在子查詢中插入一個表並從同一表中進行選擇。當從同一個表中進行選擇和插入時,MySQL 會創建一個內部臨時表來保存 SELECT 中的行,然後將這些行插入到目標表中。但是,當t是臨時表時,不能使用 INSERT INTO t ... SELECT ...
  • 因用了Insert into select語句,碼農被開除了!
    通過在某度的海洋裡遨遊,他發現了可以使用 insert into select 實現,這樣就可以避免使用網絡 I/O,直接使用 SQL 依靠資料庫 I/O 完成,這樣簡直不要太棒,然後他就被開除了。由於數據資料庫中 order_today 數據量過大,當時好像有 700W 了,並且每天在以 30W 的速度增加。
  • 史上最全的select加鎖分析(Mysql)
    意向共享鎖(IS鎖):一個事務在獲取(任何一行/或者全表)S鎖之前,一定會先在所在的表上加IS鎖。意向排他鎖(IX鎖):一個事務在獲取(任何一行/或者全表)X鎖之前,一定會先在所在的表上加IX鎖。意向鎖存在的目的?OK,這裡說一下意向鎖存在的目的。
  • 史上最全的select加鎖分析(MySQL)
    意向共享鎖(IS鎖):一個事務在獲取(任何一行/或者全表)S鎖之前,一定會先在所在的表上加IS鎖。意向排他鎖(IX鎖):一個事務在獲取(任何一行/或者全表)X鎖之前,一定會先在所在的表上加IX鎖。意向鎖存在的目的?OK,這裡說一下意向鎖存在的目的。
  • 追加查詢 select into from和insert into select的區別
  • 資料庫:MySQL 中 「select ... for update」 排他鎖分析
    二、行鎖與表鎖1、只根據主鍵進行查詢,並且查詢到數據,主鍵欄位產生行鎖。begin;select * from goods where id = 1 for update;commit;2、只根據主鍵進行查詢,沒有查詢到數據,不產生鎖。
  • InnoDB加鎖實驗
    鎖資料庫通過鎖來保證讀寫操作的一致性.InnoDB中使用的最多的行級鎖(record lock), 所有的行級鎖都是作用在索引上面的. InnoDB通過用行鎖代替表鎖大大提高了對並發的支持. 對某張表加意向鎖表示有對該表加同類型(X,S)的行鎖的操作. 通俗點講, 意向鎖就是行鎖同表鎖進行交涉的代言人, 如果意向鎖與某個表鎖不兼容, 其實表示表上面加的某個行鎖同表鎖不兼容. 關於鎖之間的兼容性問題, 有個經典的表格, 表格裡面的X表示的表鎖, 未涉及行鎖.
  • mysql資料庫基礎命令(一)
    @') where user='test';創建用戶並授權mysql>grant select,insert,update on student.* to test@localhost identified by 'A1b2c3#!
  • 幾個常見而嚴重的 MySQL 問題分析 | 運維進階
    一旦出現Metadata Lock Wait等待現象,後續所有對該表的訪問都會阻塞在該等待上,導致連接堆積,業務受影響。MySQL的設計:在設置的autocommit=0;read_commited的時候,無論session的第一條語句是select還是dml,都開始一個事務,然後直到commit,所持有的MDL鎖也一直維持到commit結束。