MySQL有三種鎖的級別:mysql
1)表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的機率最高,併發度最低。 2)行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。 3)頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常。 4)InnoDB行級鎖的實現:InnoDB的行級鎖是經過在索引上加鎖來實現的,因此只有經過明確的索引來查找數據時纔會使用行級鎖!換句話說就是:若是在執行sql時沒有用到索引,則mysql就沒法使用行鎖了。
MySQL中鎖的類型:sql
共享鎖(S鎖:Shared lock) 1)共享鎖又叫讀鎖,MySQL會在select ... lock in share mode語句的查詢結果集上添加共享鎖; 2)共享鎖能夠被多個事務同時持有。 3)某個事務在涉及的數據行加上共享鎖後,全部的事務只能對這些數據進行讀操做,目的是爲了防止在讀取數據的過程當中,其它事務對數據進行修改。 4)不能在共享鎖的基礎上再加其它類型的鎖。 獨佔鎖(X鎖:Exclusive lock) 1)獨佔鎖又叫排它鎖,InnoDB引擎默認會給insert、delete、update、select ... for update 等語句加上獨佔鎖。 2)獨佔鎖只能被一個事務獲取,其它事務只有等到持有獨佔鎖的事務將鎖釋放後,才能去獲取獨佔鎖。 3)某個事務在涉及的數據行加上獨佔鎖後,這個事務就能夠對鎖定這些數據進行修改操做了。 4)不能在獨佔鎖的基礎上再加其它類型的鎖。 共享鎖和獨佔鎖能夠加在表、頁、數據行(索引)上。 排它鎖的選擇: 若where條件中明確指定了主鍵,且該行數據存在,則只鎖定該行,故排它鎖爲行鎖(row lock)。 若where條件中明確指定了主鍵,可是該行數據不存在,則不會加鎖。 若where條件中明確指定了索引,且該行數據存在,則只鎖定該行,故排它鎖爲行鎖(row lock)。 若where條件中明確指定了索引,可是該行數據不存在,則不會加鎖。 若where條件中未明確指定主鍵或索引,則會鎖定全表,故排它鎖爲表鎖(table lock)。換句話說就是:在執行sql時沒有用到索引!!! 注:未明確指定 即 未指定(主鍵/索引) 或 指定的是(主鍵/索引)的範圍 eg: # 只鎖定message_id爲1的行 set autocommit=0; begin; select * from t_message where message_id=1 for update; # message_id爲主鍵 commit; # 鎖定全表 set autocommit=0; begin; select * from t_message where message_id>1 for update; # message_id爲主鍵 commit; # 鎖定全表 set autocommit=0; begin; select * from t_message where type='good' for update; # good非索引列 commit; 其它線程由於等待(排它鎖)超時而報錯: update t_message set title='asdf' where message_id=1; [Err] 1205 - Lock wait timeout exceeded; try restarting transaction
死鎖產生的四個必要條件:數據庫
互斥條件 資源只能由一個線程使用 請求保持 保持已鎖定的資源不釋放 不可剝奪 已持有的資源不會被其它線程剝奪 環路條件 循環等待
避免死鎖:併發
說明:致使死鎖的常見場景:兩個Connection中加鎖的順序不一致。 1)不一樣的方法併發存取多個表時,儘可能以相同的順序訪問這些表。 2)在一個事務中,儘可能一次性鎖定所需的全部資源,即一次性鎖定多行。
排查死鎖:spa
1)show processlist 運行時間最大的(即:Time值最大)的線程最有多是致使死鎖的線程。 2)MySQL中information_schema數據庫中關於Innodb事務和鎖的三張表: select * from information_schema.INNODB_TRX select * from information_schema.INNODB_LOCKS select * from information_schema.INNODB_LOCK_WAITS 3)show engine innodb status 4)kill MySQL線程Id
Tips: 在select後面添加sleep(n)後,該sql最少會執行n秒;若查詢結果爲m行,則該sql最少會執行m*n秒。 eg:select sleep(5),t.name from t_user t where t.age=1線程
MySQL中information_schema數據庫中關於Innodb事務和鎖的三張表:rest
1)查看在InnoDB引擎中正在執行的事務,包括:正在執行的事務 和 因申請加鎖而等待的事務。 SQL:select * from information_schema.INNODB_TRX trx_id 事務的ID trx_state 事務的狀態: RUNNING, LOCK WAIT, ROLLING BACK or COMMITTING. trx_started 事務的開始時間 trx_requested_lock_id 事務等待的鎖的ID(若是事務狀態不是LOCK WAIT,這個字段是NULL),詳細的鎖的信息能夠連查INNODB_LOCKS表 trx_wait_started 事務等待開始的時間(若是事務狀態不是LOCK WAIT,這個字段是NULL) trx_weight 事務的權重,反映了一個事務修改和鎖住的行數。當發生死鎖回滾的時候,優先選擇該值最小的進行回滾。 trx_mysql_thread_id MySQL中的線程ID,show processlist結果中的Id列。 trx_query 事務中正在運行的sql語句 trx_operation_state 事務當操做的類型 trx_tables_in_use 查詢用到的表的數量 trx_tables_locked 查詢加行鎖的表的數量 2)查看在InnoDB引擎中存在的鎖,包括:事務正在申請的鎖 和 事務已經持有的鎖。 SQL:select * from information_schema.INNODB_LOCKS lock_id 鎖ID lock_trx_id 事務ID,能夠連INNODB_TRX表查事務詳情 lock_mode 鎖的模式: S, X, IS, IX, S_GAP, X_GAP, IS_GAP, IX_GAP, or AUTO_INC lock_type 鎖的類型:行級鎖 或 表級鎖 lock_table 加鎖的表 lock_index 若是是lock_type='RECORD' 行級鎖,爲鎖住的索引,若是是表鎖爲null lock_space 若是是lock_type='RECORD' 行級鎖,爲鎖住對象的Tablespace ID,若是是表鎖爲null lock_page 若是是lock_type='RECORD' 行級鎖,爲鎖住頁號,若是是表鎖爲null lock_rec 若是是lock_type='RECORD' 行級鎖,爲鎖住行號,若是是表鎖爲null lock_data 事務鎖住的主鍵值,如果表鎖,則該值爲null 3)查看在InnoDB引擎中鎖等待的相關信息: SQL:select * from information_schema.INNODB_LOCK_WAITS requesting_trx_id 申請鎖的事務ID requesting_lock_id 申請的鎖的ID blocking_trx_id 阻塞的事務ID blocking_lock_id 阻塞的鎖的ID
show open tables where in_use > 0 Database 數據庫 Table 表名 In_use 正在訪問該表的線程數 Name_locked 表名是否被鎖(Drop或Rename這張表時,表名會被鎖住)code