Aaron Li

MySQL: Lock

Table of Content

MySQL 有哪些鎖

MySQL 的鎖依照顆粒度大小能分成全局鎖、表級鎖、行級鎖,而在各個種類又能夠分成以下

  • 全局鎖

  • 表級鎖: Table Lock、 MDL ( Meta Data Lock )、 Intention Lock ( 意向鎖 ) 、 Auto-Inc Lock

  • 行級鎖: Record Lock、Gap Lock、 Next-Key Lock

全局鎖

加上全局鎖後,整個 DB 都會變成唯讀狀態,當執行以下的動作都會被阻塞:

  • 修改資料:insert, update, delete
  • 修改表的結構: alter table, drop table
mysql> FLUSH TABLES WITH READ LOCK;                              
Query OK, 0 rows affected (0.00 sec)                             
                                                                 
mysql> UPDATE messages SET content = 'changed';                  
ERROR 1223 (HY000): Can't execute the query because you have a co
nflicting read lock                                              

mysql> UNLOCK TABLES;                                            
Query OK, 0 rows affected (0.00 sec)

表級鎖

盡量避免使用 Table Lock 會影響性能

  • Table Lock Table Lock 需顯示的加上,其中又有分

    • SHARED (S) Lock (共享鎖):本線程和其他線程只能夠使用讀操作, 會阻塞本線程跟其他線程的寫操作
    mysql> LOCK TABLE users READ;                                    
    Query OK, 0 rows affected (0.00 sec)                             
                                                                    
    mysql> SELECT id FROM users WHERE id = 1;                        
    +----+                                                           
    | id |                                                           
    +----+                                                           
    |  1 |                                                           
    +----+                                                           
    1 row in set (0.00 sec)                                          
                                                                    
    mysql> UPDATE users SET name = 'Aaron' WHERE id = 1;             
    ERROR 1099 (HY000): Table 'users' was locked with a READ lock and
    can't be updated                                                
    mysql> UNLOCK TABLES;                                            
    Query OK, 0 rows affected (0.00 sec)
    • EXCLUSIVE (X) Lock (獨佔鎖): 本線程可使用讀寫操作,會阻塞其他線程的讀寫操作
    mysql> LOCK TABLE users WRITE;           │mysql> SELECT id, name FROM users WHERE id = 1; <-- 阻塞
    Query OK, 0 rows affected (0.01 sec)     │
    mysql> UNLOCK TABLE; <-- 移除寫鎖
  • Meta Data Locks (MDL)

    • 目的: 確保多併發狀態下可以安全、一致地執行讀取或寫入表等操作,而不會出現資料損壞或其他不一致的風險。

    • 種類:

      • SHARED (S) Lock (共享鎖): 在執行 DML 時自動會加上
      • EXCLUSIVE (X) Lock (獨佔鎖): 在執行 DDL 會加上

      DML: 操作資料庫中資料的 SQL 命令, EX: INSERT, UPDATE, DELETE, SELECT, CALL

      DDL: 定義資料結構和修改資料的SQL指令, EX: CREATE, ALTER, DROP, TRUNCATE, RENAME

      DMLDDL
      用於操作資料庫內的資料定義資料庫對象,如表格、索引
      變更會影響資料庫中儲存的資料變更會影響資料庫的結構
      事務性,可以在必要時回滾不是事務性,無法回滾
    • 為何能夠確保多併發狀態下可以安全、一致地執行讀取或寫入表等操作?

             GET SHARED LOCK
      p1     UPDATE ...                   FINISH UPDATE OPERATION
                |                                 |
      time ---------------------------------------------------->
                    |                               |
      p2          ALTER TABLE                       SHARED LOCK release, 
                Request EXCLUSIVE LOCK              get EXCLUSIVE LOCK, and do alter table
                but SHARED LOCK has been taken
                so blocked.
    • 如何加上 MDL, 又在什麼時候釋放呢? 兩種鎖哪一種會優先取得呢?

      • 不需顯示的加上 MDL, 在進行操作時會自動請求鎖

      • 釋放的時機為:

        1. 一般使用會在執行完指令後自動釋放
        2. 在 transaction 內的話會在 transaction 結束後釋放 (重要)
      • X 鎖會優先於 S 鎖, 所以如果有一個 transaction 拿到(或正在等待拿到) X 鎖,又遲遲不能結束 transaction。會導致後面的讀寫操作全部卡住。

            GET S LOCK             release S LOCK
      t1      |                       |(commit)
      time -------------------------------------------------------------->
      t2          |             |     |               (commit)||
          ( request X LOCK )    |  GET X LOCK   release X LOCK |
      t3                        |                              |
                      SELECT .. IN SHARED MODE BLOCKED       GET S LOCK, execute select
  • Intention Locks Intention Locks 表示 Transaction 稍後需要對表中的行使用哪種類型的鎖(S 或 X Lock), 其中能分成以下兩種:

    • Intention S Lock (IS): Transaction intends to set a shared lock on individual rows in a table
    • Intention X Lock (IX): Transaction intends to set an exclusive lock on individual rows in a table

    在 Transaction 拿到 row level S(X) Lock 前需要先拿到 table IS(IX) LOCK。

    表級鎖的相容性:

    XIXSIS
    X衝突衝突衝突衝突
    IX衝突相容衝突相容
    S衝突衝突相容相容
    IS衝突相容相容相容
  • AUTO-INC LOCK (ref) 用來 generate auto-increment values, 在 innodb_autoinc_lock_mode = 0 時就會使用 AUTO-INC LOCK。 會在執行 “INSERT-like” statements 時自動在 Table 加上 AUTO-INC LOCK 以確保值是自增的,然後在 statement 結束後直接釋放(不是在 transaction)但這樣在大量 Insert 時會有效能問題,因為會 block 其他 transaction 的 insert operation.

行級鎖

  • Record Lock

A record lock is a lock on an index record. For example, SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; prevents any other transaction from inserting, updating, or deleting rows where the value of t.c1 is 10.

mysql> BEGIN;                                                    │mysql> BEGIN;
Query OK, 0 rows affected (0.01 sec)                             │Query OK, 0 rows affected (0.00 sec)
mysql> SELECT id FROM users WHERE id = 1 FOR UPDATE;             │mysql> UPDATE users SET name = 'aaron' WHERE id = 1; <-- blocked
+----+                                                           │
| id |                                                           │
+----+                                                           │
|  1 |                                                           │
+----+                                                           │
1 row in set (0.00 sec)                                          │
  • Gap Lock 防止同一事務的兩次當前讀,出現幻讀的情況

SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;防止其他交易將 的值插入15列 中 t.c1,無論該列中是否已經存在任何此類值,因為範圍中所有現有值之間的間隙已被鎖定

mysql> BEGIN;                                                    │mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)                             │Query OK, 0 rows affected (0.00 sec)
mysql> SELECT id FROM users WHERE id BETWEEN 1 and 10 FOR UPDATE;│mysql> UPDATE users SET name = 'aaron' WHERE id = 12;
                                                                 │Query OK, 1 row affected (0.01 sec)
+----+                                                           │Rows matched: 1  Changed: 1  Warnings: 0
| id |                                                           │
+----+                                                           │mysql> UPDATE users SET name = 'aaron' WHERE id = 9; <-- blocked
|  1 |                                                           │
|  2 |                                                           │
|  3 |                                                           │
|  4 |                                                           │
|  5 |                                                           │
|  6 |                                                           │
|  7 |                                                           │
|  8 |                                                           │
|  9 |                                                           │
| 10 |                                                           │
+----+                                                           │
10 rows in set (0.00 sec)                                        │
  • Next Key Lock 對於行的查詢會加上 Record Lock + Gap Lock, 主要目的是解決幻讀的問題。
create  table t(id int , key idx_id(id))engine = innodb;
mysql> BEGIN;                                                    │mysql> begin;
Query OK, 0 rows affected (0.00 sec)                             │Query OK, 0 rows affected (0.00 sec)
mysql> select * from t                                           │mysql> insert  into t values ( 12 );
    -> ;                                                         │^C^C -- query aborted
+------+                                                         │ERROR 1317 (70100): Query execution was interrupted
| id   |                                                         │mysql> insert  into t values ( 6 );
+------+                                                         │^C^C -- query aborted
|    1 |                                                         │ERROR 1317 (70100): Query execution was interrupted
|    3 |                                                         │
|    5 |                                                         │
|    8 |                                                         │
|   11 |                                                         │mysql> insert  into t values ( 4 );
+------+                                                         │^C^C -- query aborted
5 rows in set (0.00 sec)                                         │ERROR 1317 (70100): Query execution was interrupted
                                                                 │mysql> insert  into t values ( 2 );
mysql> select  *  from t where a between 5 and 11 for  update ;  │Query OK, 1 row affected (0.00 sec)
+------+                                                         │
| a    |                                                         │mysql>
+------+                                                         │
|    5 |                                                         │
|    8 |                                                         │
|   11 |                                                         │
+------+                                                         │
3 rows in set (0.00 sec)                                         │
mysql>

因為目前的 key 有 1, 3, 5, 8, 11, 間隙為 (-inf, 1], (1, 3], (3, 5], (5, 8], (8, 11], (11, inf] between 5 and 11 鎖定得範圍為 (3, 5], (5, 8], (8, 11], (11, inf],所以 insert 4, 6, 12 都被 blocked