實例詳解mysql中innodb_autoinc_lock_mode

下面小編就為大家帶來一篇淺談innodb_autoinc_lock_mode的表現形式和選值參考方法。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

前提條件,percona 5.6版本,事務隔離級別為RR

mysql>?show?create?table?test_autoinc_lockG  ***************************?1.?row?***************************  ????Table:?test_autoinc_lock  Create?Table:?CREATE?TABLE?`test_autoinc_lock`?(  ?`id`?int(11)?NOT?NULL?AUTO_INCREMENT,  ?`a`?int(11)?DEFAULT?NULL,  ?PRIMARY?KEY?(`id`),  ?KEY?`idx_a`?(`a`)  )?ENGINE=InnoDB?AUTO_INCREMENT=14?DEFAULT?CHARSET=utf8    1?row?in?set?(0.00?sec)  mysql>?select?*?from?test_autoinc_lock;  +----+------+  |?id?|?a??|  +----+------+  |?1?|??1?|  |?12?|??2?|  |?2?|??3?|  |?3?|??5?|  |?4?|??7?|  |?5?|??7?|  |?6?|??9?|  |?7?|??10?|  +----+------+  8?rows?in?set?(0.00?sec)

條件1 innodb_autoinc_lock_mode設置為0

session1  ?begin;delete?from?test_autoinc_lock?where?a>7;//這時未提交  session2  mysql>?insert?into?test_autoinc_lock(a)?values(100);//gap鎖的存在,這時處于鎖等待  session3  mysql>?insert?into?test_autoinc_lock(a)?values(2);//這時同樣處于等待狀態,理論上這個不是gap鎖的鎖定范圍,那么它是在等什么呢  session4  mysql>?select?*?from?information_schema.innodb_trxG  ***************************?1.?row?***************************  ??????????trx_id:?2317  ?????????trx_state:?LOCK?WAIT  ????????trx_started:?2016-10-31?19:28:05  ???trx_requested_lock_id:?2317:20  ?????trx_wait_started:?2016-10-31?19:28:05  ????????trx_weight:?1  ????trx_mysql_thread_id:?9  ?????????trx_query:?insert?into?test_autoinc_lock(a)?values(2)  ????trx_operation_state:?setting?auto-inc?lock  ?????trx_tables_in_use:?1  ?????trx_tables_locked:?1  ?????trx_lock_structs:?1  ???trx_lock_memory_bytes:?360  ??????trx_rows_locked:?0  ?????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

這時查看session3是等待自增鎖,一直處于setting auto-inc lock狀態

session2

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

這時session3鎖等待超時退出

session3

這時再看session3可以發現insert完成。

mysql>?select?*?from?test_autoinc_lock;  +----+------+  |?id?|?a??|  +----+------+  |?1?|??1?|  |?12?|??2?|  |?13?|??2?|  |?2?|??3?|  |?3?|??5?|  |?4?|??7?|  |?5?|??7?|  |?6?|??9?|  |?7?|??10?|  +----+------+  9?rows?in?set?(0.00?sec)//注意看這時的最大自增值是13,也就是之前自增最大值上+1,也就是說session2后來釋放了預計生成的自增id,將13留給了session3,自增id值的申請完全是串行順序的。

結論:innodb_autoinc_lock_mode為0時的,也就是官方說的traditional

級別,該自增鎖是表鎖級別,且必須等待當前SQL執行完成后或者回滾掉才會釋放,這樣在高并發的情況下可想而知自增鎖競爭是比較大的。

條件2 innodb_autoinc_lock_mode設置為1

session1  mysql>?begin;  Query?OK,?0?rows?affected?(0.00?sec)      mysql>?delete?from?test_autoinc_lock?where?a>7;  Query?OK,?2?rows?affected?(0.00?sec)  mysql>?select?*?from?test_autoinc_lock;  +----+------+  |?id?|?a??|  +----+------+  |?1?|??1?|  |?12?|??2?|  |?13?|??2?|  |?2?|??3?|  |?3?|??5?|  |?4?|??7?|  |?5?|??7?|  |?6?|??9?|  |?7?|??10?|  +----+------+  9?rows?in?set?(0.00?sec)//注意看這時的最大自增值是13      session2  mysql>?insert?into?test_autoinc_lock(a)?values(100);//同樣gap鎖的存在,這時處于鎖等待  session3  mysql>?insert?into?test_autoinc_lock(a)?values(5);  Query?OK,?1?row?affected?(0.00?sec)      mysql>?select?*?from?test_autoinc_lock;  +----+------+  |?id?|?a??|  +----+------+  |?1?|??1?|  |?12?|??2?|  |?13?|??2?|  |?2?|??3?|  |?3?|??5?|  |?15?|??5?|  |?4?|??7?|  |?5?|??7?|  |?6?|??9?|  |?7?|??10?|  +----+------+  10?rows?in?set?(0.00?sec)//session3直接完成了,并且注意觀察插入的自增id值是15,也就是跳過了預計分配給session2的14,可以看到自增id值立馬就分配給了session3,而不必等session2執行完成

結論:innodb_autoinc_lock_mode為1時的,也就是官方說的consecutive

級別,這時如果是單一的insert SQL,可以立即獲得該鎖,并立即釋放,而不必等待當前SQL執行完成(除非在其他事務中已經有session獲取了自增鎖)。另外當SQL是一些批量insert sql時,比如insert into …select …,load data,replace ..select..時,這時還是表級鎖,可以理解成退化為必須等待當前SQL執行完才釋放。

可以認為,該值為1時是相對比較輕量的鎖,也不會對復制產生影響,唯一的缺陷是產生的自增值不一定是完全連續的(不過個人認為這個往往不是很重要,也沒必要根據自增id值來統計行數之類)

條件3 innodb_autoinc_lock_mode設置為2

先說結論:當innodb_autoinc_lock_mode設置為2時,所有insert種類的SQL都可以立馬獲得鎖并釋放,這時的效率最高。但是會引入一個新的問題:當binlog_format為statement時,這時的復制沒法保證退出,因為批量的insert,比如insert ..select..語句在這個情況下,也可以立馬獲取到一大批的自增id值,不必鎖整個表,slave在回放這個sql時必然會產生錯亂。我們做個測試驗證復制不是安全的。

master?session1  mysql>?show?variables?like?'%binlog_for%';  +---------------+-----------+  |?Variable_name?|?Value???|  +---------------+-----------+  |?binlog_format?|?STATEMENT?|  +---------------+-----------+  1?row?in?set?(0.00?sec)  mysql>?insert?into?test_autoinc_lock(a)?select?*?from?test_auto;  Query?OK,?8388608?rows?affected,?1?warning?(29.85?sec)  Records:?8388608?Duplicates:?0?Warnings:?1      master?session2(注意session2在session1執行完成之前執行)  mysql>?insert?into?test_autoinc_lock(a)?values(2);  Query?OK,?1?row?affected?(0.01?sec)  mysql>?select?*?from?test_autoinc_lock?where?a=2;  +---------+------+  |?id???|?a??|  +---------+------+  |?1376236?|??2?|  +---------+------+  1?row?in?set?(0.00?sec)      slave?session1(這時可看到1376236主鍵沖突)  mysql>?show?slave?statusG  ***************************?1.?row?***************************  ????????Slave_IO_State:?Waiting?for?master?to?send?event  ?????????Master_Host:?10.9.73.139  ?????????Master_User:?ucloudbackup  ?????????Master_Port:?3306  ????????Connect_Retry:?60  ???????Master_Log_File:?mysql-bin.000006  ?????Read_Master_Log_Pos:?75823243  ????????Relay_Log_File:?mysql-relay.000002  ????????Relay_Log_Pos:?541  ????Relay_Master_Log_File:?mysql-bin.000006  ???????Slave_IO_Running:?Yes  ??????Slave_SQL_Running:?No  ???????Replicate_Do_DB:?  ?????Replicate_Ignore_DB:?  ??????Replicate_Do_Table:?  ????Replicate_Ignore_Table:?  ???Replicate_Wild_Do_Table:?  ?Replicate_Wild_Ignore_Table:?  ??????????Last_Errno:?1062  ??????????Last_Error:?Error?'Duplicate?entry?'1376236'?for?key?'PRIMARY''?on?query.?Default?database:?'test'.?Query:?'insert?into?test_autoinc_lock(a)?select?*?from?test_auto'  ?????????Skip_Counter:?0  ?????Exec_Master_Log_Pos:?75822971

我們這時解析下主庫的binlog不難發現問題原因,第一條批量insert還沒執行完時,第二條簡單insert執行時獲得了自增id值為1376236的鎖,這時在主庫寫入是沒有問題的,但是反應到從庫時,因為是基于statement的復制,必然出現主鍵沖突。

SET?INSERT_ID=1376236/*!*/;  #161031?21:44:31?server?id?168380811?end_log_pos?75822940?CRC32?0x65797f1c???Query??thread_id=20??exec_time=0???error_code=0  use?`test`/*!*/;  SET?timestamp=1477921471/*!*/;  insert?into?test_autoinc_lock(a)?values(2)  /*!*/;  #?at?75822940  #161031?21:44:31?server?id?168380811?end_log_pos?75822971?CRC32?0xbb91449d???Xid?=?274  COMMIT/*!*/;  #?at?75822971  #161031?21:44:26?server?id?168380811?end_log_pos?75823050?CRC32?0xa297b57b???Query??thread_id=57??exec_time=30??error_code=0  SET?TIMESTAMP=1477921466/*!*/;  BEGIN  /*!*/;  #?at?75823050  #?at?75823082  #161031?21:44:26?server?id?168380811?end_log_pos?75823082?CRC32?0xa5aa31a1???Intvar  SET?INSERT_ID=1/*!*/;  #161031?21:44:26?server?id?168380811?end_log_pos?75823212?CRC32?0x470282ba???Query??thread_id=57??exec_time=30??error_code=0  SET?TIMESTAMP=1477921466/*!*/;  insert?into?test_autoinc_lock(a)?select?*?from?test_auto

總結:

1 innodb row復制時,可將innodb_autoinc_lock_mode設置為2,這時可在所有insert情況下表獲得最大并發度

2 innodb statement復制時,可將innodb_autoinc_lock_mode設置為1,保證復制安全的同時,獲得簡單insert語句的最大并發度

3 myisam引擎情況下,無論什么樣自增id鎖都是表級鎖,設置innodb_autoinc_lock_mode參數無效(測試略)

4 實際上提問者說到的在innodb引擎下自增id值作為主鍵的情況下,相比uuid或者自定義的主鍵,是可以提到插入速度的,因為innodb是主鍵聚集退出,實際的主鍵值必須按照主鍵順序存取,那么自增id本身就是升序的,那么在退出時,底層就不必再做額外的排序操作,也減少了索引頁分裂的次數,從而大大增加insert速度(除非其他方案也能保證主鍵完全自增)

【相關推薦】

1.?退出

2.?退出

3.?退出

4.?退出

5.?退出

? 版權聲明
THE END
喜歡就支持一下吧
點贊15 分享