分布式系統(tǒng)題目答案(3)
8、MySQL 如何實現(xiàn) XA 規(guī)范?
說 XA 規(guī)范之前,需要先說明一下 MySQL 及 InnoDB 引擎中涉及到的日志:
重做日志(redo log),數(shù)據(jù)真正更改前,會先把相關操作寫入 redo 日志,遇到意外情況,待系統(tǒng)恢復后,可以根據(jù)日志繼續(xù)完成記錄中的更改操作。
回滾日志(undo log),記錄事務開始前數(shù)據(jù)的狀態(tài),更改在執(zhí)行一半時發(fā)生意外,就可以根據(jù)撤消日志恢復到更改之前的狀態(tài)。
二進制日志(binlog),MySQL sever 層維護的一種二進制日志,記錄了所有的 DDL 和 DML 語句,主要用來復制和恢復。
XA 規(guī)范是由 X/Open 組織提出的分布式事務規(guī)范,主要定義了事務協(xié)調者(Transaction Manager)和資源管理器(Resource Manager)之間的接口。
XA 事務由兩階段提交實現(xiàn):
Prepare 階段,Transaction Manager 向 Resource Manager 發(fā)送prepare 指令,Resource Manager 收到指令執(zhí)行操作,返回是否可以提交。
Commit 階段,Transaction Manager 收到 Resource Manager 返回結果,有不可提交或超時的情況,向 Resource Manager 發(fā)出回滾指令;若都可以提交,則發(fā)出提交指令。
MySQL 單機 XA 的實現(xiàn),binlog 作為協(xié)調者,在事務提交時,則需要將提交信息寫入二進制日志。
分布式的 XA 的實現(xiàn),通過代理層使用 START/ END / PREPARE / COMMIT 這些指令完成分布式事務。
9、TCC 事務模型的實現(xiàn)
目的是解決復雜業(yè)務中,跨表跨庫等大顆粒度資源鎖定的問題,基于業(yè)務層面的事務定義,鎖粒度完全由業(yè)務自己控制。
TCC 把事務運行過程分成 Try、Confirm/Cancel 兩個階段,每個階段的邏輯由業(yè)務代碼控制,避免長事務,獲取更高性能。
Try 階段:調用 Try 接口,嘗試執(zhí)行業(yè)務,完成所有業(yè)務檢查,預留業(yè)務資源
Confirm/Cancel階段:提交或取消,接口冪等、允許失敗重試。
TCC 主要把數(shù)據(jù)庫 XA 中的二階段提交,提升到微服務層面實現(xiàn),解決了跨服務的業(yè)務操作原子性問題。
TCC 的不足主要體現(xiàn)在對微服務的侵入性強,需要對業(yè)務系統(tǒng)進行改造,業(yè)務邏輯的每個分支都需要實現(xiàn) try、Confirm、Cancel 三個操作,并且 Confirm、Cancel 必須保證冪等。
實現(xiàn) TCC 模型的組件:
Seata TCC 模式、Tcc-transaction、ByteTCC、Spring-cloud-rest-tcc 等。
10、分布式鎖如何實現(xiàn)
常用三種實現(xiàn)方式:
1、基于數(shù)據(jù)庫的實現(xiàn),依賴數(shù)據(jù)庫的唯一性來實現(xiàn)資源鎖定-- 建表
CREATE TABLE `t_lock` (
`lock_name` varchar(64) NOT NULL DEFAULT '' COMMENT '鎖定的方法或者資源',
PRIMARY KEY (`lock_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 嘗試加鎖
insert into t_lock(lock_name) values ('lock_name');
執(zhí)行 insert 語句嘗試加鎖,插入成功代表加鎖成功,執(zhí)行完成,刪除記錄釋放鎖。
但基于數(shù)據(jù)庫實現(xiàn)的分布式鎖,存在很多問題,如:需要數(shù)據(jù)庫高可用防止單點故障、需要自行解決超時鎖釋放的問題、處理鎖可重入需要修改表結構加上線程唯一標識進行判斷、數(shù)據(jù)庫連接資源比較寶貴。所以生產環(huán)境一般不使用這種方式。
2、基于 Redis 實現(xiàn)
setnx + expire,設置緩存值 + 過期時間,由于兩個指令非原子操作,仍然可能存在 setnx 成功 expire 失敗的情況導致鎖不會釋放的情況。
Redis 2.8 新增了 SETEX 指令支持 setnx + expire 的原子操作,但如果過期時間設置過短,會導致鎖提前釋放。所以業(yè)務操作不要時間太長;set 的 value 給一個隨機數(shù),釋放時線程持有值與redis緩存值進行比對,相同才給予釋放;記錄上下游鏈路,異常情況進行追蹤、人工介入處理。
在 redis 集群環(huán)境下,數(shù)據(jù)同步是異步的,所以會存在加鎖數(shù)據(jù)未同步到其他節(jié)點,導致其他線程依然可以加鎖成功的情況。為解決這個問題,redis 作者推薦使用 Redlock 算法,Redisson 內置了對 Redlock 的實現(xiàn)。
3、基于 zookeeper 實現(xiàn)
利用 zk 支持臨時順序節(jié)點的特性實現(xiàn)分布式鎖。
當客戶端對某個方法加鎖時,在 ZooKeeper 中該方法對應的指定節(jié)點目錄下,生成一個唯一的臨時有序節(jié)點。
判斷是否獲取鎖,只需要判斷持有的節(jié)點是否是有序節(jié)點中序號最小的一個。
釋放鎖時,將這個臨時節(jié)點刪除。
客戶端 watch 持有節(jié)點的子節(jié)點變動,若有變動則激活獲取鎖的競爭。
實際開發(fā)中一般使用 Apache Curator 來快速實現(xiàn)分布式鎖。
