解決MySQL事務未提交導致死鎖報錯 避免死鎖的方法
解決mysql 事務未提交導致死鎖報錯:
當 sessionA 嘗試修改 B 表資料,因為 sessionB 當前為鎖定狀態,而且 sessionB 對 B 表中資料具有鎖定狀態中,則出現死鎖。sessionB 會自動終止嘗試修改 A 表資料事務, 兩個事務操作都被終止,並返回下面錯誤資訊。
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
檢視當前執行的所有事務
關注事務所線上程ID:mysql> SELECT * FROM information_schema.INNODB_TRX\G *************************** 1. row *************************** trx_id: 45900 trx_state: ROLLING BACK trx_started: 2018-04-09 10:24:38 trx_requested_lock_id: NULL trx_wait_started: NULL trx_weight: 30687 trx_mysql_thread_id: 0 trx_query: NULL trx_operation_state: NULL trx_tables_in_use: 0 trx_tables_locked: 0 trx_lock_structs: 1 trx_lock_memory_bytes: 320 trx_rows_locked: 1 trx_rows_modified: 30686 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 1 row in set (0.00 sec)
trx_mysql_thread_id: 0
執行緒0的事務沒有commit導致死鎖,執行:kill 0; //強制關閉執行緒和事務
執行結果:ERROR 1094 (HY000): Unknown thread id: 0
發現並沒有解決問題,繼續。MYSQL中如何強制終止一條語句的執行
KILL命令的語法格式如下:KILL [CONNECTION | QUERY] thread_id
步驟如下:
1、KILL允許自選的CONNECTION或QUERY修改符:KILL CONNECTION與不含修改符的KILL一樣:它會終止與給定的thread_id有關的連線。
2、KILL QUERY會終止連線當前正在執行的語句,但是會保持連線的原狀。
3、如果您擁有PROCESS許可權,則您可以檢視所有執行緒。
4、如果您擁有超級管理員許可權,您可以終止所有執行緒和語句。否則,您只能檢視和終止您自己的執行緒和語句。
5、您也可以使用mysqladmin processlist和mysqladmin kill命令來檢查和終止執行緒。
首先登入mysql,然後使用: show processlist; 檢視當前mysql中各個執行緒狀態。
以上顯示出當前正在執行的sql語句列表,找到消耗資源最大的那條語句對應的id.
然後執行kill命令,命令格式如下:
[sql] view plain copy kill id; - 示例: kill 8358
避免死鎖的方法
InnoDB給MySQL提供了具有提交,回滾和崩潰恢復能力的事務安全(ACID相容)儲存引擎。InnoDB鎖定在行級並且也在SELECT語句提供非鎖定讀。這些特色增加了多使用者部署和效能。但其行鎖的機制也帶來了產生死鎖的風險,這就需要在應用程式設計時避免死鎖的發生。以單個SQL語句組成的隱式事務來說,建議的避免死鎖的方法如下:
1.如果使用insert…select語句備份表格且資料量較大,在單獨的時間點操作,避免與其他sql語句爭奪資源,或使用select into outfile加上load data infile代替 insert…select,這樣不僅快,而且不會要求鎖定
2. 一個鎖定記錄集的事務,其操作結果集應儘量簡短,以免一次佔用太多資源,與其他事務處理的記錄衝突。
3.更新或者刪除表格資料,sql語句的where條件都是主鍵或都是索引,避免兩種情況交叉,造成死鎖。對於where子句較複雜的情況,將其單獨通過sql得到後,再在更新語句中使用。
4. sql語句的巢狀表格不要太多,能拆分就拆分,避免佔有資源同時等待資源,導致與其他事務衝突。
5. 對定點執行指令碼的情況,避免在同一時間點執行多個對同一表進行讀寫的指令碼,特別注意加鎖且操作資料量比較大的語句。
6.應用程式中增加對死鎖的判斷,如果事務意外結束,重新執行該事務,減少對功能的影響。
(六)事務的提交與回滾極死鎖檢測、處理和預防
事務的提交與回滾極死鎖檢測、處理和預防
(一)MySQL InnoDB事務模型
(二)MySQL InnoDB鎖模型
(三)MySQL InnoDB非鎖定一致性讀與鎖定讀
(四)MySQL InnoDB鎖型別及幻象讀問題
(五)MySQL InnoDB中各類語句加鎖方式
(六)事務的提交與回滾極死鎖檢測、處理和預防
事務的提交與回滾
預設情況下,MySQL開啟自動提交,每條語句執行完成且執行無誤的情況下會被自動提交。若語句發生了錯誤,提交或回滾與具體的SQL有關。另外,還有一些語句會隱式的結束一個事務,就好比在執行這些SQL前執行了COMMIT語句。
若發生了Table is full錯誤 InnoDB會回滾語句。
死鎖導致InnoDB回滾整個事務。若單是發生了 lock wait timeout則InnoDB僅會回滾事務中等待鎖併發生超時的SQL語句。若想在此種情況下回滾整個事務,可通過同時開啟 --innodb_rollback_on_timeout選項。死鎖和鎖等待在繁忙的伺服器中很常見。應用需妥善處理這些情況,儘可能在較少的記錄上持有鎖,並且鎖定的時間儘可能的短。若是通過START TRANSACTION或BEGIN明確開啟事務,則死鎖或者鎖等待超時導致的回滾並不會關閉當前事務,後續的SQL語句仍會成為當前事務的一部分,除非使用COMMIT,ROLLBACK或者其他隱式提交事務的語句。
若沒有指定IGNORE則 duplicate-key error會導致SQL語句回滾。row too long error也會導致SQL語句回滾。其他的錯誤大多由MySQL Server層而非InnoDB引擎層檢測,會回滾SQL語句。單挑SQL語句回滾不會釋放事務持有的鎖。
一些會隱式的結束事務的SQL,分為幾大類:定義或修改資料庫物件的DDL語句;隱式的使用或者修改mysql庫中的表的語句;事務控制與鎖定語句;資料匯入語句;資料庫管理語句;複製控制語句等。具體可參考官方手冊:http://dev.mysql.com/doc/refman/5.6/en/implicit-commit.html
InnoDB中死鎖自動被檢測出,並選擇代價較小的事務進行回滾以打破死鎖。事務完全回滾後其保持的鎖被全部釋放,若是僅有單條SQL由於錯誤發生了回滾則語句保持的鎖可能不會被釋放,因為InnoDB中不儲存哪條語句持有哪些鎖的資訊。若事務中的select呼叫了儲存函式,函式中的SQL執行失敗,則該語句被回滾。
死鎖是事務型應用中的典型問題,不可消除只能儘可能避免。死鎖並不危險但頻繁出現就有問題了。應用中應做好出現死鎖導致事務回滾後的後續處理邏輯。
如何預防和處理死鎖?
對於DBA,可以通過SHOW ENGINE INNODB STATUS 檢視最近的死鎖資訊,以輔助調整應用。另外,若想看到更加詳細的資訊可開啟innodb_print_all_deadlocks 配置,這樣可以在error log中看到所有的死鎖資訊而非最近的一個(除錯結束後記得關閉)。
應用中需要有死鎖發生後導致事務回滾的處理邏輯。
儘量保持事務短小精悍以避免衝突。
儘早提交事務,不要保持一個有長期未關閉事務的互動式mysql session。
若非必要則不使用鎖定讀,若要選擇使用鎖定讀則可選擇較低的事務隔離級別如 READ COMMITTED。
若在同一事務中修改多個表或者修改同一表中的不同行,則每次儘量按一致的順序進行操作。
為表新增合理的索引,並用explain確認SQL能否使用到合適的索引。
特殊情況下可以使用表鎖。
通過設定輔助表來序列化事務,表中只包含一行,事務在訪問其他表前必須更新該表中的行,這樣可保證事務的序列執行,避免死鎖。
不好的事務使用習慣
· 在迴圈中提交事務
· 使用自動提交
· 自動回滾
· 長事務
mysql中 insert …select …帶來的死鎖問題
轉載 2015年06月17日 14:51:26mysql中 insert …select …帶來的問題
當使用insert...select...進行記錄的插入時,如果select的表是innodb型別的,不論insert的表是什麼型別的表,都會對select的表的紀錄進行鎖定。
對於那些從oracle遷移過來的應用,需要特別的注意,因為oracle並不存在類似的問題,所以在oracle的應用中insert...select...操作非常的常見。例如:有時候會對比較多的紀錄進行統計分析,然後將統計的中間結果插入到另外一個表,這樣的操作因為進行的非常少,所以可能並沒有設定相應的索引。如果遷移到mysql資料庫後不進行相應的調整,那麼在進行這個操作期間,對需要select的表實際上是進行的全表掃描導致的所有記錄的鎖定,將會對應用的其他操作造成非常嚴重的影響。
究其主要原因,是因為mysql在實現複製的機制時和oracle是不同的,如果不進行select表的鎖定,則可能造成從資料庫在恢復期間插入結果集的不同,造成主從資料的不一致。如果不採用主從複製,關閉binlog並不能避免對select紀錄的鎖定,某些文件中提到可以通過設定innodb_locks_unsafe_for_binlog來避免這個現象,當這個引數設定為true的時候,將不會對select的結果集加鎖,但是這樣的設定將可能帶來非常嚴重的隱患。如果使用這個binlog進行從資料庫的恢復,或者進行主資料庫的災難恢復,都將可能和主資料庫的執行效果不同。
因此,我們並不推薦通過設定這個引數來避免insert...select...導致的鎖,如果需要進行可能會掃描大量資料的insert...select操作,我們推薦使用select...into outfile和load data infile的組合來實現,這樣是不會對紀錄進行鎖定的,但是效率會下降
參考:
mysql事務執行時間過長引起死鎖http://blog.csdn.net/lin_credible/article/details/8541195
http://www.51testing.com/html/16/390216-838016.html
http://www.jb51.net/article/32651.htm