數(shù)據(jù)庫(kù)鎖設(shè)計(jì)的初衷是處理并發(fā)問題废士。作為多用戶共享的資源蚯姆,當(dāng)出現(xiàn)并發(fā)訪問的時(shí)候媒抠,數(shù)據(jù)庫(kù)需要合理地控制資源的訪問規(guī)則弟断。而鎖就是用來(lái)實(shí)現(xiàn)這些訪問規(guī)則的重要數(shù)據(jù)結(jié)構(gòu)。
根據(jù)加鎖的范圍趴生,MySQL 里面的鎖大致可以分成全局鎖阀趴、表級(jí)鎖和行鎖三類。
全局鎖
顧名思義冲秽,全局鎖就是對(duì)整個(gè)數(shù)據(jù)庫(kù)實(shí)例加鎖。MySQL 提供了一個(gè)加全局讀鎖的方法矩父,命令是 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)等)和更新類事務(wù)的提交語(yǔ)句球订。
全局鎖的典型使用場(chǎng)景是后裸,做全庫(kù)邏輯備份。也就是把整庫(kù)每個(gè)表都 select 出來(lái)存成文本冒滩。
表級(jí)鎖
MySQL 里面表級(jí)別的鎖有兩種:一種是表鎖微驶,一種是元數(shù)據(jù)鎖(meta data lock,MDL)。
表鎖的語(yǔ)法是 lock tables … read/write因苹。與 FTWRL 類似苟耻,可以用 unlock tables 主動(dòng)釋放鎖,也可以在客戶端斷開的時(shí)候自動(dòng)釋放扶檐。需要注意凶杖,lock tables 語(yǔ)法除了會(huì)限制別的線程的讀寫外,也限定了本線程接下來(lái)的操作對(duì)象款筑。
舉個(gè)例子, 如果在某個(gè)線程 A 中執(zhí)行 lock tables t1 read, t2 write; 這個(gè)語(yǔ)句智蝠,則其他線程寫 t1、讀寫 t2 的語(yǔ)句都會(huì)被阻塞奈梳。同時(shí)杈湾,線程 A 在執(zhí)行 unlock tables 之前,也只能執(zhí)行讀 t1颈嚼、讀寫 t2 的操作毛秘。連寫 t1 都不允許,自然也不能訪問其他表阻课。
在還沒有出現(xiàn)更細(xì)粒度的鎖的時(shí)候叫挟,表鎖是最常用的處理并發(fā)的方式。而對(duì)于 InnoDB 這種支持行鎖的引擎限煞,一般不使用 lock tables 命令來(lái)控制并發(fā)抹恳,畢竟鎖住整個(gè)表的影響面還是太大。
另一類表級(jí)的鎖是 MDL(metadata lock)署驻。MDL 不需要顯式使用奋献,在訪問一個(gè)表的時(shí)候會(huì)被自動(dòng)加上。MDL 的作用是旺上,保證讀寫的正確性瓶蚂。
如何安全地給小表加字段?
首先我們要解決長(zhǎng)事務(wù)宣吱,事務(wù)不提交窃这,就會(huì)一直占著 MDL 鎖。在 MySQL 的 information_schema 庫(kù)的 innodb_trx 表中征候,你可以查到當(dāng)前執(zhí)行中的事務(wù)杭攻。如果你要做 DDL 變更的表剛好有長(zhǎng)事務(wù)在執(zhí)行,要考慮先暫停 DDL疤坝,或者 kill 掉這個(gè)長(zhǎng)事務(wù)兆解。
但考慮一下這個(gè)場(chǎng)景。如果你要變更的表是一個(gè)熱點(diǎn)表跑揉,雖然數(shù)據(jù)量不大锅睛,但是上面的請(qǐng)求很頻繁,而你不得不加個(gè)字段,你該怎么做呢衣撬?
這時(shí)候 kill 可能未必管用乖订,因?yàn)樾碌恼?qǐng)求馬上就來(lái)了。比較理想的機(jī)制是具练,在 alter table 語(yǔ)句里面設(shè)定等待時(shí)間乍构,如果在這個(gè)指定的等待時(shí)間里面能夠拿到 MDL 寫鎖最好,拿不到也不要阻塞后面的業(yè)務(wù)語(yǔ)句扛点,先放棄哥遮。之后開發(fā)人員或者 DBA 再通過重試命令重復(fù)這個(gè)過程。
MariaDB 已經(jīng)合并了 AliSQL 的這個(gè)功能陵究,所以這兩個(gè)開源分支目前都支持 DDL NOWAIT/WAIT n 這個(gè)語(yǔ)法眠饮。