精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

成人久久一区二区| 国产视频丨精品|在线观看| 一级黄色片播放| 国内精品久久久久久久久久久| 尤物网精品视频| 亚洲欧美日韩国产中文| 91日韩精品视频| 成av人片在线观看www| 国产欧美综合在线观看第十页| 91精品久久久久久久久久入口 | 国产精品极品| 91极品视觉盛宴| 国产黄色激情视频| wwwxxx在线观看| 成人动漫视频在线| 国产精品自拍偷拍视频| 日韩精品乱码久久久久久| 久久综合电影| 亚洲美女在线视频| 中文字幕乱码在线人视频| 精品三区视频| 午夜一区二区三区视频| 黄色网络在线观看| 丁香婷婷在线观看| www国产成人免费观看视频 深夜成人网| 国产精品三级网站| 人妻 日韩精品 中文字幕| 欧美不卡一区| 久久久精品在线| 人妻无码一区二区三区免费| 日韩美女国产精品| 亚洲成人网在线观看| 在线观看日本www| 777午夜精品电影免费看| 精品国产91久久久久久| 日韩在线视频在线| www红色一片_亚洲成a人片在线观看_| 久久久久久99久久久精品网站| 亚洲free性xxxx护士白浆| 中文字幕免费视频观看| 久久精品一区二区三区中文字幕 | 黑人巨大精品| 欧美日韩精品在线| 中文字幕无码精品亚洲35| 欧洲一区二区三区| 亚洲黄色性网站| 香蕉视频免费版| 日本欧美在线视频免费观看| 中文字幕免费观看一区| 日韩激情视频| av电影在线观看| 中文一区在线播放| 午夜精品一区二区三区在线观看| 欧美精品a∨在线观看不卡| 26uuu精品一区二区| 欧美国产一区二区在线| 亚洲欧洲精品视频| 久久精品人人做人人综合| 日本成人黄色| 色综合久久久久综合一本到桃花网| 国产欧美在线观看一区| 永久免费精品视频网站| 91在线中文| 亚洲一区二区偷拍精品| 国产一二三在线视频| 国产在线美女| 色呦呦国产精品| 国产精品天天av精麻传媒| 最新欧美电影| 欧美日韩大陆在线| 自拍一级黄色片| 99这里只有精品视频| 亚洲国产精品久久久久秋霞蜜臀| 亚洲一区二区三区四区av| 久久悠悠精品综合网| 亚洲欧美三级伦理| 99精品中文字幕| 好吊视频一区二区三区四区| 午夜精品久久久久久久99黑人 | 欧美日免费三级在线| 一二三级黄色片| 国产精品极品| 影音先锋欧美精品| 成人涩涩小片视频日本| 136国产福利精品导航网址| 欧美在线视频观看| 91影院在线播放| 成人午夜电影小说| 色综合电影网| 日本不卡影院| 色激情天天射综合网| www.色.com| 国产欧美日韩精品一区二区三区| www.xxxx精品| 国产午夜视频在线播放| 日本不卡视频在线观看| 国产精品播放| 成人精品一区二区三区免费| 亚洲专区一二三| 污污视频网站免费观看| 亚洲专区**| 中文字幕日韩专区| 中文字幕一区二区三区手机版 | 国产裸舞福利在线视频合集| 亚洲美女区一区| 日本精品www| 中文字幕一区图| 夜夜嗨av一区二区三区四区| 久久久久免费看| 麻豆视频一区二区| 精品综合在线| 亚洲图区一区| 在线观看中文字幕不卡| 国产精品久久久久久久无码| 亚洲成av人电影| 国产精品999999| 特黄视频在线观看| 亚洲男同1069视频| 中文字幕永久视频| 日韩精品免费一区二区三区竹菊| 九九久久国产精品| 91国内精品视频| 国产欧美一区二区三区网站| 怡红院av亚洲一区二区三区h| 精品一区二区三区中文字幕| 亚洲视频网站在线观看| 日韩精品视频免费播放| 国产成人福利片| 永久免费在线看片视频| 福利视频亚洲| 亚洲一区999| 天堂网中文字幕| av色综合久久天堂av综合| 大胆欧美熟妇xx| 精品视频在线播放一区二区三区 | 精品视频一区在线视频| 日本免费一二三区| 丁香另类激情小说| 日韩免费在线观看av| 亚洲三区欧美一区国产二区| 精品久久久999| 夜夜躁很很躁日日躁麻豆| 国产婷婷色一区二区三区在线| 日本毛片在线免费观看| 欧美一级二级三级视频| 2019中文在线观看| 日韩二区三区| 色婷婷亚洲一区二区三区| 亚洲综合色一区| 日韩国产在线观看| 亚洲国产高清国产精品| 亚洲日本免费电影| 久久久精品影院| 国产黄频在线观看| 亚洲夂夂婷婷色拍ww47| 日韩www视频| 国产精品亚洲产品| 日本一区美女| 日韩成人在线一区| 免费av一区二区| 亚洲卡一卡二卡三| 欧美丝袜美女中出在线| 亚洲av熟女国产一区二区性色| 麻豆精品在线播放| 9l视频自拍9l视频自拍| 午夜日韩影院| 91av福利视频| 福利在线播放| 91麻豆精品国产91久久久久久| 免费在线一级片| 99久久er热在这里只有精品15 | 成人一级片在线观看| 国产精品999视频| 蜜桃一区二区| 成人av在线天堂| 99在线视频影院| 国产一区二区久久精品| 91精东传媒理伦片在线观看| 一区二区不卡在线播放| 影音先锋黄色资源| 久久精品国产免费看久久精品| 日韩视频在线免费播放| 久久人人爽人人爽人人片av不| 国产精品美女无圣光视频| www在线观看播放免费视频日本| 亚洲福利视频免费观看| 亚洲免费视频二区| 亚洲第一福利视频在线| 少妇的滋味中文字幕bd| 成人免费视频网站在线观看| 欧在线一二三四区| 一区二区三区网站| 欧美精品v日韩精品v国产精品| 亚洲国产91视频| 2025国产精品视频| 18网站在线观看| 亚洲日本aⅴ片在线观看香蕉| 国产黄色小视频在线观看| 日韩欧美国产一区二区| 欧美黑吊大战白妞| 国产女人18毛片水真多成人如厕 | 国产午夜精品福利| 日韩女优在线视频| 精品一区二区三区不卡| 欧美日韩国产精品激情在线播放| 午夜精品视频一区二区三区在线看| 精品欧美国产| 精品国产18久久久久久二百| 国产成人91久久精品| 蜜桃传媒在线观看免费进入| 一本色道久久综合狠狠躁篇怎么玩| 亚洲女同志亚洲女同女播放| 欧美午夜精品久久久| 国产精品第9页| 亚洲一区二区精品3399| 黑人狂躁日本娇小| 国产区在线观看成人精品| 99久久免费看精品国产一区| 国产精品一区三区| 亚洲一区精品视频在线观看| 久久蜜桃精品| 内射国产内射夫妻免费频道| 午夜久久久久| 熟女视频一区二区三区| 日韩精品电影| 日韩三级电影免费观看| 免费成人高清在线视频theav| 国产精品视频入口| 在线一区二区三区视频| 91最新国产视频| 伊人久久大香| 成人av色在线观看| 99精品女人在线观看免费视频| 国产精品爱啪在线线免费观看| 中文字幕在线高清| 欧美在线视频免费播放| 色综合亚洲图丝熟| 欧美一级高清免费播放| 在线天堂中文资源最新版| 18性欧美xxxⅹ性满足| 极品在线视频| 奇米成人av国产一区二区三区| 中国色在线日|韩| 日本精品视频在线播放| 在线成人av观看| 日本在线观看天堂男亚洲| 中文在线а√天堂| 国产精品成人aaaaa网站| 国产 日韩 欧美一区| 国产精品久久久久久久电影| 成人h在线观看| 91精品国产自产在线老师啪 | 日韩av一二三| 另类小说色综合| 久久97超碰国产精品超碰| 五月天丁香花婷婷| 国产成人啪免费观看软件| 中国极品少妇xxxx| 91色porny蝌蚪| 国产三级短视频| 亚洲日韩欧美一区二区在线| 国产1区2区3区4区| 亚洲成人激情自拍| 成年人av网站| 欧美日韩国产一区| 精品人妻伦一区二区三区久久| 欧美成人一区二区三区片免费| 黄频在线免费观看| 亚洲天堂av高清| 麻豆av在线免费看| 久久久亚洲影院| 久久91导航| 成人黄色免费片| 成人香蕉社区| 日韩国产精品一区二区| 一区二区免费不卡在线| 尤物av无码色av无码| 日韩影院精彩在线| 成人免费播放视频| 91麻豆精品视频| 亚洲一区电影在线观看| 亚洲一区在线观看免费| 潘金莲一级淫片aaaaaa播放| 制服丝袜亚洲播放| 午夜视频www| 久久精品小视频| 亚洲精品一区| 亚洲在线视频观看| 国产一区二区三区四区大秀| 亚洲成人动漫在线| 亚洲免费影视| a级大片免费看| 久久久久成人黄色影片| 久草成人在线视频| 欧美性大战久久| 全部免费毛片在线播放一个| 最近2019中文字幕mv免费看 | 国产日韩精品在线看| 欧美乱大交xxxxx| 亚洲成人短视频| 国产精品久久久久久久天堂第1集| 国产精品一区二区99| 日韩亚洲欧美一区二区| 青青青伊人色综合久久| 白嫩情侣偷拍呻吟刺激| 自拍av一区二区三区| 亚洲AV无码成人精品区东京热| 日韩一区二区麻豆国产| 91欧美在线视频| 欧美一区二区三区……| avtt综合网| 日本在线视频www色| 免费高清在线视频一区·| 亚洲欧美日本一区| 一区二区日韩电影| 亚洲天堂自拍偷拍| 亚洲精品中文字幕有码专区| 青青草原av在线| 91在线视频九色| 欧美精品一区二区久久| a√天堂在线观看| 成人小视频免费在线观看| 久久久久99精品成人片试看| 欧美日韩免费观看一区三区| 精品电影在线| 日韩av电影手机在线观看| 米奇精品关键词| 免费特级黄色片| 国产不卡视频一区| 欧美黄片一区二区三区| 日韩一区二区免费高清| 91精选在线| 亚洲xxx视频| 欧美国产免费| 亚洲三级在线视频| 亚洲激情成人在线| av免费观看在线| 久久综合免费视频| 国产精品美女久久久久| 可以免费看的黄色网址| 激情伊人五月天久久综合| 女人十八毛片嫩草av| 在线亚洲高清视频| 成人在线免费公开观看视频| 国产精品久久久久av免费| 欧美中文字幕一区二区| 91精品无人成人www| 中文字幕中文字幕中文字幕亚洲无线 | 天堂a√在线| 日本91av在线播放| 国内黄色精品| 午夜激情av在线| 最近日韩中文字幕| xxxx国产精品| 69久久夜色精品国产69乱青草| 日本妇女一区| 亚洲一区二区蜜桃| 自拍偷在线精品自拍偷无码专区| 国产按摩一区二区三区| 国产+人+亚洲| 亚洲动漫精品| 黄色片视频在线| 一区二区在线观看视频| 四季av日韩精品一区| 国产成人精品午夜| 午夜av一区| 日韩Av无码精品| 色哟哟一区二区三区| 欧美成人xxx| 成人永久免费| 久久精品国语| √天堂中文官网8在线| 精品国产免费一区二区三区香蕉 | 国产一在线精品一区在线观看| 欧美极品jizzhd欧美仙踪林| 欧美日韩色婷婷| av福利在线播放| 国产精品xxx在线观看www| 可以看av的网站久久看| 天天色天天综合| 日韩av在线看| av在线成人| 自拍日韩亚洲一区在线| 国产精品丝袜91| 黄色小视频免费观看| 国产成人精品视频| 午夜欧美精品久久久久久久| 青青草福利视频| 日韩欧美激情一区| 桃花岛tv亚洲品质| 欧美a级免费视频| 国产清纯在线一区二区www| 亚洲第九十九页| 国产精品成人播放| 亚洲激情视频| 中文字幕在线观看2018| 亚洲精品视频网上网址在线观看| 国产一区二区三区免费观看在线 | 久久综合999| 亚洲国产成人在线观看|