精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

MySQL insert t select s 導致 s 表鎖表

數據庫 MySQL
之前遇到過 mysqldump 導致鎖表,后來才發現 insert select 也會給源表加鎖,具體加鎖類型是 S 型 next-key lock。本文分析加鎖現象與原因,并提供優化建議。

引言

之前遇到過 mysqldump 導致鎖表,后來才發現 insert select 也會給源表加鎖,具體加鎖類型是 S 型 next-key lock。本文分析加鎖現象與原因,并提供優化建議。

現象

時間:20231124 09:58

數據庫版本:MySQL 5.7.24

現象:insert select 備份表導致 update 鎖等待

查看監控

其中:

  • 鎖等待顯示每秒平均等待時間將近一小時
  • 慢 SQL 顯示 insert select 期間鎖表,阻塞業務 update 語句

測試

測試準備

mysql> show create table t3_bak \G
*************************** 1. row ***************************
       Table: t3_bak
Create Table: CREATE TABLE `t3_bak` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `a` int(10) DEFAULT NULL,
  `name` varchar(20) DEFAULT NULL,
  `b` int(11) DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `idx_name_a` (`name`,`a`)
) ENGINE=InnoDB AUTO_INCREMENT=10000000 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> select * from t3_bak limit 3;
+----+------+------+------+
| id | a    | name | b    |
+----+------+------+------+
| 11 |   11 | test |    0 |
| 12 |   12 | abc  |    0 |
| 13 |   13 | test |    0 |
+----+------+------+------+
3 rows in set (0.00 sec)

mysql> create table t3_bak_1124 like t3_bak;
Query OK, 0 rows affected (0.02 sec)

復現

操作流程,其中事務 1 備份全表,事務 2 update 其中一行數據。

時刻 2 查看鎖信息

其中:

  • information_schema.innodb_locks 表中記錄鎖等待相關信息,顯示事務 1 持有主鍵 S 型 next-key lock,事務 2 申請同一行數據的 X 型 next-key lock,因此發生鎖等待。

由于查詢全表時加鎖過多,為了查看事務 1 insert select 完整的鎖信息,下面單獨執行 insert select limit 語句。

SQL

mysql> insert into t3_bak_1124 select * from t3_bak limit 3;
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

查看鎖等待信息

其中:

  • 給 t3_bak 表中掃描的每行數據的主鍵索引加 S 型 next-key lock。

分析

執行流程

從 trace 中可以明確看到 insert select 的執行可以分兩步:

  • select
  • insert

詳見下圖。

select

insert

加鎖函數

給 sel_set_rec_lock 函數設置斷點,查看堆棧用于定位加鎖操作。

其中:

  • sel_set_rec_lock 函數入參 mode=2, type=0,表示 S 型 next-key lock;
  • row_search_mvcc 調用 sel_set_rec_lock 函數加鎖,因此給 row_search_mvcc 函數設置斷點,堆棧如下所示。

其中:

  • row_search_mvcc 函數用于行記錄加鎖判斷,相關代碼如下所示,其中判斷是否加 gap lock。
if (prebuilt->select_lock_type != LOCK_NONE) {
  /* Try to place a lock on the index record; note that delete
  marked records are a special case in a unique search. If there
  is a non-delete marked record, then it is enough to lock its
  existence with LOCK_REC_NOT_GAP. */

  /* If innodb_locks_unsafe_for_binlog option is used
  or this session is using a READ COMMITED isolation
  level we lock only the record, i.e., next-key locking is
  not used. */

  ulint lock_type;

  // 不加gap鎖的場景
  if (!set_also_gap_locks
      || srv_locks_unsafe_for_binlog
      || trx->isolation_level <= TRX_ISO_READ_COMMITTED
      || (unique_search && !rec_get_deleted_flag(rec, comp))
      || dict_index_is_spatial(index)) {

   goto no_gap_lock;
  } else {
   lock_type = LOCK_ORDINARY;
  }
}

其中:

  • 對于 RR,未開啟 innodb_locks_unsafe_for_binlog 時,根據 prebuilt->select_lock_type 字段判斷是否加 gap lock,如果為空,使用 record lock,否則使用 next-key lock;
  • prebuilt->select_lock_type 表示加鎖的類型,對應 lock_mode 枚舉類型,常見取值包括:
  1. 5(LOCK_NONE),如普通 select 快照讀;
  2. 2(LOCK_S),如 select lock in share mode 當前讀禁止寫;
  3. 3(LOCK_X),如 select for update 當前讀禁止讀寫。
  • 對于 insert select 語句,由于 prebuilt->select_lock_type = 2,因此加鎖類型為 S 型 next-key lock。

如下所示,sel_set_rec_lock 函數中加鎖時 lock_mode 同樣使用 prebuilt->select_lock_type,個人判斷行鎖類型與表鎖類型有關。

  err = sel_set_rec_lock(pcur,
             rec, index, offsets,
             prebuilt->select_lock_type,
             lock_type, thr, &mtr);

因此重點在于 prebuilt->select_lock_type 字段的賦值操作,定位到對應堆棧如下所示。

其中:

  • sql_command = 6 = SQLCOM_INSERT_SELECT,表示 insert select 語句;
  • thr_lock_type = TL_WRITE_CONCURRENT_INSERT,對應表鎖,表示允許在表的末尾進行插入操作,同時其他線程可以讀取表中的數據;
  • m_prebuilt->select_lock_type = LOCK_S,對應行鎖,表示使用行共享鎖。

ha_innobase::store_lock 函數中根據 lock_type 與 sql_command 判斷需要是否加 S 鎖,相關代碼如下所示。

// storge/innobase/handler/ha_innodb.cc

/* Check for LOCK TABLE t1,...,tn WITH SHARED LOCKS */
// 首先根據 lock_type 判斷
} else if ((lock_type == TL_READ && in_lock_tables)
     || (lock_type == TL_READ_HIGH_PRIORITY && in_lock_tables)
     || lock_type == TL_READ_WITH_SHARED_LOCKS
     || lock_type == TL_READ_NO_INSERT
     || (lock_type != TL_IGNORE
         && sql_command != SQLCOM_SELECT)) {

  /* The OR cases above are in this order:
  1) MySQL is doing LOCK TABLES ... READ LOCAL, or we
  are processing a stored procedure or function, or
  2) (we do not know when TL_READ_HIGH_PRIORITY is used), or
  3) this is a SELECT ... IN SHARE MODE, or
  4) we are doing a complex SQL statement like
  INSERT INTO ... SELECT ... and the logical logging (MySQL
  binlog) requires the use of a locking read, or
  MySQL is doing LOCK TABLES ... READ.
  5) we let InnoDB do locking reads for all SQL statements that
  are not simple SELECTs; note that select_lock_type in this
  case may get strengthened in ::external_lock() to LOCK_X.
  Note that we MUST use a locking read in all data modifying
  SQL statements, because otherwise the execution would not be
  serializable, and also the results from the update could be
  unexpected if an obsolete consistent read view would be
  used. */

  /* Use consistent read for checksum table */
 
  // 然后根據 sql_command 判斷
  if (sql_command == SQLCOM_CHECKSUM
      || ((srv_locks_unsafe_for_binlog
    || trx->isolation_level <= TRX_ISO_READ_COMMITTED)
    && trx->isolation_level != TRX_ISO_SERIALIZABLE
    && (lock_type == TL_READ
        || lock_type == TL_READ_NO_INSERT)
    && (sql_command == SQLCOM_INSERT_SELECT // insert select 語句
        || sql_command == SQLCOM_REPLACE_SELECT
        || sql_command == SQLCOM_UPDATE
        || sql_command == SQLCOM_CREATE_TABLE))) {

    /* If we either have innobase_locks_unsafe_for_binlog
    option set or this session is using READ COMMITTED
    isolation level and isolation level of the transaction
    is not set to serializable and MySQL is doing
    INSERT INTO...SELECT or REPLACE INTO...SELECT
    or UPDATE ... = (SELECT ...) or CREATE  ...
    SELECT... without FOR UPDATE or IN SHARE
    MODE in select, then we use consistent read
    for select. */

    m_prebuilt->select_lock_type = LOCK_NONE;
    m_prebuilt->stored_select_lock_type = LOCK_NONE;
  } else {
    m_prebuilt->select_lock_type = LOCK_S;
    m_prebuilt->stored_select_lock_type = LOCK_S;
  }

其中:

  • 根據 lock_type 與 sql_command 判斷,以下 SQL 可能需要加鎖:
  1. LOCK TABLES ... READ LOCAL
  2. SELECT ... IN SHARE MODE
  3. INSERT INTO ... SELECT / REPLACE INTO...SELECT / CREATE  ... SELECT
  • 滿足以下條件時不需要加鎖,否則需要加 S 型鎖:

1.事務隔離級別不是 SERIALIZABLE,并開啟 innodb_locks_unsafe_for_binlog

2.事務隔離級別是 RC

前面提到兩個枚舉類型,下面展示定義。

首先是 enum_sql_command,表示 SQL 的類型,比如 insert select = 6 = SQLCOM_INSERT_SELECT。

enum enum_sql_command {
  SQLCOM_SELECT,
  SQLCOM_CREATE_TABLE,
  SQLCOM_CREATE_INDEX,
  SQLCOM_ALTER_TABLE,
  SQLCOM_UPDATE,
  SQLCOM_INSERT,
  SQLCOM_INSERT_SELECT,
  ...
};

然后是 lock_mode,表示加鎖的模式,比如 insert select = 2 = LOCK_S。

/* Basic lock modes */
enum lock_mode {
 LOCK_IS = 0, /* intention shared */
 LOCK_IX, /* intention exclusive */
 LOCK_S,  /* shared */
 LOCK_X,  /* exclusive */
 LOCK_AUTO_INC, /* locks the auto-inc counter of a table
   in an exclusive mode */
 LOCK_NONE, /* this is used elsewhere to note consistent read */
 LOCK_NUM = LOCK_NONE, /* number of lock modes */
 LOCK_NONE_UNSET = 255
};

加鎖原因

下面分析 insert select 語句加 S 型 next-key lock 的原因。

首先參考官方文檔。  

INSERT INTO T SELECT ... FROM S WHERE ... sets an exclusive index record lock (without a gap lock) on each row inserted into T. If the transaction isolation level is READ COMMITTED, or innodb_locks_unsafe_for_binlog is enabled and the transaction isolation level is not SERIALIZABLE, InnoDB does the search on S as a consistent read (no locks). Otherwise, InnoDB sets shared next-key locks on rows from S. InnoDB has to set locks in the latter case: During roll-forward recovery using a statement-based binary log, every SQL statement must be executed in exactly the same way it was done originally.

對于 insert t select s 語句,其中 t、s 分別表示表名。

執行過程中給 t 表加 record lock,具體是隱式鎖,而給 s 表的加鎖類型與事務隔離級別及參數配置有關:

  • 如果事務隔離級別是 READ COMMITTED,不加鎖;
  • 如果事務隔離級別不是 SERIALIZABLE,并開啟 innodb_locks_unsafe_for_binlog,不加鎖;
  • 如果事務隔離級別是 REPEATABLE-READ,加鎖,類型是 S 型 next-key lock。

然后參考 MySQL 45 講。

創建測試表

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;

在 RR 事務隔離級別下,binlog_format = statement 時執行以下語句時,為什么需要對 t 的所有行和間隙加鎖呢?

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

原因是需要保證日志與數據的一致性,否則將導致主從不一致。

假設 insert select 時 t 表存在并發 insert,其中假設 session B 先執行。

其中:

  • 由于該 SQL 會給 t 表主鍵索引 (-∞, 1] 加 next-key lock,因此 session A 將阻塞直到 session B 執行完成;
  • 如果不加鎖,可能出現 session B 的 insert 先執行,后寫入 binlog 的場景。在 binlog_format = statement 時,binlog 中的語句序列如下所示。
insert into t values(-1,-1,-1);
insert into t2(c,d) select c,d from t;

因此從庫執行時,會將 id=-1 的記錄也寫入 t2 表中,從而導致主從不一致。

參考 chatgpt,insert t select * from s 給 s 表加鎖的原因如下所示,顯示與 45 講中一致。

在MySQL中,執行"insert ... select"語句時,會對選擇的表S進行鎖定以確保在整個選擇和插入過程中的數據一致性。

理論上說,"insert ... select"操作包含兩個步驟:第一步是從表S中選擇數據;第二步是將選擇的數據插入到目標表。在這兩個步驟之間,如果表S的數據被其他事務或操作更改,那么從表S選擇的數據可能就不再準確或一致,插入到目標表的數據也會出現問題。

因此,為了在整個選擇和插入過程中保持數據的一致性,MySQL在執行"insert ... select"操作時會對表S進行鎖定。這樣在鎖定期間,其他事務或操作就不能更改表S的數據,從而保證了數據的一致性。

參考文章 mysql- insert select帶來的鎖問題,由于復制的實現機制不同,針對 insert select 語句,oracle 中不需要鎖定源表。

MySQL 中可以通過開啟 innodb_locks_unsafe_for_binlog 來避免這個現象,顯然可能導致主從不一致,因此不建議使用。

針對給源表加鎖的問題,建議使用 select ... into outfile 和 load data file 的組合來代替 insert select 語句,從而避免操作期間鎖表。

需要注意的是如果主從版本不一致,也有可能導致主從不一致,原因是不同版本的加鎖規則不同。

官方文檔顯示 5.7 中 CREATE TABLE ... SELECT 語句與 INSERT ... SELECT 語句加鎖規則相同,也就是給源表加鎖。

CREATE TABLE ... SELECT ... performs the SELECT with shared next-key locks or as a consistent read, as for INSERT ... SELECT.

早期版本(個人理解比如 5.5,未驗證)中不給源表加鎖,因此假如主庫是 5.5,從庫是 5.6+,對于 binlog_format = statement,主庫不加鎖從庫加鎖,導致主從不一致。

針對該問題,有兩種方案,使用 binlog_format = row 或將主庫升級為 5.7。

MySQL 5.7 does not allow a CREATE TABLE ... SELECT statement to make any changes in tables other than the table that is created by the statement. Some older versions of MySQL permitted these statements to do so; this means that, when using replication between a MySQL 5.6 or later replica and a source running a previous version of MySQL, a CREATE TABLE ... SELECT statement causing changes in other tables on the source fails on the replica, causing replication to stop. To prevent this from happening, you should use row-based replication, rewrite the offending statement before running it on the source, or upgrade the source to MySQL 5.7. (If you choose to upgrade the source, keep in mind that such a CREATE TABLE ... SELECT statement fails following the upgrade unless it is rewritten to remove any side effects on other tables.)

執行計劃

參考 MySQL 45 講,對比以下三條語句的執行計劃。

其中:

  • SQL 1,insert select,執行計劃顯示有兩條記錄,且 ID 相同,正常情況下 ID 相同時從上往下執行,但是個人理解這里先執行第二條的 select,具體待定;
  • SQL 2,insert select limit,執行計劃顯示 rows 沒變化,原因是 limit 語句的執行計劃中 rows 不準確;
  • SQL 3,insert 循環寫入,查詢與寫入是同一張表,extra 顯示使用臨時表。

下面分別測試驗證。

首先是 insert select 全表,顯示 Innodb_rows_read 值的變化與慢查詢中的掃描行數相等,且等于表的大小。

然后是 insert select limit,顯示 Innodb_rows_read 值的變化與慢查詢中的掃描行數相等,且等于 3。

最后是 insert 循環寫入,顯示 Innodb_rows_read 值的變化與慢查詢中的掃描行數不相等,后者是前者的兩倍。

原因是 insert 循環寫入的執行流程為:

  • 創建臨時表;
  • 按照索引掃描 t 表,由于 limit 3,因此僅取前三行數據,Rows_examined = 3;
  • 最后將臨時表的數據全部插入 t 表,因此 Rows_examined 加 3,等于 6。

顯然,insert select 相同表與不同表的主要區別是后者需要使用臨時表,原因是如果讀出來的數據直接寫回原表,可能導致讀取到新插入的記錄,注意事務隔離級別為 RR 時,事務可以看到自己修改的數據。

注意這里的測試結果與 45 講中不同,45 講中 limit 失效, t 表全表掃描,limit 在從臨時表插回原表時生效。

參考文章 關于MySQL insert into ... select 的鎖情況,判斷原因是 select 語句中使用主鍵排序與非主鍵排序時的加鎖規則不同。其中:

  • 使用主鍵排序,逐行鎖定掃描的記錄,limit 失效,臨時表中寫入 limit 數據;
  • 非主鍵排序,一次性鎖定全表的記錄,limit 生效,臨時表中寫入全表數據。

如下所示,對比測試使用主鍵排序與非主鍵排序。

其中:

  • 使用主鍵排序,執行成功,Rows_examined = 6;
  • 使用非主鍵排序,執行失敗,Rows_examined = 5190999,報錯臨時表打滿。

因此,使用 insert select 時需要重點關注是否使用主鍵排序,減少掃描行數與加鎖行數。

知識點

innodb_locks_unsafe_for_binlog

row_search_mvcc 函數中判斷加鎖類型時,如果開啟 innodb_locks_unsafe_for_binlog 參數,只會對行加鎖,而不會鎖間隙。

innodb_locks_unsafe_for_binlog 參數用于控制查詢與索引掃描時是否使用 gap lock。默認 0,表示使用 gap lock。

RR 開啟 innodb_locks_unsafe_for_binlog 參數時相當于退化為 RC,但有兩點不同:

  • innodb_locks_unsafe_for_binlog 是全局參數,不支持 session 級別配置;
  • innodb_locks_unsafe_for_binlog 是靜態參數,不支持動態修改。

開啟 innodb_locks_unsafe_for_binlog 時,將導致幻讀,原因是間隙沒有加鎖,因此其他事務可以插入。

注意與 RC 相同,開啟 innodb_locks_unsafe_for_binlog 參數時,外鍵沖突檢測與唯一性檢查時依然需要使用 gap lock。

Enabling innodb_locks_unsafe_for_binlog does not disable the use of gap locking for foreign-key constraint checking or duplicate-key checking.

除了影響查詢語句的加鎖規則,開啟 innodb_locks_unsafe_for_binlog 參數時也會影響更新操作,具體規則為:

  • 對于 update / delete 語句,提前釋放不滿足 where 條件的記錄上的鎖,優點是可以減少鎖沖突,缺點是違背兩階段加鎖協議;
  • 對于 update 語句,如果發現行記錄被鎖定,使用半一致性讀(semi-consistent read),具體是先不發生鎖等待,而是先返回最新已提交的數據,然后判斷是否滿足條件,如果不滿足條件,就不需要加鎖,否則發生鎖等待。因此 semi-consistent read 是 read committed 與 consistent read 兩者的結合。

由于開啟 innodb_locks_unsafe_for_binlog 參數時可能導致主從數據不一致,因此官方不建議使用,8.0.0 中已刪除該參數,如果需要使用,建議使用 RC。

那么,針對 insert select,RC 中會存在數據不一致的問題嗎?

實際上不會,原因是 RC 不支持 binlog_format=statement。具體操作中 RC 雖然可以將 binlog_format 修改為 statement,但是寫入時報錯。

參考官方文檔,RC 中 binlog_format 僅支持 ROW 格式。

Only row-based binary logging is supported with the READ COMMITTED isolation level. If you use READ COMMITTED with binlog_format=MIXED, the server automatically uses row-based logging.

thr_lock_type

thr_lock_type 是表鎖的一種類型,從名稱判斷是多線程鎖數據結構。

盡管 MySQL 對外展示出現的只有讀鎖與寫鎖兩種類型,但實際上內部枚舉類型中定義了 14 種多線程鎖類型,詳見下表。

其中:

  • select lock in share mode 對應 TL_READ_WITH_SHARED_LOCKS;
  • insert select 對應 TL_WRITE_CONCURRENT_INSERT,表示允許在表的末尾進行插入操作,同時其他線程可以讀取表中的數據。

具體不同類型的區別還不太清楚,待后續分析。

LOCK_AUTO_INC

前面關注的都是 insert select 中給源表的加鎖規則,其實目標表的加鎖規則也需要關注,比如自增鎖 LOCK_AUTO_INC。

LOCK_AUTO_INC 也是表鎖的一種類型,用于給自增計數器加鎖,從而保證自增列(AUTO_INCREMENT)值的唯一性與連續性。

自增鎖的鎖定范圍是 SQL 級別,但是鎖的釋放時間與自增鎖模式有關,通過參數innodb_autoinc_lock_mode控制。

取值包括:

  • 0,傳統加鎖模式(traditional),用于兼容 5.1 版本引入該參數之前的策略,具體是所有 insert 類型的語句,都在 SQL 執行結束時釋放表級鎖,因此對于 binlog_format=statement,可以保證主從數據的一致性;
  • 1,連續加鎖模式(consecutive),5.7 中的默認值,普通 insert 與批量 insert 的釋放時間不同。具體為:
  • 普通 insert,由于可以提前確定插入行數,因此可以在分配自增值后立即釋放鎖,使用 mutex (a light-weight lock);
  • 批量 insert,由于無法提前確定插入行數,因此依然需要在 SQL 執行結束后釋放鎖,使用 table-level AUTO-INC lock。

“bulk inserts” use the special AUTO-INC table-level lock and hold it until the end of the statement. This applies to all INSERT ... SELECT, REPLACE ... SELECT, and LOAD DATA statements.

  • 2,交叉加鎖模式(interleaved),進一步放寬加鎖模式,所有 insert 類型的語句,都在分配后立即釋放鎖,優點是允許批量插入,缺點是存在以下兩個問題:
  • 對于 binlog_format = statement,可能導致主從數據不一致;
  • 對于批量插入語句,有可能多條語句交叉分配自增值,因此可能不連續。

LOCK_AUTO_INC 加鎖函數是 ha_innobase::innobase_lock_autoinc,實現邏輯見下圖,其中通過加鎖模式與 SQL 類型選擇加鎖實現。

從 trace 中也可以看到,ha_innobase::write_row 函數中 row_ins 函數開始前后分別調用函數 handler::update_auto_increment 與 ha_innobase::innobase_lock_autoinc。

代碼注釋顯示 ha_innobase::write_row 函數中在插入開始前獲取當前自增值,并在插入結束后更新當前自增值。

// storge/innobase/handler/ha_innodb.cc
 
  /* Step-3: Handling of Auto-Increment Columns. */
  // 內部調用 ha_innobase::innobase_lock_autoinc 函數
  update_auto_increment()
  
  /* Step-4: Prepare INSERT graph that will be executed for actual INSERT
 (This is a one time operation) */
  /* Build the template used in converting quickly between
  the two database formats */
  build_template(true);
  
 /* Step-5: Execute insert graph that will result in actual insert. */
 // 內部調用 row_ins 函數
 error = row_insert_for_mysql((byte*) record, m_prebuilt);

 /* Step-6: Handling of errors related to auto-increment. */
  auto_inc = innobase_next_autoinc(
    auto_inc,
    1, increment, offset,
    col_max_value);
 
 // 內部調用 ha_innobase::innobase_lock_autoinc 函數
  err = innobase_set_max_autoinc(
    auto_inc);

結論

insert select 語句的執行分兩步,先 select 后 insert,其中 select 階段需要給源表加 S 型 next-key lock。

原因是數據查詢階段中判斷加鎖類型時:

  • 判斷 prebuilt->select_lock_type 是否為空,如果是,不加鎖,表示快照讀,否則繼續判斷;
  • 判斷事務隔離級別與 innodb_locks_unsafe_for_binlog,如果 RC 或開啟 innodb_locks_unsafe_for_binlog,不加鎖,同樣使用快照讀,否則加 next-key lock。

其中 prebuilt->select_lock_type 對應 thr_lock_type,表示表鎖的類型,其中對于 insert select,對應 S 型鎖。

而在行鎖加鎖時 lock_mode 同樣使用 prebuilt->select_lock_type,個人判斷行鎖類型與表鎖類型有關。

關于加鎖類型,有兩個參數需要關注:

  • innodb_locks_unsafe_for_binlog 參數控制查詢源表時是否使用間隙鎖,RR 開啟該參數時相當于 RC。對于 update 語句,使用半一致性讀(semi-consistent read),semi-consistent read 是 read committed 與 consistent read 兩者的結合;
  • LOCK_AUTO_INC 參數控制目標表中自增鎖的加鎖模式,實際上是自增鎖的釋放時間,默認 1,對于批量插入的場景,由于無法提前確定插入行數,因此需要在 SQL 執行結束后釋放鎖,否則可以在分配自增值后立即釋放。

insert select 給源表加鎖的原因是保證日志與數據的一致性,否則 binlog_format = statement 時可能導致主從數據不一致。

針對 insert select 給源表加鎖的問題,有以下幾個優化建議:

  • RR 中開啟 innodb_locks_unsafe_for_binlog,但是 binlog_format = statement 時可能導致主從數據不一致,因此不建議使用;
  • 使用 RC,RC 中 binlog_format 僅支持 ROW 格式,因此不會導致主從不一致;
  • 使用 select ... into outfile 和 load data file 的組合來代替 insert select 語句。

即使使用 insert select,也需要注意以下兩點:

  • 是否使用主鍵排序,如果使用非主鍵排序,可能導致全表掃描與直接鎖表;
  • 如果主從數據庫版本不一致,依然可能導致主從不一致,原因是早期版本中不加鎖,5.6+ 中加鎖。

待辦

  • thr_lock_type
責任編輯:華軒 來源: 丹柿小院
相關推薦

2024-03-04 00:01:00

鎖表鎖行MySQL

2023-10-25 08:21:15

悲觀鎖MySQL

2023-11-06 08:35:08

表鎖行鎖間隙鎖

2024-06-14 09:27:00

2010-11-22 14:27:05

MySQL鎖表

2024-03-06 08:18:22

語句GreatSQL

2025-02-10 09:58:48

2024-11-29 07:38:12

MySQL數據庫

2023-01-27 20:59:19

行鎖表鎖查詢

2017-07-05 14:14:33

MySQL表服務變慢

2010-05-24 12:50:59

MySQL表級鎖

2020-10-20 13:50:47

MySQL數據庫

2010-10-14 16:18:21

MySQL表鎖情況

2024-11-13 15:29:08

MySQL技術索引

2021-06-26 08:09:21

MySQL不停機不鎖表

2024-10-08 09:35:23

2022-07-20 08:06:57

MySQL表鎖Innodb

2011-08-11 11:51:39

MySQLselect

2024-06-03 00:00:01

索引MySQL技術

2022-10-24 00:33:59

MySQL全局鎖行級鎖
點贊
收藏

51CTO技術棧公眾號

五月天综合激情网| 动漫美女无遮挡免费| 日本中文字幕在线看| 日韩二区在线观看| 久久影院在线观看| 亚洲婷婷在线观看| 国产99久久久久久免费看| 97欧美成人| 亚洲视频在线一区观看| 91性高湖久久久久久久久_久久99| 男女性高潮免费网站| 波多野结衣一区二区三区免费视频| 久久久一区二区三区| 国产精品极品美女在线观看免费| 国内毛片毛片毛片毛片毛片| 亚洲码欧美码一区二区三区| 日韩欧美在线视频日韩欧美在线视频| 免费国产一区二区| 国产男女裸体做爰爽爽| 国产日韩亚洲欧美精品| 精品国产一区二区三区四区在线观看| 亚洲一级Av无码毛片久久精品| 欧美男体视频| 亚洲一区二区三区四区在线免费观看| 欧美一区二区三区精美影视| 91浏览器在线观看| 日韩一区三区| 日韩av一区二区在线观看| 天天摸天天舔天天操| 9999热视频在线观看| 国产精品久久久久影视| 国产激情久久久| 91丨porny丨对白| 国产精品99| 欧美性jizz18性欧美| 久久久久亚洲av无码专区喷水| 噜噜噜在线观看播放视频| 国产美女av一区二区三区| 国产不卡一区二区在线播放| 久久免费播放视频| 羞羞答答成人影院www| 亚洲男人的天堂在线播放| 少妇欧美激情一区二区三区| 成人av集中营| 欧美午夜片在线免费观看| 超薄肉色丝袜足j调教99| 国产美女性感在线观看懂色av| 99视频在线精品国自产拍免费观看| 在线播放亚洲激情| 日韩大片一区二区| 涩涩网在线视频| |精品福利一区二区三区| 日韩三级电影| 天堂a中文在线| 成人在线视频一区| 国产91精品久久久久久久| 劲爆欧美第一页| 美国成人xxx| 日韩女优毛片在线| 在线观看视频你懂得| 天天综合在线观看| 欧美日韩国产成人在线91| 欧美精品成人网| 成人欧美大片| 色哟哟亚洲精品| 情侣黄网站免费看| 午夜裸体女人视频网站在线观看| 亚洲成人你懂的| 欧美亚洲日本一区二区三区| 99re6在线精品视频免费播放| 亚洲一区日韩精品中文字幕| bt天堂新版中文在线地址| 三级网站视频在在线播放| 亚洲影院在线观看| 性高湖久久久久久久久aaaaa| 欧美性受ⅹ╳╳╳黑人a性爽| 一区二区三区免费看视频| 九一免费在线观看| 2021天堂中文幕一二区在线观| 亚洲已满18点击进入久久| 国产高清www| 国内高清免费在线视频| 午夜精品久久久久久久99水蜜桃 | 国产精品盗摄久久久| 欧美国产一级片| 自拍日韩欧美| 欧美国产精品日韩| 91精品国产高潮对白| 亚洲综合国产| 国产精品久久久久久久久免费看 | 久久综合亚洲色hezyo国产| 国产精品v欧美精品v日本精品动漫| 欧美精品一区二区精品网| 国产视频一区二区三区在线播放 | 国产在线视频2019最新视频| 国产三级精品在线观看| 成人免费视频app| 欧美精品成人一区二区在线观看| av基地在线| www.亚洲激情.com| 欧美日韩一区二区三区在线观看免| 久久久久久久久亚洲精品| 亚洲国产精品av| 国产女教师bbwbbwbbw| 忘忧草在线日韩www影院| 在线观看网站黄不卡| 日本女人黄色片| 天美av一区二区三区久久| 中文国产成人精品| 国产一级久久久| 亚洲a在线视频| 午夜欧美大片免费观看| 欧美三级小视频| 国产高清一区| 91精品国产91久久久久| 日本黄色中文字幕| 国产91精品欧美| 日本高清不卡一区二区三| 色黄网站在线观看| 欧美性生活影院| www.555国产精品免费| 欧美三级情趣内衣| 国语自产精品视频在免费| 91好色先生tv| 国产亚洲午夜高清国产拍精品| 欧美三级午夜理伦三级老人| 天天综合网站| 日韩不卡在线观看| 欧美色图一区二区| 久久99精品久久久久婷婷| 另类欧美小说| 182在线视频观看| 欧美一激情一区二区三区| 中文字幕人妻一区二区三区在线视频| 伊人久久大香线蕉av超碰演员| 91久久久久久国产精品| www 日韩| 在线精品观看国产| 黄色短视频在线观看| 亚洲午夜黄色| av一区二区三区免费| 免费网站成人| 亚洲蜜臀av乱码久久精品蜜桃| 国产精品少妇在线视频| 日韩一级电影| 97碰在线观看| 天天摸天天干天天操| 亚洲第一福利视频在线| 国产精品熟女一区二区不卡| 91精品久久久久久久蜜月| 国产精品综合网站| 高h视频在线| 中文字幕视频一区| 孩娇小videos精品| 国产欧美日韩精品一区二区三区 | 亚洲激情图片小说视频| 国产资源第一页| 全亚洲第一av番号网站| 亚洲精品720p| 日韩片在线观看| 国内精品福利| 97伦理在线四区| а√资源新版在线天堂| 欧美日韩在线一区二区| 手机毛片在线观看| 日韩电影在线看| 欧美精品中文字幕一区二区| 一区一区三区| 亚洲摸下面视频| 美女又爽又黄免费视频| 99国产精品久久久| 欧美黑人又粗又大又爽免费| 国产精品片aa在线观看| 欧美在线激情网| 中文字幕 国产精品| 成人精品免费看| 成年人看的毛片| 老司机精品在线| 欧美中文字幕在线播放| 免费一级毛片在线观看| 精品久久久久久久久久 | 久久福利精品| 成人午夜黄色影院| 日本中文字幕伦在线观看| 欧美美女网站色| 国产一级生活片| 91在线一区二区| 欧美两根一起进3p做受视频| 日韩免费高清| eeuss一区二区三区| 成人国产电影在线观看| 亚洲欧美国产视频| 波多野结衣大片| 一区二区三区四区在线播放 | 日韩女优视频免费观看| 国产亚洲精品久久777777| 99久久国产综合色|国产精品| 香蕉精品视频在线| 国产精品久av福利在线观看| 欧美一级成年大片在线观看 | 国产精品99久久免费| 欧美国产日韩在线| 天天在线女人的天堂视频| 在线观看视频一区二区欧美日韩| 欧美成人精品欧美一级| 99久久精品国产观看| 午夜久久久精品| 亚洲欧洲日韩| 国产日韩欧美精品| 久久99精品久久久久久野外| 欧美一区二区三区免费大片| 天海翼一区二区| 国产精品护士白丝一区av| 稀缺呦国内精品呦| 日韩精品一卡二卡三卡四卡无卡| 永久免费精品视频网站| 欧美电影免费网站| 国产区精品在线观看| 国内在线免费视频| 欧美成人高清视频| 久久久久久女乱国产| 欧美一卡二卡三卡| 草久久免费视频| 99久久精品免费精品国产| 99九九精品视频| 久久精品亚洲| 成人性免费视频| 日韩欧美午夜| 91久久在线观看| 九九久久国产| 国产91精品高潮白浆喷水| 成人在线观看免费网站| 亚洲男女性事视频| 亚洲av成人无码久久精品老人 | 日本一本中文字幕| 色爱综合网欧美| 久久久久久久久一区| 欧美影院在线| 91最新国产视频| 国产精品高潮久久| 欧洲s码亚洲m码精品一区| 18av在线播放| 中文字幕国产亚洲| www 日韩| 亚洲视频专区在线| 少妇av一区二区| 日韩欧美国产麻豆| 国产福利小视频| 在线综合视频播放| 国产美女www爽爽爽| 欧美视频专区一二在线观看| 久久久国产精品黄毛片| 亚洲欧洲中文日韩久久av乱码| 国产伦理片在线观看| 99国产精品99久久久久久| 俄罗斯黄色录像| 大胆亚洲人体视频| 亚洲欧美日韩中文字幕在线观看| 久久av资源站| 婷婷免费在线观看| 久久99精品久久久久久国产越南 | 国产精品呻吟| 欧美二区在线视频| 亚洲人成毛片在线播放女女| 精品日韩欧美| 精品按摩偷拍| 91网站在线免费观看| 秋霞一区二区三区| 成人91免费视频| 一区中文字幕电影| 高清视频一区二区三区| 国产一区调教| 精品国产一区二区三| 好吊妞国产欧美日韩免费观看网站| 粉嫩精品一区二区三区在线观看 | 欧美性生交大片免网| 亚洲欧美精品一区二区三区| 欧美性猛交xxxx富婆| 亚洲欧美综合另类| 日本韩国精品在线| 国产又粗又黄视频| 欧美一级免费大片| 亚洲大尺度视频| 亚洲精品国精品久久99热一| 日韩在线视频观看免费| 精品一区二区亚洲| 成人不用播放器| 日韩在线观看免费全| 日本免费中文字幕在线| 久久久之久亚州精品露出| 国产免费拔擦拔擦8x在线播放| 欧美野外猛男的大粗鳮| 成人免费在线观看视频| 国产精品视频入口| 亚洲午夜久久| 亚洲精品第一区二区三区| 91精品国产91久久综合| ww国产内射精品后入国产| 石原莉奈在线亚洲二区| 国产大片一区二区三区| 成人av网址在线观看| 欧美性生给视频| 亚洲综合免费观看高清在线观看| 日本高清不卡码| 欧美精品乱人伦久久久久久| 色综合久久网女同蕾丝边| 色爱av美腿丝袜综合粉嫩av| 色黄网站在线观看| 国产精品福利在线| 久久久久观看| 一级特黄录像免费播放全99| 好吊妞国产欧美日韩免费观看网站| 精品日韩电影| 日产精品一区二区| 国产特级黄色大片| 久久97超碰色| 黄色a一级视频| 国产精品国产三级国产aⅴ中文| 亚洲永久精品在线观看| 在线综合+亚洲+欧美中文字幕| 四虎国产精品永远| 日韩一中文字幕| 久久野战av| 国产精品午夜av在线| av一区二区高清| 久草视频这里只有精品| 久久av中文字幕片| 国产ts丝袜人妖系列视频| 亚洲欧美日韩一区二区 | 婷婷综合视频| 亚洲精品中文字幕无码蜜桃| 福利一区福利二区| 日韩成人毛片视频| 欧美羞羞免费网站| 欧美日韩免费做爰大片| 久久69精品久久久久久久电影好 | 你懂的免费在线观看| 久久99精品久久久久久噜噜| 欧美日韩破处视频| 亚洲精品8mav| 日韩精品乱码av一区二区| 国产一线在线观看| 亚洲欧美另类久久久精品| 亚洲综合视频在线播放| 国产亚洲福利一区| 国产精品—色呦呦| 国产v亚洲v天堂无码| 亚洲色图网站| 在线观看日本一区二区| 91蜜桃网址入口| 日本一区二区欧美| 欧美tickling网站挠脚心| 午夜视频在线免费观看| 国产在线999| 日本一区二区三区视频| 日韩精品一区二区三区色欲av| 国产91丝袜在线18| 日本视频免费在线| 亚洲精品黄网在线观看| 日韩精品卡一| 国产一区自拍视频| 在线成人黄色| 中文字幕一区二区三区乱码不卡| 国产精品大尺度| 国产女人高潮时对白| 日韩在线欧美在线| 在线播放成人| 亚洲国产一区二区在线| 蜜臀精品久久久久久蜜臀| 日本少妇xxxxx| 欧洲国产伦久久久久久久| 免费a在线看| 亚洲一区二区免费| 牛牛国产精品| 91视频在线免费| 欧美性猛交xxxx富婆弯腰| 男人天堂网在线观看| 欧美有码在线视频| 日韩精品欧美| 99re6在线观看| 亚洲一二三四久久| 黄色毛片在线看| 国产日韩精品视频| 欧美在线1区| 一级特级黄色片| 在线亚洲一区二区| 日本视频在线播放| 国产日韩精品在线播放| 亚洲一区二区日韩| 国产麻豆剧传媒精品国产av| 色综合久久久久综合| 成人欧美一区| 91欧美精品成人综合在线观看| 亚洲视频在线免费| 在线观看福利片| 欧美日韩国产精品成人| 欧美性爽视频| 日韩影视精品| 国产成人av资源| 日韩不卡在线播放|