客戶端分片到Proxy分片,如絲般順滑的平穩(wěn)遷移
點擊上方藍色字體,選擇“設為星標”

背景
隨著訂單數(shù)量的增多,以及大促時需要扛住比平時多N倍的流量,單庫單表的瓶頸日益顯現(xiàn)。
需要對數(shù)據(jù)庫進行水平拆分,目前訂單使用的是客戶端分片的方式進行拆分,采用Sharding-Jdbc框架實現(xiàn)。
Sharding-Jdbc后改名為ShardingSphere,提供client和proxy方式進行數(shù)據(jù)庫的拆分,目前訂單使用的是client方式。
client方式的優(yōu)勢是實現(xiàn)簡單,只需要通過簡單的配置即可完成拆分操作。在本地通過分片進行計算,得到真實的庫和表進行路由,性能相對較高。不依賴于三方,沒有單點故障。
client方式的劣勢是每個項目都要去管理分片,讀寫分離等信息,沒辦法統(tǒng)一進行管理。
當需要升級的時候只能所有項目都進行升級,沒辦法統(tǒng)一升級。最難的點在于需要推動各個業(yè)務域進行升級,升級的周期較長,對業(yè)務方的壓力也比較大。
相對于proxy方式,client方式連接數(shù)占用的比較多,一個數(shù)據(jù)源10個連接,部署10個實例就是100個連接。
proxy方式指的是部署一個獨立的服務,這個服務會實現(xiàn)Mysql協(xié)議,應用中只需要連接這個獨立的proxy服務,把它當做一個完整的獨立的數(shù)據(jù)庫使用即可。
分庫分表等規(guī)則全部由這個proxy去管理,對應用透明。
彩虹橋DAL是得物基礎架構組研發(fā)的proxy方式的代理服務,有了彩虹橋就可以統(tǒng)一管理所有數(shù)據(jù)庫,可以對數(shù)據(jù)庫操作做限流保護,可以做壓測時的數(shù)據(jù)自動進入影子庫,可以監(jiān)控慢SQL等等。
接入方案
直接修改老數(shù)據(jù)源-改成連接彩虹橋
最簡單,最快速的方式就是直接將所有老的數(shù)據(jù)源改成新的代理數(shù)據(jù)源,然后重新發(fā)布就可以了。這個方案確實快,但是不夠好,不夠完美,不夠穩(wěn)定,為啥?
這其實跟改接口是一樣的道理,當某天加了個新的需求,你會發(fā)現(xiàn)直接在老接口的基礎上改一下就完成了。改完后測試發(fā)現(xiàn)當前沒問題,但是老版本的產(chǎn)品還在用,然后兼容出問題了。這種場景下如果你是對接口進行升級,之前是v1,現(xiàn)在改成v2,老的邏輯完全不動,就完美了呀。
如果我們直接改成最新的方式,那么如何平滑的發(fā)布上線呢?
全部發(fā)布,萬一彩虹橋那邊配置有問題,或者有其他的問題導致不可用,你這不就尷尬了么,當然也可以回滾歷史版本。
另一種方式就是灰度發(fā)布一臺機器,進行測試,沒問題后全部發(fā)布,有問題就不發(fā),其實也不錯。就怕過了一段時間,萬一彩虹橋有Bug, 我們還是得回滾到接入彩虹橋之前的版本。
新增一套數(shù)據(jù)源 - 支持開關切換到新數(shù)據(jù)源,下線老數(shù)據(jù)源
新增數(shù)據(jù)源,支持開關切換就跟升級API版本是一樣的邏輯。保持老邏輯不變,上線時還是走老的數(shù)據(jù)源,然后通過開關動態(tài)切換到新的數(shù)據(jù)源,完成上線動作。
當然這邊也會出現(xiàn)上面提到的問題,比如彩虹橋不可用之類的情況,也可以通過灰度配置的方式來測試。將開關配置灰度到某個節(jié)點上,只切這個節(jié)點的數(shù)據(jù)源,進行測試,沒問題后可以擴大范圍。有問題開關一關就還原到之前的邏輯了,無影響。
如果用了一段時間,彩虹橋有Bug的話同樣改下開關的配置即可回滾,也不用重新發(fā)布項目。
此方案不足的點就是會占用一分部數(shù)據(jù)庫的連接,因為老的數(shù)據(jù)源還存在。此時需要改代碼去掉老的數(shù)據(jù)源,就能釋放了。如果后面還要去改代碼,那還能叫完美的方案么?
這點我也考慮到了,還是通過配置開關來關閉老的數(shù)據(jù)源,但是這個操作得重啟服務,重啟后就只有一套數(shù)據(jù)源了。(請確保以后只用彩虹橋的方式才用此開關)
實現(xiàn)步驟
配置2套新數(shù)據(jù)源,連接彩虹橋的地址,訂單的庫分為老訂單庫和新的分庫分表庫。
新增2個動態(tài)數(shù)據(jù)源,用于開關動態(tài)切換,使用AbstractRoutingDataSource管理。
老數(shù)據(jù)源通過@ConditionalOnProperty來控制是否加載,默認加載,用于后期下線老數(shù)據(jù)源。
讀寫分離兼容client和proxy方式。
分片算法重寫,之前用的Sharding-Jdbc3.X版本,新的彩虹橋基于5.X版本深度定制開發(fā),在自定義算法這塊有變化,目前彩虹橋的分片算法全部在彩虹橋的擴展包中,不在訂單里面。
注意事項
select last_insert_id()不支持
在insert中通過select last_insert_id()實時返回當前插入的自增ID場景需要修改,目前訂單中就一個地方用到了,而且上層其實沒消費這個ID, 如果后面有其他場景需要獲取剛插入的ID可以手動提前獲取分布式ID,然后再用這個ID存到表中。
強制走從節(jié)點查詢
老的讀寫分離用的Sharding-Jdbc做的,正常情況下默認查詢走從節(jié)點,其他走主節(jié)點。而現(xiàn)在訂單里面默認都是走主節(jié)點,如果需要強制走從節(jié)點需要使用HintManagerHolder.clear() 清空Hint信息來實現(xiàn)。
猜測最開始沒有從節(jié)點,業(yè)務都走主節(jié)點。后面加了從節(jié)點,如果用默認的方式查詢都會走從節(jié)點,但是在某些業(yè)務場景中一致性比較高,必須走主節(jié)點查詢,相對于標記出哪些查詢走從節(jié)點來說,默認全部走主節(jié)點更穩(wěn)。然后根據(jù)業(yè)務場景再標記是否要走從節(jié)點,這樣幾乎不會影響老的邏輯。
接入彩虹橋后,默認是彩虹橋和Sharding-Jdbc兩套數(shù)據(jù)源共存的,所以在HintManagerHolder.clear() 這塊也需要做兼容,同時支持彩虹橋和Sharding-Jdbc方式的強制走從節(jié)點。
第一步:兼容老代碼
將所有使用HintManagerHolder.clear()改成SqlHintUtils.clear()。
public class SqlHintUtils {
public static void clear() {
// ShardingJdbc
HintManagerHolder.clear();
// 彩虹橋DAL
BifrostContext.clearNotAutoClearPrimary();
}
}
第二步:新增注解強制走從節(jié)點,比代碼更優(yōu)雅
以后有新的查詢需要走從節(jié)點就在Dao方法上增加@ForceSlave注解即可,此注解只能作用于Dao方法上,加在其它層無效。如果Dao方法上加了注解,那么方法內(nèi)所有的查詢操作都將走從節(jié)點。
老的clear相關的代碼其實可以用注解代替,但為了保險起見還是不改變原有的方式,新的可以用注解的方式。
參考文檔 shardingsphere:https://shardingsphere.apache.org/document/current/cn/overview/
后臺回復 學習資料 領取學習視頻
如有收獲,點個在看,誠摯感謝
