前言
鎖在 MySQL 中是非常重要的一部分,鎖對(duì) MySQL 的數(shù)據(jù)訪問(wèn)并發(fā)有著舉足輕重的影響。鎖涉及到的知識(shí)篇幅也很多,所以要啃完并消化到自己的肚子里,是需要靜下心好好反反復(fù)復(fù)幾遍地細(xì)細(xì)品味。本文是對(duì)鎖的一個(gè)大概的整理,一些相關(guān)深入的細(xì)節(jié),還是需要找到相關(guān)書(shū)籍來(lái)繼續(xù)夯實(shí)。
鎖的認(rèn)識(shí)
1.1 鎖的解釋
計(jì)算機(jī)協(xié)調(diào)多個(gè)進(jìn)程或線程并發(fā)訪問(wèn)某一資源的機(jī)制。
1.2 鎖的重要性
在數(shù)據(jù)庫(kù)中,除傳統(tǒng)計(jì)算資源(CPU、RAM、I/O等)的爭(zhēng)搶,數(shù)據(jù)也是一種供多用戶共享的資源。
如何保證數(shù)據(jù)并發(fā)訪問(wèn)的一致性,有效性,是所有數(shù)據(jù)庫(kù)必須要解決的問(wèn)題。
鎖沖突也是影響數(shù)據(jù)庫(kù)并發(fā)訪問(wèn)性能的一個(gè)重要因素,因此鎖對(duì)數(shù)據(jù)庫(kù)尤其重要。
1.3 鎖的缺點(diǎn)
加鎖是消耗資源的,鎖的各種操作,包括獲得鎖、檢測(cè)鎖是否已解除、釋放鎖等 ,都會(huì)增加系統(tǒng)的開(kāi)銷。
1.4 簡(jiǎn)單的例子
現(xiàn)如今網(wǎng)購(gòu)已經(jīng)特別普遍了,比如淘寶雙十一活動(dòng),當(dāng)天的人流量是千萬(wàn)及億級(jí)別的,但商家的庫(kù)存是有限的。
系統(tǒng)為了保證商家的商品庫(kù)存不發(fā)生超賣現(xiàn)象,會(huì)對(duì)商品的庫(kù)存進(jìn)行鎖控制。當(dāng)有用戶正在下單某款商品最后一件時(shí),
系統(tǒng)會(huì)立馬對(duì)該件商品進(jìn)行鎖定,防止其他用戶也重復(fù)下單,直到支付動(dòng)作完成才會(huì)釋放(支付成功則立即減庫(kù)存售罄,支付失敗則立即釋放)。
鎖的類型
2.1 表鎖
種類
讀鎖(read lock),也叫共享鎖(shared lock):針對(duì)同一份數(shù)據(jù),多個(gè)讀操作可以同時(shí)進(jìn)行而不會(huì)互相影響(select)。
寫(xiě)鎖(write lock),也叫排他鎖(exclusive lock):當(dāng)前操作沒(méi)完成之前,會(huì)阻塞其它讀和寫(xiě)操作(update、insert、delete)。
存儲(chǔ)引擎默認(rèn)鎖
MyISAM
特點(diǎn)
1. 對(duì)整張表加鎖
2. 開(kāi)銷小
3. 加鎖快
4. 無(wú)死鎖
5. 鎖粒度大,發(fā)生鎖沖突概率大,并發(fā)性低
結(jié)論
1. 讀鎖會(huì)阻塞寫(xiě)操作,不會(huì)阻塞讀操作
2. 寫(xiě)鎖會(huì)阻塞讀和寫(xiě)操作
建議
MyISAM的讀寫(xiě)鎖調(diào)度是寫(xiě)優(yōu)先,這也是MyISAM不適合做寫(xiě)為主表的引擎,因?yàn)閷?xiě)鎖以后,其它線程不能做任何操作,大量的更新使查詢很難得到鎖,從而造成永遠(yuǎn)阻塞。
2.2 行鎖
種類
讀鎖(read lock),也叫共享鎖(shared lock)
允許一個(gè)事務(wù)去讀一行,阻止其他事務(wù)獲得相同數(shù)據(jù)集的排他鎖
寫(xiě)鎖(write lock),也叫排他鎖(exclusive lock)
允許獲得排他鎖的事務(wù)更新數(shù)據(jù),阻止其他事務(wù)取得相同數(shù)據(jù)集的共享鎖和排他鎖
意向共享鎖(IS)
一個(gè)事務(wù)給一個(gè)數(shù)據(jù)行加共享鎖時(shí),必須先獲得表的IS鎖
意向排它鎖(IX)
一個(gè)事務(wù)給一個(gè)數(shù)據(jù)行加排他鎖時(shí),必須先獲得該表的IX鎖
存儲(chǔ)引擎默認(rèn)鎖
InnoDB
特點(diǎn)
1. 對(duì)一行數(shù)據(jù)加鎖
2. 開(kāi)銷大
3. 加鎖慢
4. 會(huì)出現(xiàn)死鎖
5. 鎖粒度小,發(fā)生鎖沖突概率最低,并發(fā)性高
事務(wù)并發(fā)帶來(lái)的問(wèn)題
1. 更新丟失
解決:讓事務(wù)變成串行操作,而不是并發(fā)的操作,即對(duì)每個(gè)事務(wù)開(kāi)始---對(duì)讀取記錄加排他鎖
2. 臟讀
解決:隔離級(jí)別為Read uncommitted
3. 不可重讀
解決:使用Next-Key Lock算法來(lái)避免
4. 幻讀
解決:間隙鎖(Gap Lock)
2.3 頁(yè)鎖
開(kāi)銷、加鎖時(shí)間和鎖粒度介于表鎖和行鎖之間,會(huì)出現(xiàn)死鎖,并發(fā)處理能力一般(此鎖不做多介紹)
如何上鎖?
3.1 表鎖
隱式上鎖(默認(rèn),自動(dòng)加鎖自動(dòng)釋放)
select //上讀鎖
insert、update、delete //上寫(xiě)鎖
顯式上鎖(手動(dòng))
lock table tableName read;//讀鎖
lock table tableName write;//寫(xiě)鎖
解鎖(手動(dòng))
unlock tables;//所有鎖表
lock table teacher read;// 上讀鎖
select * from teacher; // session01可以正常讀取
select * from teacher;// session02可以正常讀取
update teacher set name = 3 where id =2;//session01報(bào)錯(cuò),因被上讀鎖不能寫(xiě)操作 update teacher set name = 3 where id =2;// session02被阻塞
unlock tables;// 解鎖
update teacher set name = 3 where id =2;// 更新操作成功
lock table teacher write;// 上寫(xiě)鎖
select * from teacher; // session01可以正常讀取
select * from teacher;// session02被阻塞
update teacher set name = 3 where id =2;// session01可以正常更新操作
update teacher set name = 4 where id =2;// session02被阻塞
unlock tables;// 解鎖
select * from teacher;// 讀取成功
update teacher set name = 4 where id =2;// 更新操作成功
3.2 行鎖
隱式上鎖(默認(rèn),自動(dòng)加鎖自動(dòng)釋放)
select //不會(huì)上鎖
insert、update、delete //上寫(xiě)鎖
顯式上鎖(手動(dòng))
select * from tableName lock in share mode;//讀鎖
select * from tableName for update;//寫(xiě)鎖
解鎖(手動(dòng))
1. 提交事務(wù)(commit)
2. 回滾事務(wù)(rollback)
3. kill 阻塞進(jìn)程
begin;
select * from teacher where id = 2 lock in share mode;// session01上讀鎖
select * from teacher where id = 2;// session02可以正常讀取
update teacher set name = 3 where id =2;// session01可以更新操作
update teacher set name = 5 where id =2;// session02被阻塞
commit;
update teacher set name = 5 where id =2;// 更新操作成功
begin;
select * from teacher where id = 2 for update;// session01上寫(xiě)鎖
select * from teacher where id = 2;// session02可以正常讀取
update teacher set name = 3 where id =2;// session01可以更新操作
update teacher set name = 5 where id =2;// session02被阻塞
rollback;
update teacher set name = 5 where id =2;// 更新操作成功
為什么上了寫(xiě)鎖,別的事務(wù)還可以讀操作?
因?yàn)镮nnoD?B有MVCC機(jī)制(多版本并發(fā)控制),可以使用快照讀,而不會(huì)被阻塞。
行鎖的實(shí)現(xiàn)算法
4.1 Record Lock 鎖
單個(gè)行記錄上的鎖(點(diǎn)查)
Record Lock總是會(huì)去鎖住索引記錄,如果InnoDB存儲(chǔ)引擎表建立的時(shí)候沒(méi)有設(shè)置任何一個(gè)索引,這時(shí)InnoDB存儲(chǔ)引擎會(huì)使用隱式的主鍵來(lái)進(jìn)行鎖定
4.2 Gap Lock 鎖(where范圍數(shù)據(jù))
當(dāng)我們用范圍條件而不是相等條件檢索數(shù)據(jù),并請(qǐng)求共享或排他鎖時(shí),InnoDB會(huì)給符合條件的已有數(shù)據(jù)記錄的索引加鎖,對(duì)于鍵值在條件范圍內(nèi)但并不存在的記錄。
優(yōu)點(diǎn):解決了事務(wù)并發(fā)的幻讀問(wèn)題
不足:因?yàn)閝uery執(zhí)行過(guò)程中通過(guò)范圍查找的話,他會(huì)鎖定整個(gè)范圍內(nèi)所有的索引鍵值,即使這個(gè)鍵值并不存在。
間隙鎖有一個(gè)致命的弱點(diǎn),就是當(dāng)鎖定一個(gè)范圍鍵值之后,即使某些不存在的鍵值也會(huì)被無(wú)辜的鎖定,而造成鎖定的時(shí)候無(wú)法插入鎖定鍵值范圍內(nèi)任何數(shù)據(jù)。在某些場(chǎng)景下這可能會(huì)對(duì)性能造成很大的危害。
4.3 Next-key Lock 鎖
同時(shí)鎖住數(shù)據(jù)+間隙鎖
在Repeatable Read隔離級(jí)別下,Next-key Lock 算法是默認(rèn)的行記錄鎖定算法。
4.4 行鎖的注意點(diǎn)
1. 只有通過(guò)索引條件檢索數(shù)據(jù)時(shí),InnoDB才會(huì)使用行級(jí)鎖,否則會(huì)使用表級(jí)鎖(索引失效,行鎖變表鎖)
2. 即使是訪問(wèn)不同行的記錄,如果使用的是相同的索引鍵,會(huì)發(fā)生鎖沖突
3. 如果數(shù)據(jù)表建有多個(gè)索引時(shí),可以通過(guò)不同的索引鎖定不同的行
如何排查鎖?
5.1 表鎖
查看表鎖情況
show open tables;
表鎖分析
show status like 'table%';
1. table_locks_waited
出現(xiàn)表級(jí)鎖定爭(zhēng)用而發(fā)生等待的次數(shù)(不能立即獲取鎖的次數(shù),每等待一次值加1),此值高說(shuō)明存在著較嚴(yán)重的表級(jí)鎖爭(zhēng)用情況
2. table_locks_immediate
產(chǎn)生表級(jí)鎖定次數(shù),不是可以立即獲取鎖的查詢次數(shù),每立即獲取鎖加1
5.2 行鎖
行鎖分析
show status like 'innodb_row_lock%';
1. innodb_row_lock_current_waits //當(dāng)前正在等待鎖定的數(shù)量
2. innodb_row_lock_time //從系統(tǒng)啟動(dòng)到現(xiàn)在鎖定總時(shí)間長(zhǎng)度
3. innodb_row_lock_time_avg //每次等待所花平均時(shí)間
4. innodb_row_lock_time_max //從系統(tǒng)啟動(dòng)到現(xiàn)在等待最長(zhǎng)的一次所花時(shí)間
5. innodb_row_lock_waits //系統(tǒng)啟動(dòng)后到現(xiàn)在總共等待的次數(shù)
information_schema 庫(kù)
1. innodb_lock_waits表
2. innodb_locks表
3. innodb_trx表
優(yōu)化建議
1. 盡可能讓所有數(shù)據(jù)檢索都通過(guò)索引來(lái)完成,避免無(wú)索引行鎖升級(jí)為表鎖
2. 合理設(shè)計(jì)索引,盡量縮小鎖的范圍
3. 盡可能較少檢索條件,避免間隙鎖
4. 盡量控制事務(wù)大小,減少鎖定資源量和時(shí)間長(zhǎng)度
5. 盡可能低級(jí)別事務(wù)隔離
死鎖
6.1 解釋
指兩個(gè)或者多個(gè)事務(wù)在同一資源上相互占用,并請(qǐng)求鎖定對(duì)方占用的資源,從而導(dǎo)致惡性循環(huán)的現(xiàn)象
6.2 產(chǎn)生的條件
1. 互斥條件:一個(gè)資源每次只能被一個(gè)進(jìn)程使用
2. 請(qǐng)求與保持條件:一個(gè)進(jìn)程因請(qǐng)求資源而阻塞時(shí),對(duì)已獲得的資源保持不放
3. 不剝奪條件:進(jìn)程已獲得的資源,在沒(méi)有使用完之前,不能強(qiáng)行剝奪
4. 循環(huán)等待條件:多個(gè)進(jìn)程之間形成的一種互相循環(huán)等待的資源的關(guān)系
6.1 解決
1. 查看死鎖:show engine innodb status \G
2. 自動(dòng)檢測(cè)機(jī)制,超時(shí)自動(dòng)回滾代價(jià)較小的事務(wù)(innodb_lock_wait_timeout 默認(rèn)50s)
3. 人為解決,kill阻塞進(jìn)程(show processlist)
4. wait for graph 等待圖(主動(dòng)檢測(cè))
6.1 如何避免
1. 加鎖順序一致,盡可能一次性鎖定所需的數(shù)據(jù)行
2. 盡量基于primary(主鍵)或unique key更新數(shù)據(jù)
3. 單次操作數(shù)據(jù)量不宜過(guò)多,涉及表盡量少
4. 減少表上索引,減少鎖定資源
5. 盡量使用較低的隔離級(jí)別
6. 盡量使用相同條件訪問(wèn)數(shù)據(jù),這樣可以避免間隙鎖對(duì)并發(fā)的插入影響
7. 精心設(shè)計(jì)索引,盡量使用索引訪問(wèn)數(shù)據(jù)
8. 借助相關(guān)工具:pt-deadlock-logger
樂(lè)觀鎖與悲觀鎖
7.1 悲觀鎖
解釋
假定會(huì)發(fā)生并發(fā)沖突,屏蔽一切可能違反數(shù)據(jù)完整性的操作
實(shí)現(xiàn)機(jī)制
表鎖、行鎖等
實(shí)現(xiàn)層面
數(shù)據(jù)庫(kù)本身
適用場(chǎng)景
并發(fā)量大
7.2 樂(lè)觀鎖
解釋
假設(shè)不會(huì)發(fā)生并發(fā)沖突,只在提交操作時(shí)檢查是否違反數(shù)據(jù)完整性
實(shí)現(xiàn)機(jī)制
提交更新時(shí)檢查版本號(hào)或者時(shí)間戳是否符合
實(shí)現(xiàn)層面
業(yè)務(wù)代碼
適用場(chǎng)景
并發(fā)量小
MySQL鎖機(jī)制起步
鎖是計(jì)算機(jī)用以協(xié)調(diào)多個(gè)進(jìn)程間并發(fā)訪問(wèn)同一共享資源的一種機(jī)制。MySQL中為了保證數(shù)據(jù)訪問(wèn)的一致性與有效性等功能,實(shí)現(xiàn)了鎖機(jī)制,MySQL中的鎖是在服務(wù)器層或者存儲(chǔ)引擎層實(shí)現(xiàn)的。
MVCC ,多版本并發(fā)控制,Multiversion Concurrency Control
大部分的MySQL的存儲(chǔ) 引擎,比如InnoDB,F(xiàn)alcon,以及PBXT并不是簡(jiǎn)簡(jiǎn)單單的使用行鎖機(jī)制。它們都使用了行鎖結(jié)合一種提高并發(fā)的技術(shù),被稱為MVCC(多版本并發(fā)控制)。MVCC并不單單應(yīng)用在MySQL中,其他的數(shù)據(jù)庫(kù)如Oracle,PostgreSQL,以及其他數(shù)據(jù)庫(kù)也使用這個(gè)技術(shù)。
MVCC避免了許多需要加鎖的情形以及降低消耗。這取決于它實(shí)現(xiàn)的方式,它允許非阻塞讀取,在寫(xiě)的操作的時(shí)候阻塞必要的記錄。
MVCC保存了某一時(shí)刻數(shù)據(jù)的一個(gè)快照。意思就是無(wú)論事物運(yùn)行了多久,它們都能看到一致的數(shù)據(jù)。也就是說(shuō)在相同的時(shí)間下,不同的事務(wù),查詢相同表的數(shù)據(jù)是不同的。如果你從來(lái)沒(méi)有這方面的經(jīng)驗(yàn),可能說(shuō)這些有點(diǎn)令人困惑。但是在以后這個(gè)會(huì)很容易理解和熟悉的。
每個(gè)存儲(chǔ)引擎實(shí)現(xiàn)MVCC方式都是不同的。有許多種包含了樂(lè)觀(optimistic)和悲觀(pessimistic)的并發(fā)控制。
我們用簡(jiǎn)單的InnoDb的行為來(lái)舉例說(shuō)明MVCC工作方式。
InnoDB實(shí)現(xiàn)MVCC的方法是,它存儲(chǔ)了每一行的兩個(gè)額外的隱藏字段,這兩個(gè)隱藏字段分別記錄了行的創(chuàng)建的時(shí)間和刪除的時(shí)間。在每個(gè)事件發(fā)生的時(shí) 候,每行存儲(chǔ)版本號(hào),而不是存儲(chǔ)事件實(shí)際發(fā)生的時(shí)間。每次事務(wù)的開(kāi)始這個(gè)版本號(hào)都會(huì)增加。
自記錄時(shí)間開(kāi)始,每個(gè)事務(wù)都會(huì)保存記錄的系統(tǒng)版本號(hào)。依照事務(wù)的版本來(lái)檢查每行的版本號(hào)。在事務(wù)隔離級(jí)別為可重復(fù)讀(RR)的情況下,來(lái)看看怎樣應(yīng)用它。
SELECT
InnoDB檢查每行,要確定它符合兩個(gè)標(biāo)準(zhǔn)。
InnoDB必須知道行的版本號(hào),這個(gè)行的版本號(hào)至少要和事物版本號(hào)一樣的老。(也就是是說(shuō)它的版本號(hào)可能少于或者和事物版本號(hào)相同)。這個(gè)既能確定事物開(kāi)始之前行是存在的,也能確定事物創(chuàng)建或修改了這行。
行的刪除操作的版本一定是未定義的或者大于事物的版本號(hào)。確定了事物開(kāi)始之前,行沒(méi)有被刪除。
符合了以上兩點(diǎn)。會(huì)返回查詢結(jié)果。
INSERT
InnoDB記錄了當(dāng)前新增行的系統(tǒng)版本號(hào)。
DELETE
InnoDB記錄的刪除行的系統(tǒng)版本號(hào)作為行的刪除ID。
UPDATE
InnoDB復(fù)制了一行。這個(gè)新行的版本號(hào)使用了系統(tǒng)版本號(hào)。它也把系統(tǒng)版本號(hào)作為了刪除行的版本。
所有其他記錄的結(jié)果保存是,從未獲得鎖的查詢。這樣它們查詢的數(shù)據(jù)就會(huì)盡可能的快。要確定查詢行要遵循這些標(biāo)準(zhǔn)。缺點(diǎn)是存儲(chǔ)引擎要為每一行存儲(chǔ)更多的數(shù)據(jù),檢查行的時(shí)候要做更多的處理以及其他內(nèi)部的一些操作。
MVCC只能在可重復(fù)讀和可提交讀的隔離級(jí)別下生效。不可提交讀不能使用它的原因是不能讀取符合事物版本的行版本。它們總是讀取最新的行版本。可序列化不能使用MVCC的原因是,它總是要鎖定行。
下面的表說(shuō)明了在MySQL中不同鎖的模式以及并發(fā)級(jí)別。
鎖的策略 | 并發(fā)性 | 開(kāi)銷 | 引擎 |
表 | 最低 | 最低 | MyISAM,Merge,Memory |
行 | 高 | 高 | NDB Cluster |
行和MVCC | 最高 | 最高 | InnoDB,Falcon,PBXT,solidD |
行鎖與表鎖
首先我們來(lái)了解行鎖與表鎖的基本概念,從名字中我們就可以了解:表鎖就是對(duì)整張表進(jìn)行加鎖,而行鎖則是鎖定某行、某幾行數(shù)據(jù)或者行之間的間隙。
各引擎對(duì)鎖的支持情況如下:
行鎖 | 表鎖 | 頁(yè)鎖 | |
---|---|---|---|
MyISAM | √ | ||
BDB | √ | √ | |
InnoDB | √ | √ |
行鎖
A record lock is a lock on an index record. Record locks always lock index records, even if a table is defined with no indexes. For such cases, InnoDB creates a hidden clustered index and uses this index for record locking.
上文出自MySQL的官方文檔,從這里我們可以看出行鎖是作用在索引上的,哪怕你在建表的時(shí)候沒(méi)有定義一個(gè)索引,InnoDB也會(huì)創(chuàng)建一個(gè)聚簇索引并將其作為鎖作用的索引。
這里還是講一下InnoDB中的聚簇索引。每一個(gè)InnoDB表都需要一個(gè)聚簇索引,有且只有一個(gè)。如果你為該表表定義一個(gè)主鍵,那么MySQL將使用主鍵作為聚簇索引;如果你不為定義一個(gè)主鍵,那么MySQL將會(huì)把第一個(gè)唯一索引(而且要求NOT NULL)作為聚簇索引;如果上訴兩種情況都GG,那么MySQL將自動(dòng)創(chuàng)建一個(gè)名字為GEN_CLUST_INDEX
的隱藏聚簇索引。
因?yàn)槭蔷鄞厮饕訠+樹(shù)上的葉子節(jié)點(diǎn)都存儲(chǔ)了數(shù)據(jù)行,那么如果現(xiàn)在是二級(jí)索引呢?InnoDB中的二級(jí)索引的葉節(jié)點(diǎn)存儲(chǔ)的是主鍵值(或者說(shuō)聚簇索引的值),所以通過(guò)二級(jí)索引查詢數(shù)據(jù)時(shí),還需要將對(duì)應(yīng)的主鍵去聚簇索引中再次進(jìn)行查詢。
關(guān)于索引的問(wèn)題就到這,我們用一張直觀的圖來(lái)表示行鎖:
接下來(lái)以兩條SQL的執(zhí)行為例,講解一下InnoDB對(duì)于單行數(shù)據(jù)的加鎖原理:
Copyupdate user set age = 10 where id = 49;
update user set age = 10 where name = 'Tom';
第一條SQL使用主鍵查詢,只需要在 id = 49 這個(gè)主鍵索引上加上鎖。第二條 SQL 使用二級(jí)索引來(lái)查詢,那么首先在 name = Tom 這個(gè)索引上加寫(xiě)鎖,然后由于使用 InnoDB 二級(jí)索引還需再次根據(jù)主鍵索引查詢,所以還需要在 id = 49 這個(gè)主鍵索引上加鎖。
也就是說(shuō)使用主鍵索引需要加一把鎖,使用二級(jí)索引需要在二級(jí)索引和主鍵索引上各加一把鎖。
根據(jù)索引對(duì)單行數(shù)據(jù)進(jìn)行更新的加鎖原理了解了,那如果更新操作涉及多個(gè)行呢,比如下面 SQL 的執(zhí)行場(chǎng)景。
Copyupdate user set age = 10 where id > 49;
上述 SQL 的執(zhí)行過(guò)程如下圖所示。MySQL Server 會(huì)根據(jù) WHERE 條件讀取第一條滿足條件的記錄,然后 InnoDB 引擎會(huì)將第一條記錄返回并加鎖,接著 MySQL Server 發(fā)起更新改行記錄的 UPDATE 請(qǐng)求,更新這條記錄。一條記錄操作完成,再讀取下一條記錄,直至沒(méi)有匹配的記錄為止。
表鎖
上面我們講解行鎖的時(shí)候,操作語(yǔ)句中的條件判斷列都是有建立索引的,那么如果現(xiàn)在的判斷列不存在索引呢?InnoDB既支持行鎖,也支持表鎖,當(dāng)沒(méi)有查詢列沒(méi)有索引時(shí),InnoDB就不會(huì)去搞什么行鎖了,畢竟行鎖一定要有索引,所以它現(xiàn)在搞表鎖,把整張表給鎖住了。那么具體啥是表鎖?還有其他什么情況下也會(huì)進(jìn)行鎖表呢?
表鎖使用的是一次性鎖技術(shù),也就是說(shuō),在會(huì)話開(kāi)始的地方使用 lock 命令將后續(xù)需要用到的表都加上鎖,在表釋放前,只能訪問(wèn)這些加鎖的表,不能訪問(wèn)其他表,直到最后通過(guò) unlock tables 釋放所有表鎖。
除了使用 unlock tables 顯示釋放鎖之外,會(huì)話持有其他表鎖時(shí)執(zhí)行l(wèi)ock table 語(yǔ)句會(huì)釋放會(huì)話之前持有的鎖;會(huì)話持有其他表鎖時(shí)執(zhí)行 start transaction 或者 begin 開(kāi)啟事務(wù)時(shí),也會(huì)釋放之前持有的鎖。
表鎖由 MySQL Server 實(shí)現(xiàn),行鎖則是存儲(chǔ)引擎實(shí)現(xiàn),不同的引擎實(shí)現(xiàn)的不同。在 MySQL 的常用引擎中 InnoDB 支持行鎖,而 MyISAM 則只能使用 MySQL Server 提供的表鎖。
兩種鎖的比較
表鎖:加鎖過(guò)程的開(kāi)銷小,加鎖的速度快;不會(huì)出現(xiàn)死鎖的情況;鎖定的粒度大,發(fā)生鎖沖突的幾率大,并發(fā)度低;
一般在執(zhí)行DDL語(yǔ)句時(shí)會(huì)對(duì)整個(gè)表進(jìn)行加鎖,比如說(shuō) ALTER TABLE 等操作;
如果對(duì)InnoDB的表使用行鎖,被鎖定字段不是主鍵,也沒(méi)有針對(duì)它建立索引的話,那么將會(huì)鎖整張表;
表級(jí)鎖更適合于以查詢?yōu)橹鳎l(fā)用戶少,只有少量按索引條件更新數(shù)據(jù)的應(yīng)用,如Web 應(yīng)用。
行鎖:加鎖過(guò)程的開(kāi)銷大,加鎖的速度慢;會(huì)出現(xiàn)死鎖;鎖定粒度最小,發(fā)生鎖沖突的概率最低,并發(fā)度也最高;
最大程度的支持并發(fā),同時(shí)也帶來(lái)了最大的鎖開(kāi)銷。
在 InnoDB 中,除單個(gè) SQL 組成的事務(wù)外,鎖是逐步獲得的,這就決定了在 InnoDB 中發(fā)生死鎖是可能的。
行級(jí)鎖只在存儲(chǔ)引擎層實(shí)現(xiàn),而 MySQL 服務(wù)器層沒(méi)有實(shí)現(xiàn)。行級(jí)鎖更適合于有大量按索引條件并發(fā)更新少量不同數(shù)據(jù),同時(shí)又有并發(fā)查詢的應(yīng)用,如一些在線事務(wù)處理(OLTP)系統(tǒng)。
MyISAM表鎖
MyISAM表級(jí)鎖模式
表共享讀鎖(Table Read Lock):不會(huì)阻塞其他線程對(duì)同一個(gè)表的讀操作請(qǐng)求,但會(huì)阻塞其他線程的寫(xiě)操作請(qǐng)求;
表獨(dú)占寫(xiě)鎖(Table Write Lock):一旦表被加上獨(dú)占寫(xiě)鎖,那么無(wú)論其他線程是讀操作還是寫(xiě)操作,都會(huì)被阻塞;
默認(rèn)情況下,寫(xiě)鎖比讀鎖具有更高的優(yōu)先級(jí);當(dāng)一個(gè)鎖釋放后,那么它會(huì)優(yōu)先相應(yīng)寫(xiě)鎖等待隊(duì)列中的鎖請(qǐng)求,然后再是讀鎖中等待的獲取鎖的請(qǐng)求。
This ensures that updates to a table are not “starved” even when there is heavy SELECT activity for the table. However, if there are many updates for a table, SELECT statements wait until there are no more updates.
這種設(shè)定也是MyISAM表不適合于有大量更新操作和查詢操作的原因。大量更新操作可能會(huì)造成查詢操作很難以獲取讀鎖,從而過(guò)長(zhǎng)的阻塞。同時(shí)一些需要長(zhǎng)時(shí)間運(yùn)行的查詢操作,也會(huì)使得線程“餓死”,應(yīng)用中應(yīng)盡量避免出現(xiàn)長(zhǎng)時(shí)間運(yùn)行的查詢操作(在可能的情況下可以通過(guò)使用中間表等措施對(duì)SQL語(yǔ)句做一定的“分解”,使每一步查詢都能在較短的時(shí)間內(nèi)完成,從而減少鎖沖突。如果復(fù)雜查詢不可避免,應(yīng)盡量安排在數(shù)據(jù)庫(kù)空閑時(shí)段執(zhí)行,比如一些定期統(tǒng)計(jì)可以安排在夜間執(zhí)行。)
我們可以通過(guò)一些設(shè)置來(lái)調(diào)節(jié)MyISAM的調(diào)度行為:
通過(guò)指定啟動(dòng)參數(shù)
low-priority-updates
,使MyISAM引擎默認(rèn)給予讀請(qǐng)求以優(yōu)先的權(quán)利;通過(guò)執(zhí)行命令
SET LOW_PRIORITY_UPDATES=1
,使該連接發(fā)出的更新請(qǐng)求優(yōu)先級(jí)降低;通過(guò)指定INSERT、UPDATE、DELETE語(yǔ)句的
LOW_PRIORITY
屬性,降低該語(yǔ)句的優(yōu)先級(jí);給系統(tǒng)參數(shù)
max_write_lock_count
設(shè)置一個(gè)合適的值,當(dāng)一個(gè)表的讀鎖達(dá)到這個(gè)值后,MySQL就暫時(shí)將寫(xiě)請(qǐng)求的優(yōu)先級(jí)降低,給讀進(jìn)程一定獲得鎖的機(jī)會(huì)。
MyISAM對(duì)表加鎖分析
MyISAM在執(zhí)行查詢語(yǔ)句(SELECT)前,會(huì)自動(dòng)給涉及的所有表加讀鎖,在執(zhí)行更新操作(UPDATE、DELETE、INSERT等)前,會(huì)自動(dòng)給涉及的表加寫(xiě)鎖,這個(gè)過(guò)程并不需要用戶干預(yù),因此用戶一般不需要直接用 LOCK TABLE 命令給 MyISAM 表顯式加鎖。在自動(dòng)加鎖的情況下,MyISAM 總是一次獲得 SQL 語(yǔ)句所需要的全部鎖,這也正是 MyISAM 表不會(huì)出現(xiàn)死鎖(Deadlock Free)的原因。
MyISAM存儲(chǔ)引擎支持并發(fā)插入,以減少給定表的讀操作和寫(xiě)操作之間的爭(zhēng)用:
如果MyISAM表在數(shù)據(jù)文件中沒(méi)有空閑塊(由于刪除或更新導(dǎo)致的空行),則行始終插入數(shù)據(jù)文件的末尾。在這種情況下,你可以自由混合并發(fā)使用MyISAM表的 INSERT 和 SELECT 語(yǔ)句而不需要加鎖(你可以在其他線程進(jìn)行讀操作的情況下,同時(shí)將行插入到MyISAM表中)。如果文件中有空閑塊,則并發(fā)插入會(huì)被禁止,但當(dāng)所有的空閑塊重新填充有新數(shù)據(jù)時(shí),它又會(huì)自動(dòng)啟用。要控制此行為,可以使用MySQL的concurrent_insert系統(tǒng)變量。
當(dāng)concurrent_insert=0時(shí),不允許并發(fā)插入功能。
當(dāng)concurrent_insert=1時(shí),允許對(duì)沒(méi)有空閑塊的表使用并發(fā)插入,新數(shù)據(jù)位于數(shù)據(jù)文件結(jié)尾(缺省)。
當(dāng)concurrent_insert=2時(shí),不管表有沒(méi)有空想快,都允許在數(shù)據(jù)文件結(jié)尾并發(fā)插入。
顯式加表鎖的應(yīng)用
上面已經(jīng)提及了表鎖的加鎖方式,一般表鎖都是隱式加鎖的,不需要我們?nèi)ブ鲃?dòng)聲明,但是也有需要顯式加鎖的情況,這里簡(jiǎn)單做下介紹:
給MyISAM表顯式加鎖,一般是為了一定程度模擬事務(wù)操作,實(shí)現(xiàn)對(duì)某一時(shí)間點(diǎn)多個(gè)表的一致性讀取。例如,有一個(gè)訂單表orders,其中記錄有訂單的總金額total,同時(shí)還有一個(gè)訂單明細(xì)表 order_detail,其中記錄有訂單每一產(chǎn)品的金額小計(jì) subtotal,假設(shè)我們需要檢查這兩個(gè)表的金額合計(jì)是否相等,可能就需要執(zhí)行如下兩條SQL:
CopySELECT SUM(total) FROM orders;
SELECT SUM(subtotal) FROM order_detail;
這時(shí),如果不先給這兩個(gè)表加鎖,就可能產(chǎn)生錯(cuò)誤的結(jié)果,因?yàn)榈谝粭l語(yǔ)句執(zhí)行過(guò)程中,order_detail表可能已經(jīng)發(fā)生了改變。因此,正確的方法應(yīng)該是:
CopyLOCK tables orders read local,order_detail read local;
SELECT SUM(total) FROM orders;
SELECT SUM(subtotal) FROM order_detail;
Unlock tables;
查看表鎖爭(zhēng)用情況:
可以通過(guò)檢查 table_locks_waited 和 table_locks_immediate 狀態(tài)變量來(lái)分析系統(tǒng)上的表鎖的爭(zhēng)奪,如果 Table_locks_waited 的值比較高,則說(shuō)明存在著較嚴(yán)重的表級(jí)鎖爭(zhēng)用情況:
Copymysql> SHOW STATUS LIKE 'Table%';
+-----------------------+---------+
| Variable_name | Value |
+-----------------------+---------+
| Table_locks_immediate | 1151552 |
| Table_locks_waited | 15324 |
+-----------------------+---------+
InnoDB行鎖與表鎖
InnoDB鎖模式
1)InnoDB中的行鎖
InnoDB實(shí)現(xiàn)了以下兩種類型的行鎖:
共享鎖(S):加了鎖的記錄,所有事務(wù)都能去讀取但不能修改,同時(shí)阻止其他事務(wù)獲得相同數(shù)據(jù)集的排他鎖;
排他鎖(X):允許已經(jīng)獲得排他鎖的事務(wù)去更新數(shù)據(jù),阻止其他事務(wù)取得相同數(shù)據(jù)集的共享讀鎖和排他寫(xiě)鎖;
2)InnoDB表鎖——意向鎖
由于表鎖和行鎖雖然鎖定范圍不同,但是會(huì)相互沖突。當(dāng)你要加表鎖時(shí),勢(shì)必要先遍歷該表的所有記錄,判斷是否有排他鎖。這種遍歷檢查的方式顯然是一種低效的方式,MySQL引入了意向鎖,來(lái)檢測(cè)表鎖和行鎖的沖突。
Intention locks are table-level locks that indicate which type of lock (shared or exclusive) a transaction requires later for a row in a table。
The intention locking protocol is as follows:
Before a transaction can acquire a shared lock on a row in a table, it must first acquire an IS lock or stronger on the table.
Before a transaction can acquire an exclusive lock on a row in a table, it must first acquire an IX lock on the table.
意向鎖也是表級(jí)鎖,分為讀意向鎖(IS鎖)和寫(xiě)意向鎖(IX鎖)。當(dāng)事務(wù)要在記錄上加上行鎖時(shí),要首先在表上加上意向鎖。這樣判斷表中是否有記錄正在加鎖就很簡(jiǎn)單了,只要看下表上是否有意向鎖就行了,從而就能提高效率。
意向鎖之間是不會(huì)產(chǎn)生沖突的,它只會(huì)阻塞表級(jí)讀鎖或?qū)戞i。意向鎖不于行級(jí)鎖發(fā)生沖突。
鎖模式的兼容矩陣
下面表顯示了了各種鎖之間的兼容情況:
X | IX | S | IS | |
---|---|---|---|---|
X | ||||
IX | 兼容 | 兼容 | ||
S | 兼容 | 兼容 | ||
IS | 兼容 | 兼容 | 兼容 |
(注意上面的X與S是說(shuō)表級(jí)的X鎖和S鎖,意向鎖不和行級(jí)鎖發(fā)生沖突)
如果一個(gè)事務(wù)請(qǐng)求的鎖模式與當(dāng)前的鎖兼容,InnoDB就將請(qǐng)求的鎖授予該事務(wù);如果兩者不兼容,那么該事務(wù)就需要等待鎖的釋放。
InnoDB的加鎖方法
意向鎖是 InnoDB 自動(dòng)加的,不需要用戶干預(yù);
對(duì)于UPDATE、DELETE和INSERT語(yǔ)句,InnoDB會(huì)自動(dòng)給涉及的數(shù)據(jù)集加上排他鎖;
-
對(duì)于普通的SELECT語(yǔ)句,InnoDB不會(huì)加任何鎖;事務(wù)可以通過(guò)以下語(yǔ)句顯示給記錄集添加共享鎖或排他鎖:
共享鎖(S):
select * from table_name where ... lock in share mode
。此時(shí)其他 session 仍然可以查詢記錄,并也可以對(duì)該記錄加 share mode 的共享鎖。但是如果當(dāng)前事務(wù)需要對(duì)該記錄進(jìn)行更新操作,則很有可能造成死鎖。排他鎖(X):
select * from table_name where ... for update
。其他session可以查詢記錄,但是不能對(duì)該記錄加共享鎖或排他鎖,只能等待鎖釋放后在加鎖。
select for update
在執(zhí)行這個(gè) select 查詢語(yǔ)句的時(shí)候,會(huì)將對(duì)應(yīng)的索引訪問(wèn)條目加上排他鎖(X鎖),也就是說(shuō)這個(gè)語(yǔ)句對(duì)應(yīng)的鎖就相當(dāng)于update帶來(lái)的效果;
使用場(chǎng)景:為了讓確保自己查找到的數(shù)據(jù)一定是最新數(shù)據(jù),并且查找到后的數(shù)據(jù)值允許自己來(lái)修改,此時(shí)就需要用到select for update語(yǔ)句;
性能分析:select for update語(yǔ)句相當(dāng)于一個(gè)update語(yǔ)句。在業(yè)務(wù)繁忙的情況下,如果事務(wù)沒(méi)有及時(shí)地commit或者rollback可能會(huì)造成事務(wù)長(zhǎng)時(shí)間的等待,從而影響數(shù)據(jù)庫(kù)的并發(fā)使用效率。
select lock in share mode
in share mode 子句的作用就是將查找的數(shù)據(jù)加上一個(gè)share鎖,這個(gè)就是表示其他的事務(wù)只能對(duì)這些數(shù)據(jù)進(jìn)行簡(jiǎn)單的 select 操作,而不能進(jìn)行 DML 操作。
使用場(chǎng)景:為了確保自己查詢的數(shù)據(jù)不會(huì)被其他事務(wù)正在修改,也就是確保自己查詢到的數(shù)據(jù)是最新的數(shù)據(jù),并且不允許其他事務(wù)來(lái)修改數(shù)據(jù)。與select for update不同的是,本事務(wù)在查找完之后不一定能去更新數(shù)據(jù),因?yàn)橛锌赡芷渌聞?wù)也對(duì)同數(shù)據(jù)集使用了 in share mode 的方式加上了S鎖;
性能分析:select lock in share mode 語(yǔ)句是一個(gè)給查找的數(shù)據(jù)上一個(gè)共享鎖(S 鎖)的功能,它允許其他的事務(wù)也對(duì)該數(shù)據(jù)上S鎖,但是不能夠允許對(duì)該數(shù)據(jù)進(jìn)行修改。如果不及時(shí)的commit 或者rollback 也可能會(huì)造成大量的事務(wù)等待。
InnoDB的鎖爭(zhēng)用情況
可以通過(guò)檢查 InnoDB_row_lock 狀態(tài)變量來(lái)分析系統(tǒng)上的行鎖的爭(zhēng)奪情況:
Copymysql> show status like 'innodb_row_lock%';
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| InnoDB_row_lock_current_waits | 0 |
| InnoDB_row_lock_time | 0 |
| InnoDB_row_lock_time_avg | 0 |
| InnoDB_row_lock_time_max | 0 |
| InnoDB_row_lock_waits | 0 |
+-------------------------------+-------+
5 rows in set (0.01 sec)
行鎖的類型
上面我們根據(jù)了鎖的粒度將鎖分為了行鎖與表鎖,接下來(lái)根據(jù)使用場(chǎng)景的不同,又可以將行鎖進(jìn)行進(jìn)一步的劃分:Next-Key Lock、Gap Lock、Record Lock以及插入意向GAP鎖。
不同的鎖鎖定的位置是不同的,比如說(shuō)記錄鎖只鎖定對(duì)應(yīng)的記錄,而間隙鎖鎖住記錄和記錄之間的間隙,Next-key Lock則鎖住所屬記錄之間的間隙。不同的鎖類型鎖定的范圍大致如圖所示:
記錄鎖(Record Lock)
記錄鎖最簡(jiǎn)單的一種行鎖形式,上面我們以及稍微提及過(guò)了。這里補(bǔ)充下的點(diǎn)就是:行鎖是加在索引上的,如果當(dāng)你的查詢語(yǔ)句不走索引的話,那么它就會(huì)升級(jí)到表鎖,最終造成效率低下,所以在寫(xiě)SQL語(yǔ)句時(shí)需要特別注意。
間隙鎖(Gap Lock)
A gap lock is a lock on a gap between index records, or a lock on the gap before the first or after the last index record。
當(dāng)我們使用范圍條件而不是相等條件去檢索,并請(qǐng)求鎖時(shí),InnoDB就會(huì)給符合條件的記錄的索引項(xiàng)加上鎖;而對(duì)于鍵值在條件范圍內(nèi)但并不存在(參考上面所說(shuō)的空閑塊)的記錄,就叫做間隙,InnoDB在此時(shí)也會(huì)對(duì)間隙加鎖,這種記錄鎖+間隙鎖的機(jī)制叫Next-Key Lock。額,扯的有點(diǎn)快。
從上面這句話可以表明間隙鎖是所在兩個(gè)存在的索引之間,是一個(gè)開(kāi)區(qū)間,像最開(kāi)始的那張索引圖,15和18之間,是有(16,17)這個(gè)間隙存在的。
Gap locks in InnoDB are “purely inhibitive”, which means that their only purpose is to prevent other transactions from inserting to the gap. Gap locks can co-exist. A gap lock taken by one transaction does not prevent another transaction from taking a gap lock on the same gap. There is no difference between shared and exclusive gap locks. They do not conflict with each other, and they perform the same function.
上面這段話表明間隙鎖是可以共存的,共享間隙鎖與獨(dú)占間隙鎖之間是沒(méi)有區(qū)別的,兩者之間并不沖突。其存在的目的都是防止其他事務(wù)往間隙中插入新的紀(jì)錄,故而一個(gè)事務(wù)所采取的間隙鎖是不會(huì)去阻止另外一個(gè)事務(wù)在同一個(gè)間隙中加鎖的。
當(dāng)然也不是在什么時(shí)候都會(huì)去加間隙鎖的:
Gap locking can be disabled explicitly. This occurs if you change the transaction isolation level to READ COMMITTED. Under these circumstances, gap locking is disabled for searches and index scans and is used only for foreign-key constraint checking and duplicate-key checking.
這段話表明,在 RU 和 RC 兩種隔離級(jí)別下,即使你使用 select in share mode 或 select for update,也無(wú)法防止幻讀(讀后寫(xiě)的場(chǎng)景)。因?yàn)檫@兩種隔離級(jí)別下只會(huì)有行鎖,而不會(huì)有間隙鎖。而如果是 RR 隔離級(jí)別的話,就會(huì)在間隙上加上間隙鎖。
臨鍵鎖(Next-key Lock)
A next-key lock is a combination of a record lock on the index record and a gap lock on the gap before the index record.
臨鍵鎖是記錄鎖與與間隙鎖的結(jié)合,所以臨鍵鎖與間隙鎖是一個(gè)同時(shí)存在的概念,并且臨鍵鎖是個(gè)左開(kāi)有閉的卻比如(16, 18]。
關(guān)于臨鍵鎖與幻讀,官方文檔有這么一條說(shuō)明:
By default, InnoDB operates in REPEATABLE READ transaction isolation level. In this case, InnoDB uses next-key locks for searches and index scans, which prevents phantom rows.
就是說(shuō) MySQL 默認(rèn)隔離級(jí)別是RR,在這種級(jí)別下,如果你使用 select in share mode 或者 select for update 語(yǔ)句,那么InnoDB會(huì)使用臨鍵鎖(記錄鎖 + 間隙鎖),因而可以防止幻讀;
但是我也在網(wǎng)上看到相關(guān)描述:即使你的隔離級(jí)別是 RR,如果你這是使用普通的select語(yǔ)句,那么此時(shí) InnoDB 引擎將是使用快照讀,而不會(huì)使用任何鎖,因而還是無(wú)法防止幻讀。(其實(shí)普通讀應(yīng)該是快照讀沒(méi)錯(cuò),但是快照讀應(yīng)該是不會(huì)有幻讀幻讀問(wèn)題,mmp)。
插入意向鎖(Insert Intention Lock)
An insert intention lock is a type of gap lock set by INSERT operations prior to row insertion. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6, respectively, each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.
官方文檔已經(jīng)解釋得很清楚了,這里我做個(gè)翻譯機(jī):
插入意圖鎖是一種間隙鎖,在行執(zhí)行 INSERT 之前的插入操作設(shè)置。如果多個(gè)事務(wù) INSERT 到同一個(gè)索引間隙之間,但沒(méi)有在同一位置上插入,則不會(huì)產(chǎn)生任何的沖突。假設(shè)有值為4和7的索引記錄,現(xiàn)在有兩事務(wù)分別嘗試插入值為 5 和 6 的記錄,在獲得插入行的排他鎖之前,都使用插入意向鎖鎖住 4 和 7 之間的間隙,但兩者之間并不會(huì)相互阻塞,因?yàn)檫@兩行并不沖突。
插入意向鎖只會(huì)和 間隙或者 Next-key 鎖沖突,正如上面所說(shuō),間隙鎖作用就是防止其他事務(wù)插入記錄造成幻讀,正是由于在執(zhí)行 INSERT 語(yǔ)句時(shí)需要加插入意向鎖,而插入意向鎖和間隙鎖沖突,從而阻止了插入操作的執(zhí)行。
不同類型鎖之間的兼容
不同類型的鎖之間的兼容如下表所示:
RECORED | GAP | NEXT-KEY | II GAP(插入意向鎖) | |
---|---|---|---|---|
RECORED | 兼容 | 兼容 | ||
GAP | 兼容 | 兼容 | 兼容 | 兼容 |
NEXT-KEY | 兼容 | 兼容 | ||
II GAP | 兼容 | 兼容 |
(其中行表示已有的鎖,列表示意圖加上的鎖)
其中,第一行表示已有的鎖,第一列表示要加的鎖。插入意向鎖較為特殊,所以我們先對(duì)插入意向鎖做個(gè)總結(jié),如下:
插入意向鎖不影響其他事務(wù)加其他任何鎖。也就是說(shuō),一個(gè)事務(wù)已經(jīng)獲取了插入意向鎖,對(duì)其他事務(wù)是沒(méi)有任何影響的;
插入意向鎖與間隙鎖和 Next-key 鎖沖突。也就是說(shuō),一個(gè)事務(wù)想要獲取插入意向鎖,如果有其他事務(wù)已經(jīng)加了間隙鎖或 Next-key 鎖,則會(huì)阻塞。
其他類型的鎖的規(guī)則較為簡(jiǎn)單:
間隙鎖不和其他鎖(不包括插入意向鎖)沖突;
記錄鎖和記錄鎖沖突,Next-key 鎖和 Next-key 鎖沖突,記錄鎖和 Next-key 鎖沖突;
文章寫(xiě)到這里吧,再寫(xiě)下去有點(diǎn)長(zhǎng)了。上面文章中很多信息都來(lái)源網(wǎng)絡(luò),我只是個(gè)搬運(yùn)工,假若哪里有表述錯(cuò)誤,請(qǐng)?jiān)u論區(qū)留言。
參考資料:
InnoDB Locking
把MySQL中的各種鎖及其原理都畫(huà)出來(lái)
MySQL中的鎖(表鎖、行鎖)
關(guān)于MySQL MyISAM 表并發(fā)
【更多閱讀:禪與計(jì)算機(jī)程序設(shè)計(jì)藝術(shù)】
軟件架構(gòu)的本質(zhì)
CORBA 架構(gòu)體系指南(通用對(duì)象請(qǐng)求代理體系架構(gòu))
軟件架構(gòu)師成長(zhǎng)之路: Master Plan for becoming a Software Architect
快看軟件架構(gòu)風(fēng)格總結(jié): 各種歷史和現(xiàn)代軟件架構(gòu)風(fēng)格的快速總結(jié)
怎樣才算是好程序員?關(guān)于好程序員與好代碼的雜談
關(guān)于軟件架構(gòu)設(shè)計(jì)的核心思想與標(biāo)準(zhǔn) ( IEEE 1471 2000 )
關(guān)系代數(shù)(Relational Algebra)——極簡(jiǎn)教程
【操作系統(tǒng)架構(gòu)原理】資源管理技術(shù)與進(jìn)程的抽象設(shè)計(jì)思想
軟件“生命”系統(tǒng)進(jìn)化論——軟件以負(fù)熵為生!
圖文詳解: 操作系統(tǒng)之內(nèi)存管理 ( 內(nèi)存模型,虛擬內(nèi)存,MMU, TLB,頁(yè)面置換算法,分段等)
成為架構(gòu)師系列: 怎樣畫(huà)系統(tǒng)架構(gòu)圖? 背后的本質(zhì)是對(duì)問(wèn)題的本質(zhì)思考
《編程的原則:改善代碼質(zhì)量的101個(gè)方法》讀書(shū)筆記
UNIX 設(shè)計(jì)哲學(xué):Do one thing and do it well
計(jì)算簡(jiǎn)史:什么是計(jì)算機(jī)?《禪與計(jì)算機(jī)程序設(shè)計(jì)藝術(shù)》
編程語(yǔ)言進(jìn)化史《禪與計(jì)算機(jī)程序設(shè)計(jì)藝術(shù)》
“風(fēng)味人間”與計(jì)算機(jī)程序設(shè)計(jì)藝術(shù)《禪與計(jì)算機(jī)程序設(shè)計(jì)藝術(shù)》
編程為什么有趣?淺談編程的快樂(lè)。
附:MySQL中的鎖思維導(dǎo)圖:
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-461399.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-461399.html
到了這里,關(guān)于【史上最全】MySQL各種鎖詳解:一文搞懂MySQL的各種鎖的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!