數(shù)據(jù)庫鎖設(shè)計(jì)的初衷是處理并發(fā)問題。作為多用戶共享的資源纲刀,當(dāng)出現(xiàn)并發(fā)訪問的時(shí)候项炼,數(shù)據(jù)庫需要合理地控制資源的訪問規(guī)則。而鎖就是用來實(shí)現(xiàn)這些訪問規(guī)則的重要數(shù)據(jù)結(jié)構(gòu)示绊。
根據(jù)加鎖的范圍锭部,mysql里面的鎖大致分為3類: 全局鎖,表級(jí)鎖面褐,行鎖拌禾。
全局鎖:就是對(duì)整個(gè)數(shù)據(jù)庫實(shí)例加鎖。加全局讀鎖的方法展哭,F(xiàn)TWRL(flush tables with read lock)湃窍,使用這個(gè)命令之后,整個(gè)庫處于只讀狀態(tài)摄杂,其他線程的一下語句會(huì)被阻塞:數(shù)據(jù)跟新語句(數(shù)據(jù)的增刪改)坝咐,數(shù)據(jù)定義語句(包括建表,修改表結(jié)構(gòu))析恢,跟新類事務(wù)的提交語句墨坚。
全局鎖的典型使用場(chǎng)景是,做全庫邏輯備份(把整個(gè)庫都select出來存成文本)映挂。Mysql自帶的邏輯備份工具是mysqldump泽篮,當(dāng)mysqldump使用參數(shù)--single-transation的時(shí)候,到數(shù)據(jù)之前就會(huì)啟動(dòng)一個(gè)事務(wù)柑船,來確保拿到一個(gè)一致性視圖帽撑。一致性讀(single-transation)的方法只適用于所有的表使用事務(wù)引擎的庫,如果有的表使用了不支持事務(wù)的引擎(例如 MyISAM)那么備份就只能通過FTWRL的方法實(shí)現(xiàn)鞍时。
為什么不使用set global readonly=true的方式將全庫設(shè)置為只讀亏拉?1. 在有些系統(tǒng)中,readonly的值會(huì)被用于來作其他邏輯逆巍,例如用來判斷一個(gè)庫是主庫還是備庫及塘,如果修改了這個(gè)全局變量,可能會(huì)造成其他影響锐极。2笙僚,Mysql在readonly和FTWRL在異常處理機(jī)制上不同。如果執(zhí)行FTWRL命令之后由于客戶端異常斷開連接灵再,Mysql會(huì)自動(dòng)釋放這個(gè)全局鎖肋层,整個(gè)庫回到可以正常跟新的狀態(tài)亿笤;但是把數(shù)據(jù)庫設(shè)置為readonly之后,如果客戶端發(fā)生異常栋猖,數(shù)據(jù)庫就會(huì)一直保持readonly狀態(tài)净薛,庫將處于不可寫狀態(tài)。
Mysql中的表級(jí)鎖有兩種:表鎖掂铐,元數(shù)據(jù)鎖(metadata lock MDL)
表鎖的語法是lock tables *** read/write罕拂。與FTWRL類似,可以用unlock table主動(dòng)釋放鎖全陨,也可以在客戶端斷開連接的時(shí)候自動(dòng)釋放爆班。lock table語法會(huì)限制本線程接下來的操作對(duì)象,也會(huì)限制別的線程的讀寫辱姨。
舉個(gè)例子, 如果在某個(gè)線程 A 中執(zhí)行 lock tablet1 read, t2 write; 這個(gè)語句柿菩,則其他線程寫t1、讀寫 t2 的語句都會(huì)被阻塞雨涛。同時(shí)枢舶,線程 A 在執(zhí)行unlock tables 之前,也只能執(zhí)行讀 t1替久、讀寫t2 的操作凉泄。連寫 t1 都不允許,自然也不能訪問其他表蚯根。后众??颅拦?蒂誉??
另一類表級(jí)鎖是MDL(metadata lock)MDL不需要顯示使用距帅,為了保證讀寫的正確性右锨,在訪問一個(gè)表的時(shí)候會(huì)被自動(dòng)加上。Mysql5.5之后碌秸,當(dāng)對(duì)一個(gè)表作怎刪改查操作的時(shí)候绍移,加MDL讀鎖,當(dāng)對(duì)表作結(jié)構(gòu)變更操作的時(shí)候讥电,加MDL寫鎖登夫。
讀鎖之間不互斥,因此可以有多個(gè)線程同時(shí)對(duì)一個(gè)表怎刪改查允趟。
讀寫鎖之間,寫鎖之間是互斥的鸦致,用來保證變更表結(jié)構(gòu)操作的安全性潮剪。如果有兩個(gè)線程要同時(shí)給一個(gè)表加字段涣楷,其中一個(gè)要等另一個(gè)執(zhí)行完才能開始執(zhí)行。
如何安全地給小表加字段抗碰?
首先要解決長事務(wù)狮斗,事務(wù)不提交,就會(huì)一直占著MDL鎖弧蝇。在mysql的information_schema庫中的innodb_trx表中碳褒,可以查到當(dāng)前執(zhí)行的事務(wù),如果在執(zhí)行DDL的表剛好有長事務(wù)在執(zhí)行看疗,需要先暫停DDL沙峻,或者kill掉這個(gè)長事務(wù)。但是如果是個(gè)常被訪問的表两芳,kill操作也未必管用摔寨,新的請(qǐng)求馬上就來了,比較理想的做法是怖辆,在alter table語句中設(shè)定一個(gè)等待時(shí)間是复,如果在規(guī)定時(shí)間內(nèi)能拿到MDL寫鎖最好,如果沒有拿到也不會(huì)阻塞后面的業(yè)務(wù)語句竖螃。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ALTER TABLE tbl_name NOWAIT add column ...
??????????????????????????????????????????????????????????????ALTER TABLE tbl_name WAIT N add column ...
FTWRL 前有讀寫的話淑廊,F(xiàn)TWRL 都會(huì)等待讀寫執(zhí)行完畢后才執(zhí)行。
FTWRL 執(zhí)行的時(shí)候要刷臟頁的數(shù)據(jù)到磁盤特咆,因?yàn)橐3謹(jǐn)?shù)據(jù)的一致性 季惩,理解的執(zhí)行FTWRL時(shí)候是所有事務(wù)都提交完畢的時(shí)候。
mysqldump + -single-transaction 也是保證事務(wù)的一致性,但他只針對(duì) 有支持事務(wù) 引擎坚弱,比如 innodb蜀备,所以 還是強(qiáng)烈建議大家在創(chuàng)建實(shí)例,表時(shí)候需要innodb 引擎為好荒叶。
全庫只讀 readonly = true 還有個(gè)情況在 slave上如果用戶有超級(jí)權(quán)限的話 readonly 是失效的
表級(jí)別鎖 :一個(gè)直接就是表鎖 lock table 建議不要使用, 影響太大碾阁,另個(gè)就是 MDL 元數(shù)據(jù)鎖
MDL 是并發(fā)情況下維護(hù)數(shù)據(jù)的一致性,在表上有事務(wù)的時(shí)候,不可以對(duì)元數(shù)據(jù)經(jīng)行寫入操作,并且這個(gè)是在server層面實(shí)現(xiàn)的
當(dāng)你做 dml 時(shí)候增加的 MDL 讀鎖, update table set id=Y where id=X; 并且由于隔離級(jí)別的原因 讀鎖之間不沖突
當(dāng)你DDL 時(shí)候 增加對(duì)表的寫鎖, 同時(shí)操作兩個(gè)alter table 操作 這個(gè)要出現(xiàn)等待情況。
但是 如果是 dml 與ddl 之間的交互 就更容易出現(xiàn)不可讀寫情況,這個(gè)情況容易session 爆滿,session是占用內(nèi)存的,也會(huì)導(dǎo)致內(nèi)存升高
MDL 釋放的情況就是 事務(wù)提交.
主庫上的一個(gè)小表做了一個(gè) DDL, 同步給slave ,由于這個(gè)時(shí)候有了先前的 single-transaction,所以slave 就會(huì)出現(xiàn) 該表的 鎖等待, 并且slave 出現(xiàn)延遲
MDL作用是防止DDL和DML并發(fā)的沖突些楣,個(gè)人感覺應(yīng)該寫清楚脂凶,一開始理解為select和update之間的并發(fā)。
Online DDL的過程是這樣的:
1. 拿MDL寫鎖
2. 降級(jí)成MDL讀鎖
3. 真正做DDL
4. 升級(jí)成MDL寫鎖
5. 釋放MDL鎖
1愁茁、2蚕钦、4、5如果沒有鎖沖突鹅很,執(zhí)行時(shí)間非常短嘶居。第3步占用了DDL絕大部分時(shí)間,這期間這個(gè)表可以正常讀寫數(shù)據(jù),是因此稱為“online ”