第三彈:MySQL事務(wù)和鎖
事務(wù)特點(diǎn):ACID
原子性、一致性、隔離性、持久性
一個(gè)事務(wù)必須被視為一個(gè)不可分割的最小工作單元,整個(gè)事務(wù)中的所有操作要么全部提交成功,要么全部失敗回滾。
數(shù)據(jù)庫(kù)總是從一個(gè)一致性狀態(tài)轉(zhuǎn)換到另一個(gè)一致狀態(tài)。
一個(gè)事務(wù)所做的修改在最終提交以前,對(duì)其他事務(wù)是不可見的。
一旦事務(wù)提交,則其所做的修改就會(huì)永久保存到數(shù)據(jù)庫(kù)中。
未提交讀:事務(wù)A可以讀取到事務(wù)B已修改但未提交的數(shù)據(jù)
讀已提交RC:等其它事務(wù)把變更提交到db,才能讀取到
可重復(fù)讀RR:同一個(gè)事務(wù)中,多次讀取某一行記錄,始終是一樣的值,不管在此期間,其它事務(wù)有沒有修改過該數(shù)據(jù)(不論是否提交)。該級(jí)別解決了RC不可重復(fù)讀的問題,但是存在幻讀問題,商品訂單容易形成超賣現(xiàn)象。
串行化:一個(gè)事務(wù)在修改其它數(shù)據(jù)時(shí),如果有其它事務(wù)也想改,必須等前面的事務(wù)提交或回滾后,才能繼續(xù)。最嚴(yán)格的級(jí)別,但是性能最低
幻讀和不可重復(fù)讀的區(qū)別
不可重復(fù)讀的重點(diǎn)是修改:在同一事務(wù)中,同樣的條件,第一次讀的數(shù)據(jù)和第二次讀的數(shù)據(jù)不一樣。(因?yàn)橹虚g有其他事務(wù)提交了修改)
幻讀的重點(diǎn)在于新增或者刪除:在同一事務(wù)中,同樣的條件,第一次和第二次讀出來的記錄數(shù)不一樣。(因?yàn)橹虚g有其他事務(wù)提交了插入/刪除)
| 隔離級(jí)別 | 存在的問題 |
|---|---|
| 讀未提交 | 臟讀、不可重復(fù)讀、幻讀 |
| 讀已提交 | 不可重復(fù)讀、幻讀 |
| 可重復(fù)讀 | 幻讀 |
| 串行化 | 性能問題 |
解決方法:
1、加鎖:在讀取數(shù)據(jù)前,對(duì)其加鎖,阻止其他事務(wù)對(duì)數(shù)據(jù)進(jìn)行修改。
2、多版本并發(fā)控制,也稱為多版本數(shù)據(jù)庫(kù):不用加任何鎖,通過一定機(jī)制生成一個(gè)數(shù)據(jù)請(qǐng)求時(shí)間點(diǎn)的一致性數(shù)據(jù)快照并用這個(gè)快照來提供一定級(jí)別(語(yǔ)句級(jí)或事務(wù)級(jí))的一致性讀取。
事務(wù)日志
包括:重做日志redo和回滾日志undo
redo log(重做日志) 實(shí)現(xiàn)持久化和原子性
undo log(回滾日志) 實(shí)現(xiàn)一致性
數(shù)據(jù)庫(kù)日志種類
- 錯(cuò)誤日志:記錄出錯(cuò)信息,也記錄一些警告信息或者正確的信息。
- 查詢?nèi)罩荆河涗浰袑?duì)數(shù)據(jù)庫(kù)請(qǐng)求的信息,不論這些請(qǐng)求是否得到了正確的執(zhí)行。
- 慢查詢?nèi)罩荆涸O(shè)置一個(gè)閾值,將運(yùn)行時(shí)間超過該值的所有SQL語(yǔ)句都記錄到慢查詢的日志文件中。
- 二進(jìn)制日志:記錄對(duì)數(shù)據(jù)庫(kù)執(zhí)行更改的所有操作。
- 中繼日志:中繼日志也是二進(jìn)制日志,用來給slave 庫(kù)恢復(fù)
- 事務(wù)日志:重做日志redo和回滾日志undo
鎖的分類
從對(duì)數(shù)據(jù)操作的類型分類:
- 讀鎖(共享鎖):針對(duì)同一份數(shù)據(jù),多個(gè)讀操作可以同時(shí)進(jìn)行,不會(huì)互相影響
- 寫鎖(排他鎖):當(dāng)前寫操作沒有完成前,它會(huì)阻斷其他寫鎖和讀鎖
從對(duì)數(shù)據(jù)操作的粒度分類:
表級(jí)鎖:開銷小,加鎖快;不會(huì)出現(xiàn)死鎖;鎖定粒度大,發(fā)生鎖沖突的概率最高,并發(fā)度最低(MyISAM 和 MEMORY 存儲(chǔ)引擎采用的是表級(jí)鎖);
行級(jí)鎖:開銷大,加鎖慢;會(huì)出現(xiàn)死鎖;鎖定粒度最小,發(fā)生鎖沖突的概率最低,并發(fā)度也最高(InnoDB 存儲(chǔ)引擎既支持行級(jí)鎖也支持表級(jí)鎖,但默認(rèn)情況下是采用行級(jí)鎖);
頁(yè)面鎖:開銷和加鎖時(shí)間界于表鎖和行鎖之間;會(huì)出現(xiàn)死鎖;鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般。
MyISAM 表鎖
InnoDB 行鎖 索引失效會(huì)導(dǎo)致行鎖變表鎖
加鎖機(jī)制
樂觀鎖與悲觀鎖是兩種并發(fā)控制的思想,可用于解決丟失更新問題

死鎖
死鎖產(chǎn)生:
死鎖是指兩個(gè)或多個(gè)事務(wù)在同一資源上相互占用,并請(qǐng)求鎖定對(duì)方占用的資源,從而導(dǎo)致惡性循環(huán).
當(dāng)事務(wù)試圖以不同的順序鎖定資源時(shí),就可能產(chǎn)生死鎖。多個(gè)事務(wù)同時(shí)鎖定同一個(gè)資源時(shí)也可能會(huì)產(chǎn)生死鎖.
鎖的行為和順序和存儲(chǔ)引擎相關(guān)。以同樣的順序執(zhí)行語(yǔ)句,有些存儲(chǔ)引擎會(huì)產(chǎn)生死鎖有些不會(huì)——死鎖有雙重原因:真正的數(shù)據(jù)沖突;存儲(chǔ)引擎的實(shí)現(xiàn)方式。
檢測(cè)死鎖:數(shù)據(jù)庫(kù)系統(tǒng)實(shí)現(xiàn)了各種死鎖檢測(cè)和死鎖超時(shí)的機(jī)制。InnoDB存儲(chǔ)引擎能檢測(cè)到死鎖的循環(huán)依賴并立即返回一個(gè)錯(cuò)誤。
死鎖恢復(fù):死鎖發(fā)生以后,只有部分或完全回滾其中一個(gè)事務(wù),才能打破死鎖,InnoDB目前處理死鎖的方法是,將持有最少行級(jí)排他鎖的事務(wù)進(jìn)行回滾。所以事務(wù)型應(yīng)用程序在設(shè)計(jì)時(shí)必須考慮如何處理死鎖,多數(shù)情況下只需要重新執(zhí)行因死鎖回滾的事務(wù)即可。
外部鎖的死鎖檢測(cè):發(fā)生死鎖后,InnoDB 一般都能自動(dòng)檢測(cè)到,并使一個(gè)事務(wù)釋放鎖并回退,另一個(gè)事務(wù)獲得鎖,繼續(xù)完成事務(wù)。但在涉及外部鎖,或涉及表鎖的情況下,InnoDB 并不能完全自動(dòng)檢測(cè)到死鎖, 這需要通過設(shè)置鎖等待超時(shí)參數(shù) innodb_lock_wait_timeout 來解決
死鎖影響性能:死鎖會(huì)影響性能而不是會(huì)產(chǎn)生嚴(yán)重錯(cuò)誤,因?yàn)镮nnoDB會(huì)自動(dòng)檢測(cè)死鎖狀況并回滾其中一個(gè)受影響的事務(wù)。在高并發(fā)系統(tǒng)上,當(dāng)許多線程等待同一個(gè)鎖時(shí),死鎖檢測(cè)可能導(dǎo)致速度變慢。有時(shí)當(dāng)發(fā)生死鎖時(shí),禁用死鎖檢測(cè)(使用innodb_deadlock_detect配置選項(xiàng))可能會(huì)更有效,這時(shí)可以依賴innodb_lock_wait_timeout設(shè)置進(jìn)行事務(wù)回滾。
MyISAM避免死鎖:
在自動(dòng)加鎖的情況下,MyISAM 總是一次獲得 SQL 語(yǔ)句所需要的全部鎖,所以 MyISAM 表不會(huì)出現(xiàn)死鎖。
InnoDB避免死鎖:
為了在單個(gè)InnoDB表上執(zhí)行多個(gè)并發(fā)寫入操作時(shí)避免死鎖,可以在事務(wù)開始時(shí)通過為預(yù)期要修改的每個(gè)元祖(行)使用SELECT ... FOR UPDATE語(yǔ)句來獲取必要的鎖,即使這些行的更改語(yǔ)句是在之后才執(zhí)行的。
在事務(wù)中,如果要更新記錄,應(yīng)該直接申請(qǐng)足夠級(jí)別的鎖,即排他鎖,而不應(yīng)先申請(qǐng)共享鎖、更新時(shí)再申請(qǐng)排他鎖,因?yàn)檫@時(shí)候當(dāng)用戶再申請(qǐng)排他鎖時(shí),其他事務(wù)可能又已經(jīng)獲得了相同記錄的共享鎖,從而造成鎖沖突,甚至死鎖。
如果事務(wù)需要修改或鎖定多個(gè)表,則應(yīng)在每個(gè)事務(wù)中以相同的順序使用加鎖語(yǔ)句。在應(yīng)用中,如果不同的程序會(huì)并發(fā)存取多個(gè)表,應(yīng)盡量約定以相同的順序來訪問表,這樣可以大大降低產(chǎn)生死鎖的機(jī)會(huì)。
通過SELECT ... LOCK IN SHARE MODE獲取行的讀鎖后,如果當(dāng)前事務(wù)再需要對(duì)該記錄進(jìn)行更新操作,則很有可能造成死鎖。
改變事務(wù)隔離級(jí)別 如果出現(xiàn)死鎖,可以用 show engine innodb status; 命令來確定最后一個(gè)死鎖產(chǎn)生的原因。
返回結(jié)果中包括死鎖相關(guān)事務(wù)的詳細(xì)信息,如引發(fā)死鎖的 SQL 語(yǔ)句,事務(wù)已經(jīng)獲得的鎖,正在等待什么鎖,以及被回滾的事務(wù)等。據(jù)此可以分析死鎖產(chǎn)生的原因和改進(jìn)措施。
