gh-ost核心原理:資料一致性和cut-over
研發同學問,gh-ost做DDL時,業務可對原表做DML,同時一邊apply binlog event,一邊rowcopy到新表,那麼問題來了,這仨操作存在先後順序,不按順序會產生資料不一致嗎?
既然是說gh-ost,那麼先放個圖鎮樓。
繼續說正文)首先,舉個例子:
id | balance
--------------
1 | 100
變更過程中,因為不阻塞
DML
,業務先對原始表
+100
(
同時產生了該
UPDATE
的
binlog event
)
:
id | balance
--------------
1 | 200
這行再被
INSERT
到新表,此時
id=1
的
balance
已經是
200
。
最後解析
binlog apply
到新表時,將
UPDATE
語句再次
apply
,
balance
會變為
300
嗎?
所以,不一樣的順序是否會造成資料不一致?
DDL
更變的過程中,涉及三種操作:
A、rowcopy(即資料copy到新表)
B、正常新增的DML(指對原表執行DML)
C、binlog apply到新表(指分析binlog
event後應用到新表)
首先明確一點,
C
一定要在
B
之後,先生成了
binlog event
,才會有對新表的
apply
所以更變時可能出現三種組合,如下:
1)A->B->C,原始資料先被copy到新表,原表產生binlog,再解析binlog去對新表做apply。
2)B->C->A,主庫產生binlog,再應用binlog,copyrow到新表。
3)B->A->C,主庫產生binlog,copyrow到新表,再應用binlog。
首先想一下
gh-ost
的對錶的兩個核心限制:
1)必須有PK(或不存在
NULL的UK),可以精準定位到某一行。
2)binlog_format=ROW,可以拿到整行資料。
有了這兩個前提,
gh-ost
會認為
binlog events
的資料是最準的,於是討論一致性,就簡單了:
分析一下rowcopy和DML的實現
-
rowcopy):
純INSERT
操作,其實就對原表做SELECT
,對新表INSERT INTO IGNORE
-
對於原始
binlog events
,binlog apply
時的幾個改寫):
對於
INSERT的DML,
apply時改為
REPLACE
INTO
對於
DELETE的DML,還是
DELETE
對於
UPDATE的DML,更新完整一行
-
INSERT):
如果apply先,rowcopy後:
比如對於PK=1的,自然以binlog為準,rowcopy遇到PK=1,IGNORE了
如果rowcopy先,apply後:
比如對於PK=1的,
REPLACE
INTO掉,依然以
binlog為準,蓋掉整行
所以rowcopy和
binlog
events相遇,
binlog為準。
-
DELETE):
1)A->B->C,這種組合始終不影響,先前rowcopy的資料在整個過程中產生了B和
C,刪掉了這一行很正常。
2)B->C->A,BC先做,資料刪掉了,A再rowcopy,這個時候對原表執行
SELECT,等於沒讀到資料,什麼都沒查到,沒所謂。
3)B->A->C,B先產生DELETE語句,A再rowcopy,對原表執行SELECT,什麼數
據沒讀到,依然沒所謂。C在做apply的時候等於沒做。
-
UPDATE):
1)A->B->C,A先rowcopy,B再生成binlog更新,最後apply,因為binlog記錄整行資料,
所以也更新整行資料就好,不會出現重複更新的情況。
2)B->C->A,BC先做,因為A還沒做,實際上執行C的時候,新表是找不到PK=
1
這行資料的,更新了個空氣,此時再做A,就等於把整行INSERT進來了,安全。
3)B->A->C,B先產生一條UPDATE,A做rowcopy的時候,INSERT的是最新的數
據,最後C做apply的時候,以binlog為準,因為還是更新整行資料,所以依然也不會出現重複更新的情況。
有沒有一點像
Redis
的
AOF
,無論中間出現什麼,重寫時就以最後一條為準。 唯一的
KEY
對應唯一的
VALUE
,無論整個過程中發生了些什麼。
btw
,隔壁
pt-osc
也是有類似的
SQL
改寫邏輯。
所以可以一邊
copy
原始資料到新表,一邊用觸發器捕獲對原表的變更,並寫到新表。
再康康cut-over
cut-over
簡單理解為就是最後原表和新表做交換的整個過程。或者叫切換。
簡單來說,
gh-ost
用兩個連線(以下用A和B替代)做了一些事,按時間順序如下:
A)、
CREATE
TABLE tbl_old (
id
int primary
key)
COMMENT=
'magic-be-here'
然後
LOCK
TABLES tbl WRITE, tbl_old WRITE
B)、
RENAME
TABLE tbl
TO tbl_old, ghost
TO tbl。
這個時候B會話會hang著。
A)、檢查B的被blocked的
RENAME(透過
SHOW
PROCESSLIST),
繼續做
DROP
TABLE tbl_old,
UNLOCK
TABLES;,
整個過程,依然是有很多其他會話嘗試進行DML操作的。
那麼這套流程是如何保證原子性的呢?
看看任何一步失敗的影響:
1)如果A做
CREATE tbl_old時掛,沒所謂,不影響業務。
2)如果A做
LOCK
TABLE tbl/tbl_old時掛了,不影響業務。
3)如果A在
CREATE和
LOCK都成功後,連線斷開,不影響業務:
①,此時B還沒執行:A斷開,寫鎖釋放,準備做
RENAME時,會報表tbl_old存在。
②,此時B等待執行(被hang住):A斷開,寫鎖釋放,終於可以
RENAME了,
還是會報表tbl_old存在。
-- 所以,tbl_old這個空表的作用就出來了。
4)如果B在
RENAME前(也就是hang住時)掛了,gh-ost會捕獲這個錯誤,
並且繼續按原計劃執行
DROP和
UNLOCK,這樣也不影響業務
5)如果B在A做
DROP
TABLE時掛了,A一樣會按原計劃執行
DROP和
UNLOCK,依舊不影響業務。
總之,上面的任何一步出現問題,
gh-ost
都會檢查下新表在不在,如果不在了,說明已經成功了。如果還在,那就說明最後的
cut-over
過程失敗了。
對了,如果
LOCK TABLE
時加不上,也可以控制加鎖等待時長的,預設應該是
3s
,參考(由
cut-over-lock-timeout-seconds
控制),重試次數由
default-retries
控制,預設
60
次。
整個過程可以大概如下:
-
會話A
創tbl_old
表和對原表、tbl_old
加鎖,當然也有可能加不上鎖,因為可能之前的會話還未提交等 -
對該表的新讀寫請求被阻塞
-
會話B
執行RENAME
,被阻塞 -
對該表的新增的讀寫請求繼續被阻塞
-
會話A
檢查是否有被阻塞的RENAME
-
會話A
刪除tbl_old
和釋放寫鎖 -
會話B RENAME
成功 -
所有被阻塞的讀寫請求於新表
我是個憨憨,看到這個過程想吐槽,
鎖表操作是不是多此一舉?
該專案
issues
也有老哥問了這個問題:
Shawn001 commented on 25 Apr
hello
why can't
use
rename t1
to t1_del, t1_gho
to t1 directly,
are there
some problems?
其實是為了避免產生新的
binlog event
,如果不鎖,其他會話可能會繼續做
DML
。
附錄:
1)RENAME TABLE還是相對安全的,MySQL手冊裡也有描述:
If any errors occur during a RENAME TABLE, the statement fails and no changes are made.
2)UNLOCK TABLES後,被hang住的DML比RENAME插隊執行怎麼辦?
這個問題
gh-ost
開發者就是一句話告訴大家:
A blocked RENAME is always prioritized over a blocked INSERT/UPDATE/DELETE, no matter who came first
。大概意思就是無論
DML
還是
RENAME
先到,
RENAME
總是第一個先跑的,這樣就可以保證不會產生新的
binlog events
。
至於原因,google了一下,理由在這裡
cut-over參考如下
https://github.com/github/gh-ost/blob/master/doc/cut-over.md
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29773961/viewspace-2726607/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 大資料——Flink核心技術及原理大資料
- 詳解Redis核心資料結構和高效能原理分析(一)Redis資料結構
- Elasticsearch Lucene 資料寫入原理 | ES 核心篇Elasticsearch
- Redis和資料庫的資料一致性問題Redis資料庫
- 保證Redis和資料庫資料一致性的方法Redis資料庫
- Redis和MySQL如何保持資料一致性?RedisMySql
- Kubernetes 併發控制與資料一致性的實現原理
- Kubernetes併發控制與資料一致性的實現原理
- 如何保證mongodb和資料庫雙寫資料一致性?MongoDB資料庫
- 從 Oracle 日誌解析學習資料庫核心原理Oracle資料庫
- 如何保證MySQL和Redis資料一致性?MySqlRedis
- [Redis] 02-快取和資料庫資料一致性問題Redis快取資料庫
- Redis與資料庫資料一致性Redis資料庫
- MySQL資料一致性MySql
- R資料分析:資料清洗的思路和核心函式介紹函式
- 大資料分析的原理和潛力大資料
- Delta Lake 資料湖原理和實戰
- 如何保證快取和資料庫的一致性?快取資料庫
- 資料庫和快取的一致性如何保證資料庫快取
- 資料一致性(一) - 介面呼叫一致性
- Netty核心原理Netty
- springboot核心原理Spring Boot
- gh-ost的學習
- 初試GH-OST(轉)
- 談談資料一致性
- mysql資料一致性解析MySql
- 深入理解 Docker 核心原理:Namespace、Cgroups 和 RootfsDockernamespace
- 趣說 | 資料庫和快取如何保證一致性?資料庫快取
- 8張圖搞懂Redis和MySQL資料一致性問題RedisMySql
- 一文搞懂一致性hash的原理和實現
- 一文搞懂一致性 hash 的原理和實現
- 競拍系統設計和核心資料結構資料結構
- Socket 核心原理分享
- MySQL 線上DDL "gh-ost"MySql
- Spring Cloud Alibaba Sentinel 主要原理和核心類介紹SpringCloud
- 資料庫和快取雙寫一致性方案總結分析資料庫快取
- 針對靜默資料錯誤,如何採用DIX和DIF保證資料一致性?
- 面試常問:如何保證Redis快取和資料庫的資料一致性NRXW面試Redis快取資料庫