innodb 鎖機制

innodb存儲引擎不須要鎖升級,由於一個鎖和多個鎖的開銷是相同的。mysql

鎖是數據庫系統區別於文件系統的一個關鍵特性。鎖機制用於管理對共享資源的併發訪問。Innodb引擎中使用鎖的地方有多個:在行級別上對錶數據上鎖;操做緩衝池中的LRU列表,刪除,添加,移動LRU列表中的元素。sql

鎖信息的查看命令:數據庫

  • show engine innodb status;
  • information_schema架構下的表:innodb_trx,innodb_locks,innodb_lock_waits

鎖的類型

innodb實現了兩種標準的行級鎖:架構

  • 共享鎖(S LOCK),容許事務讀一行數據
  • 排他鎖(X lock),容許事務刪除或更新一行數據

鎖兼容:若是一個事務T1已經得到了r行的共享鎖,那麼另外的事務T2能夠當即得到行r的共享鎖,由於讀取並無改變行r的數據。併發

鎖不兼容:若是T3想得到r行的x鎖,則必須等T1,T2釋放行r上的S鎖spa

 

 

S鎖和X鎖的兼容性
  X S
X 不兼容 不兼容
S 不兼容 兼容

innodb支持兩個鐘意向鎖(在innodb中即爲表鎖):orm

  • 意向共享鎖(IS lock),事務想要得到一張表中某幾行的共享鎖
  • 意向排他鎖(IX lock),事務想要得到一張表中某幾行的排他鎖

查看innodb隔離級別:blog

mysql> SELECT @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ       |
+-----------------------+
1 row in set (0.00 sec)

  

實驗過程:事務

建表test:資源

mysql> CREATE TABLE `test` (
    ->   `id` bigint(20) NOT NULL,
    ->   `name` varchar(20) NOT NULL DEFAULT "name",
    ->   PRIMARY KEY (id),
    ->   KEY `index_name` (`name`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.99 sec)

填充數據:

mysql> insert into test (id) values(1),(2),(3),(9);
Query OK, 4 rows affected (0.08 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> select * from test;
+----+------+
| id | name |
+----+------+
|  1 | name |
|  2 | name |
|  3 | name |
|  9 | name |
+----+------+
4 rows in set (0.00 sec)

 

實驗數據:

說明:事務一,事務二都有數據,順序是先執行事務一,再執行事務二。

 
事務一 事務二 innodb_trx innodb_locks innodb_lock_waits
mysql> begin; select * from test where id=1 for update;
Query OK, 0 rows affected (0.00 sec)

+----+------+
| id | name |
+----+------+
|  1 | name |
+----+------+
1 row in set (0.00 sec)

  

 
mysql>  select * from information_schema.INNODB_TRX\G
*************************** 1. row ***************************
                    trx_id: 1074396261
                 trx_state: RUNNING
               trx_started: 2017-04-26 15:18:25
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 2
       trx_mysql_thread_id: 8950735
                 trx_query: NULL
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 0
          trx_lock_structs: 2
     trx_lock_memory_bytes: 376
           trx_rows_locked: 1
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 10000
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
1 row in set (0.00 sec)

  

mysql> select * from information_schema.INNODB_LOCKS\G
Empty set (0.00 sec)

  

mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS\G
Empty set (0.00 sec)

  

 
mysql> begin;select * from test where id=1 lock in share mode;
Query OK, 0 rows affected (0.00 sec)

  

mysql>  select * from information_schema.INNODB_TRX\G
*************************** 1. row ***************************
                    trx_id: 1074397998
                 trx_state: LOCK WAIT
               trx_started: 2017-04-26 15:21:02
     trx_requested_lock_id: 1074397998:5594:3:2
          trx_wait_started: 2017-04-26 15:21:02
                trx_weight: 2
       trx_mysql_thread_id: 8959888
                 trx_query: select * from test where id=1 lock in share mode
       trx_operation_state: starting index read
         trx_tables_in_use: 1
         trx_tables_locked: 1
          trx_lock_structs: 2
     trx_lock_memory_bytes: 376
           trx_rows_locked: 1
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 10000
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
*************************** 2. row ***************************
                    trx_id: 1074396261
                 trx_state: RUNNING
               trx_started: 2017-04-26 15:18:25
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 2
       trx_mysql_thread_id: 8950735
                 trx_query: NULL
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 0
          trx_lock_structs: 2
     trx_lock_memory_bytes: 376
           trx_rows_locked: 1
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 10000
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
2 rows in set (0.00 sec)

  

mysql> select * from information_schema.INNODB_LOCKS\G
*************************** 1. row ***************************
    lock_id: 1074397998:5594:3:2
lock_trx_id: 1074397998
  lock_mode: S
  lock_type: RECORD
 lock_table: `ztest`.`test`
 lock_index: PRIMARY
 lock_space: 5594
  lock_page: 3
   lock_rec: 2
  lock_data: 1
*************************** 2. row ***************************
    lock_id: 1074396261:5594:3:2
lock_trx_id: 1074396261
  lock_mode: X
  lock_type: RECORD
 lock_table: `ztest`.`test`
 lock_index: PRIMARY
 lock_space: 5594
  lock_page: 3
   lock_rec: 2
  lock_data: 1
2 rows in set (0.00 sec)

  

mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS\G
*************************** 1. row ***************************
requesting_trx_id: 1074397998
requested_lock_id: 1074397998:5594:3:2
  blocking_trx_id: 1074396261
 blocking_lock_id: 1074396261:5594:3:2
1 row in set (0.00 sec)

  

mysql> update test set name="name1" where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from test;
+----+-------+
| id | name  |
+----+-------+
|  2 | name  |
|  3 | name  |
|  9 | name  |
|  1 | name1 |
+----+-------+
4 rows in set (0.00 sec)

  

mysql> select * from test;
+----+------+
| id | name |
+----+------+
|  1 | name |
|  2 | name |
|  3 | name |
|  9 | name |
+----+------+
4 rows in set (0.00 sec)

  該查詢下,id爲1的行,並無修改name,由於事務一的修改並無提交。

     
mysql> commit
-> ;
Query OK, 0 rows affected (0.08 sec)

mysql> select * from test;
+----+-------+
| id | name |
+----+-------+
| 2 | name |
| 3 | name |
| 9 | name |
| 1 | name1 |
+----+-------+
4 rows in set (0.00 sec)

  

 
mysql> select * from test;
+----+------+
| id | name |
+----+------+
|  1 | name |
|  2 | name |
|  3 | name |
|  9 | name |
+----+------+
4 rows in set (0.00 sec)

  事務一,已經提交,該查詢下,id爲1的行,name沒有發生變化,代表在RR隔離級別下的,可重複讀:同一個事務的,同一個select讀出的數據徹底一致。

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

mysql> select * from test;
+----+-------+
| id | name  |
+----+-------+
|  2 | name  |
|  3 | name  |
|  9 | name  |
|  1 | name1 |
+----+-------+
4 rows in set (0.00 sec)

  兩個事務都結束了。

     
 實驗二    
 事務一  事務二 innodb_trx  innodb_locks  innodb_lock_waits
 
mysql> begin;select * from test where id>0 and id<5 for update;
Query OK, 0 rows affected (0.00 sec)

+----+-------+
| id | name  |
+----+-------+
|  2 | name  |
|  3 | name  |
|  1 | name1 |
+----+-------+
3 rows in set (0.00 sec)

  

mysql> begin;update test set name="nihao" where id=1;
Query OK, 0 rows affected (0.00 sec)

select * from test;

  事務二的兩個操做都被阻塞

 
   
mysql> select * from information_schema.INNODB_LOCKS\G
*************************** 1. row ***************************
    lock_id: 1074413840:5594:3:6
lock_trx_id: 1074413840
  lock_mode: X
  lock_type: RECORD
 lock_table: `ztest`.`test`
 lock_index: PRIMARY
 lock_space: 5594
  lock_page: 3
   lock_rec: 6
  lock_data: 1
*************************** 2. row ***************************
    lock_id: 1074413574:5594:3:6
lock_trx_id: 1074413574
  lock_mode: X
  lock_type: RECORD
 lock_table: `ztest`.`test`
 lock_index: PRIMARY
 lock_space: 5594
  lock_page: 3
   lock_rec: 6
  lock_data: 1
2 rows in set (0.00 sec)

  

mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS\G
*************************** 1. row ***************************
requesting_trx_id: 1074413840
requested_lock_id: 1074413840:5594:3:6
  blocking_trx_id: 1074413574
 blocking_lock_id: 1074413574:5594:3:6
1 row in set (0.00 sec)

  

 
 
mysql> update test set name="fjsld" where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from test;
+----+-------+
| id | name  |
+----+-------+
|  1 | fjsld |
|  2 | name  |
|  3 | name  |
|  9 | name  |
+----+-------+
4 rows in set (0.00 sec)

  

       
 
mysql> commit;
Query OK, 0 rows affected (0.02 sec)

  

mysql> update test set name="nihao" where id=1;


Query OK, 1 row affected (1 min 45.44 sec)
Rows matched: 1  Changed: 1  Warnings: 0

  

事務一提交,事務二更新成功 

事務一優先獲取了X鎖,此後,事務二想要獲取X鎖,須要等候

     

 

常見概念:

  1. select ...for update
    1. 鎖定查詢出來的行,其餘事務要修改該行,須要等待,該行會上X鎖
  2. select ...lock in share mode;
    1. 鎖定查詢出來的行,其餘事務須要修改改行,須要等待,該行會上S鎖
  3. 四種事務隔離級別:
    1. RR:可重複讀
    2. RC:提交讀
    3. RU:非提交讀
    4. SEARILIZED:事務串行執行
  4. innodb支持的兩種級別舉例分析
    1. RR:repeatable read:可重複讀
    2. RC:read commit:提交讀
  5. 髒讀,幻讀,不可重複讀
    1. 髒讀
      1. 事務一,能讀到事務二尚未提交的更改信息
      2. 隔離級別最低的RU中會出現此種狀況
    2. 幻讀
      1. 不可重複讀的特殊狀況:同一條sql語句,是範圍查找類型的,兩次獲得的結果不同
      2. 事務二在某個範圍內插入一條數據,並提交;事務一,再次範圍查找獲得不同的結果
      3. innodb的RR下,此種類型不會出現
    3. 不可重複讀
      1. 在同一個事務中,一樣一條sql語句,執行兩次,獲得的結果不同
      2. 適用場景:update
      3. innodb的RR下,此種類型不會出現
  6. 設置innodb_lock_wait_timeout
    1. 相關操做
      1. mysql> show global variables like "%timeout%";
        +-----------------------------+----------+
        | Variable_name               | Value    |
        +-----------------------------+----------+
        | connect_timeout             | 10       |
        | delayed_insert_timeout      | 300      |
        | innodb_flush_log_at_timeout | 1        |
        | innodb_lock_wait_timeout    | 120      |
        | innodb_rollback_on_timeout  | OFF      |
        | interactive_timeout         | 28800    |
        | lock_wait_timeout           | 31536000 |
        | net_read_timeout            | 30       |
        | net_write_timeout           | 60       |
        | slave_net_timeout           | 3600     |
        | wait_timeout                | 28800    |
        +-----------------------------+----------+
        11 rows in set (0.00 sec)
        
        mysql> set innodb_lock_wait_timeout=3600;
        Query OK, 0 rows affected (0.00 sec)
        
相關文章
相關標籤/搜索