全局鎖
命令 Flush tables with read lock (FTWRL)
- 阻塞
當(dāng)你需要讓整個(gè)庫(kù)處于只讀狀態(tài)的時(shí)候次绘,可以使用這個(gè)命令嫂伞,之后其他線程的以下語(yǔ)句會(huì)被阻塞:數(shù)據(jù)更新語(yǔ)句(數(shù)據(jù)的增刪改)琳彩、數(shù)據(jù)定義語(yǔ)句(包括建表、修改表結(jié)構(gòu)等)和更新類(lèi)事務(wù)的提交語(yǔ)句。 - 適用場(chǎng)景 :做全庫(kù)邏輯備份
- 不加鎖的話偎血,備份系統(tǒng)備份的得到的庫(kù)不是一個(gè)邏輯時(shí)間點(diǎn)霸株,這個(gè)視圖是邏輯不一致的雕沉。
- 官方自帶的邏輯備份工具是 mysqldump。當(dāng) mysqldump 使用參數(shù)–single-transaction 的時(shí)候去件,導(dǎo)數(shù)據(jù)之前就會(huì)啟動(dòng)一個(gè)事務(wù)坡椒,來(lái)確保拿到一致性視圖。而由于 MVCC 的支持尤溜,這個(gè)過(guò)程中數(shù)據(jù)是可以正常更新的倔叼。
- 對(duì)于 MyISAM 這種不支持事務(wù)的引擎,必須全局鎖
另一種全局鎖 不推薦使用
set global readonly=true 的方式
- readonly 的值會(huì)被用來(lái)做其他邏輯宫莱,比如用來(lái)判斷一個(gè)庫(kù)是主庫(kù)還是備庫(kù)
- 執(zhí)行異常斷開(kāi)后不會(huì)釋放全局鎖
表級(jí)鎖
一種是表鎖丈攒, lock tables … read/write
- 表鎖的語(yǔ)法是線程會(huì)造成阻塞,在還沒(méi)有出現(xiàn)更細(xì)粒度的鎖的時(shí)候授霸,表鎖是最常用的處理并發(fā)的方式
表級(jí)鎖 meta data lock拗盒,MDL
一種是元數(shù)據(jù)鎖(meta data lock,MDL)涉兽。
- 讀鎖之間不互斥嗤谚,因此你可以有多個(gè)線程同時(shí)對(duì)一張表增刪改查。
- 讀寫(xiě)鎖之間藏畅、寫(xiě)鎖之間是互斥的敷硅,用來(lái)保證變更表結(jié)構(gòu)操作的安全性。同一個(gè)表會(huì)順序加字段
- 事務(wù)中的 MDL 鎖愉阎,在語(yǔ)句執(zhí)行開(kāi)始時(shí)申請(qǐng)绞蹦,但是語(yǔ)句結(jié)束后并不會(huì)馬上釋放,而會(huì)等到整個(gè)事務(wù)提交后再釋放榜旦。MDL讀鎖與MDL 寫(xiě)鎖(ALter改變表結(jié)構(gòu)的)幽七,讀鎖沒(méi)釋放,寫(xiě)鎖被阻塞導(dǎo)致溅呢,下一個(gè)讀鎖依賴(lài)寫(xiě)鎖被釋放澡屡,導(dǎo)致表讀寫(xiě)不可用, 使用NOWAIT/WAIT 等待執(zhí)行咐旧,不成功可以重復(fù)操作
行鎖
- 兩階段鎖:在 InnoDB 事務(wù)中驶鹉,行鎖是在需要的時(shí)候才加上的,但并不是不需要了就立刻釋放铣墨,而是要等到事務(wù)結(jié)束時(shí)才釋放室埋。這個(gè)就是兩階段鎖協(xié)議。
- 如果你的事務(wù)中需要鎖多個(gè)行,要把最可能造成鎖沖突姚淆、最可能影響并發(fā)度的鎖盡量往后放孕蝉。
死鎖
兩個(gè)事務(wù)互相等待對(duì)方先提交會(huì)造成死鎖
- 一種策略是,直接進(jìn)入等待腌逢,直到超時(shí)降淮。這個(gè)超時(shí)時(shí)間可以通過(guò)參數(shù) innodb_lock_wait_timeout 來(lái)設(shè)置,InnoDB引擎默認(rèn)值是50s搏讶。
- 另一種策略是骤肛,發(fā)起死鎖檢測(cè),發(fā)現(xiàn)死鎖后窍蓝,主動(dòng)回滾死鎖鏈條中的某一個(gè)事務(wù)腋颠,讓其他事務(wù)得以繼續(xù)執(zhí)行。將參數(shù) innodb_deadlock_detect 設(shè)置為 on吓笙,表示開(kāi)啟這個(gè)邏輯淑玫。
- 死鎖檢查會(huì)消耗大量CPU,如何解決
熱點(diǎn)數(shù)據(jù)性能問(wèn)題
- 如果你能確保這個(gè)業(yè)務(wù)一定不會(huì)出現(xiàn)死鎖面睛,可以臨時(shí)把死鎖檢測(cè)關(guān)閉掉絮蒿。一般不建議采用
- 控制并發(fā)度,對(duì)應(yīng)相同行的更新叁鉴,在進(jìn)入引擎之前排隊(duì)土涝。這樣在InnoDB內(nèi)部就不會(huì)有大量的死鎖檢測(cè)工作了。
- 將熱更新的行數(shù)據(jù)拆分成邏輯上的多行來(lái)減少鎖沖突幌墓,但是業(yè)務(wù)復(fù)雜度可能會(huì)大大提高但壮。
檢查死鎖
show engine innodb status
事務(wù)的啟動(dòng)方式
begin/start transaction 命令并不是一個(gè)事務(wù)的起點(diǎn),在執(zhí)行到它們之后的第一個(gè)操作 InnoDB 表的語(yǔ)句常侣,事務(wù)才真正啟動(dòng)蜡饵。如果你想要馬上啟動(dòng)一個(gè)事務(wù),可以使用 start transaction with consistent snapshot 這個(gè)命令胳施。
- 第一種啟動(dòng)方式溯祸,一致性視圖是在執(zhí)行第一個(gè)快照讀語(yǔ)句時(shí)創(chuàng)建的;
- 第二種啟動(dòng)方式舞肆,一致性視圖是在執(zhí)行 start transaction with consistent snapshot 時(shí)創(chuàng)建的焦辅。
事務(wù)id
開(kāi)啟事務(wù)會(huì)獲得一個(gè)事務(wù)id(自增),數(shù)據(jù)版本是按照提交時(shí)間來(lái)排序的
所以 A看到的還是1椿胯,一致性原則筷登。
事務(wù)id的排序是 A<B<C
數(shù)據(jù)版本的排序是C早于B
B如果再自己update之前查詢(xún),查到的還是1压状,因?yàn)橐恢滦?br>
但是自己update之后查到的就是3仆抵,原因是當(dāng)前讀 再最新的數(shù)據(jù)版本上執(zhí)行更新。
更新數(shù)據(jù)都是先讀后寫(xiě)的种冬,而這個(gè)讀镣丑,只能讀當(dāng)前的值,稱(chēng)為“當(dāng)前讀”(current read)娱两。
事務(wù)是如何實(shí)現(xiàn)的MVCC呢?
- 每個(gè)事務(wù)都有一個(gè)事務(wù)ID,叫做transaction id(嚴(yán)格遞增)
- 事務(wù)在啟動(dòng)時(shí),找到已提交的最大事務(wù)ID記為up_limit_id莺匠。
- 事務(wù)在更新一條語(yǔ)句時(shí),比如id=1改為了id=2.會(huì)把id=1和該行之前的row trx_id寫(xiě)到undo log里,
并且在數(shù)據(jù)頁(yè)上把id的值改為2,并且把修改這條語(yǔ)句的transaction id記在該行行頭 - 再定一個(gè)規(guī)矩,一個(gè)事務(wù)要查看一條數(shù)據(jù)時(shí),必須先用該事務(wù)的up_limit_id與該行的transaction id做比對(duì),
如果up_limit_id>=transaction id,那么可以看.如果up_limit_id<transaction id,則只能去undo log里去取。去undo log查找數(shù)據(jù)的時(shí)候,也需要做比對(duì),必須up_limit_id>transaction id,才返回?cái)?shù)據(jù)