作者:魏新平,知數(shù)堂第5期MySQL實戰(zhàn)班學員,第10期MySQL優(yōu)化班學員,現(xiàn)任職助教。
Describing safe, blocking, atomic, pure-mysql cut-over phase
原文鏈接:https://github.com/github/gh-ost/issues/82
作者:shlomi-noach
我們提供的方式是基于兩個數(shù)據(jù)庫連接的。假如我們的連接是C10,C20。應用的連接是C1..C9,C11..C19,C21..C29。
C1..C9在 tbl 上進行正常的dml操作:INSERT, UPDATE, DELETE
C10: CREATE TABLE tbl_old (id int primary key) COMMENT='magic-be-here'
C10: LOCK TABLES tbl WRITE, tbl_old WRITE
C11..C19,新進來的對tbl的操作,由于C10的鎖,會被阻塞
C20: RENAME TABLE tbl TO tbl_old, ghost TO tbl
由于C10加的鎖,也會被阻塞住。但是當鎖被釋放后,會比C1..C9,C11..C19先執(zhí)行。
C21..C29,新進來對tbl的dml操作還是會被阻塞住
C10: 檢測C20的rename操作是否存在(在show processlist當中尋找rename關鍵字)
C10: DROP TABLE tbl_old
大家還是被鎖住,什么嚴重的事情都不會發(fā)生,除了刪除這個tbl_old表。
C10:UNLOCK TABLES
BAM(象聲詞,不知道怎么翻譯,尷尬,只能意會不能言傳)!RENAME操作會先執(zhí)行,ghost表會被重命名為tbl表,然后C1..C9,C11..C19,C21..C29會直接在新的tbl表上執(zhí)行。
一些解釋:
創(chuàng)建tbl_old是為了阻止C20的RENAME操作
當一個連接擁有對某個表的WRITE鎖的時候,可以執(zhí)行drop該表的操作。
不管是誰先被阻塞,當一個表的INSERT/UPDATE/DELETE操作和RENAME操作同時被阻塞的情況下,RENAME操作總是會先執(zhí)行。
假如上面的過程當中C10或者C20失敗了,會發(fā)生什么呢
先說結論,就算失敗了,不會發(fā)生災難性的事情,也不需要回滾。
假如C10在CREATE tbl_old的時候發(fā)生錯誤,直接退出
假如C10在LOCK tbl,tbl_old的語句發(fā)生錯誤,直接退出,表不會被鎖住,app可以繼續(xù)對tbl進行dml操作
假如C10在C20剛要執(zhí)行RENAME操作的時候連接直接掛了
WRITE鎖會被釋放掉,C1..C9,C11..C19可以繼續(xù)在原表執(zhí)行
C20會因為tbl_old表的存在而RENAME失敗
整個操作失敗,但是沒什么嚴重的問題產(chǎn)生,只是一些語句被阻塞了很短的一段時間。我們會重試整個cut-over流程。
假如C10在C20被阻塞后掛了,發(fā)生的事情和上面的流程差不多。鎖釋放,C20失?。ㄒ驗閠bl_old的存在),其他所有的被阻塞的語句會正常在原來的表上執(zhí)行
假如C20在C10 DROP表之前掛了,我們會捕捉到錯誤并按計劃執(zhí)行。刪除表并且釋放鎖。沒什么嚴重的事情發(fā)生,頂多一些語句被阻塞一會。我們需要重新嘗試整個流程。
假如C20在C10 DROP后釋放鎖之前掛了,和上面發(fā)生的事情一樣。
假如C10和C20都掛了。鎖被釋放,RENAME失敗,C1..C9,C11..C19,C21..C29阻塞的語句會正常在tbl上執(zhí)行。
不管發(fā)生什么事情,在操作的最后我們都會檢查ghost表是否還存在。假如不在了,那就說明操作成功了。整個流程可以被看成是原子性的。
順便說一下,如果操作失敗了,可能會存在table_old表需要我們手動刪除。其實刪不刪除都無所謂。如果你看不慣可以刪除掉,不刪除掉的話,也沒關系,下一次操作就不用重建了。
對應用的影響
在流程開始之后到流程結束的時間里,不管是成功還是失敗,應用的連接都會被阻塞住。成功的話,阻塞的語句會被執(zhí)行到新表,失敗的話,阻塞的語句會執(zhí)行到舊表。
對復制的影響
復制只會看到RENAME操作,binlog是不會記錄lock語句的。所以復制看到的是原子性的兩表交換,不會有表不存在的情況。
針對網(wǎng)友的一些提問
為啥要用兩個連接進行這么麻煩的流程?
因為一個連接在獲取tbl的鎖的情況下,無法進行rename操作(至少現(xiàn)在不能)。但是作者說他會說服工程師在MYSQL的下個版本當中實現(xiàn),就不需要這么麻煩的操作了。(下面代碼測試是我測試的,不是作者的)
admin@localhost [test] 11:39:32>lock table t write;
Query OK, 0 rows affected (0.00 sec)
admin@localhost [test] 11:39:36>rename table t to t10;
ERROR 1192 (HY000): Can't execute the given command because you have active locked tables or an active transaction
admin@localhost [test] 11:39:48>select @@version;
+---------------+
| @@version |
+---------------+
| 5.7.22-22-log |
+---------------+
1 row in set (0.00 sec)
為啥要鎖原來的表和創(chuàng)建的tbl_old表?
由于異步的應用binlog的日志,如果不鎖住原表的話,可能會存在一些語句未被應用。為啥要鎖住tbl_old表呢,作者自己也不太記得了,因為時間的原因,畢竟是回憶三年前的事情了,不過肯定是有原因的。