圖數(shù)據(jù)庫(kù)系統(tǒng)重構(gòu)之路:從OrientDB遷移到NebulaGraph 真實(shí)案例分享
一、寫(xiě)在前面
讀過(guò)我公眾號(hào)文章的同學(xué)都知道,我做過(guò)很多次重構(gòu),可以說(shuō)是“重構(gòu)釘子戶”,但是這次,重構(gòu)圖數(shù)據(jù)庫(kù)OrientDB為Nebula Graph(https://www.nebula-graph.io/),可以說(shuō)是我做過(guò)最艱難的一次重構(gòu),那這篇文章就來(lái)聊聊,圖數(shù)據(jù)庫(kù)重構(gòu)之路。
二、難點(diǎn)在哪里
1、歷史包袱重,原來(lái)使用OrientDB系統(tǒng)是2016年開(kāi)始開(kāi)發(fā)的,邏輯很復(fù)雜,歷史背景完全不清楚。
2、業(yè)務(wù)不了解,我們是臨時(shí)接的大數(shù)據(jù)需求,之前沒(méi)有參與過(guò)這塊業(yè)務(wù),完全不了解。
3、技術(shù)棧不了解,圖數(shù)據(jù)庫(kù)是第一次接觸(團(tuán)隊(duì)中也沒(méi)有人了解),OrientDB和Nebula之前都沒(méi)有接觸過(guò),原來(lái)老系統(tǒng)大部分代碼是Scala語(yǔ)言寫(xiě)的,系統(tǒng)中使用的Hbase,Spark,Kafka對(duì)于我們也比較陌生。
4、時(shí)間緊迫
總結(jié)來(lái)說(shuō): 業(yè)務(wù)不了解,技術(shù)棧不熟悉!
tips: 大家思考一個(gè)問(wèn)題,在業(yè)務(wù)和技術(shù)棧都不熟的情況下,如何做重構(gòu)呢?
三、技術(shù)方案
下面介紹一下本次重構(gòu)技術(shù)方案
1、背景
獵戶座的圖數(shù)據(jù)庫(kù)OrientDB存在性能瓶頸和單點(diǎn)問(wèn)題,需升級(jí)為Nebula。
老系統(tǒng)使用技術(shù)棧無(wú)法支持彈性伸縮,監(jiān)控報(bào)警設(shè)施也不夠完善。
2、調(diào)研事項(xiàng)
注: 既然業(yè)務(wù)都不熟悉,那我們都調(diào)研了哪些東西呢?
1)、對(duì)外接口梳理: 梳理系統(tǒng)所有對(duì)外接口,包括接口名,接口用途,請(qǐng)求量(QPS),平均耗時(shí),調(diào)用方(服務(wù)和IP)
2)、老系統(tǒng)核心流程梳理: 輸出老系統(tǒng)整理架構(gòu)圖,重要的接口(大概10個(gè))輸出流程圖
3)、環(huán)境梳理: 涉及到的需要改造的項(xiàng)目有哪些 , 應(yīng)用部署、Mysql,Redis,Hbase集群IP,及目前線上部署分支整理
4)、觸發(fā)場(chǎng)景: 接口都是如何觸發(fā)的,從業(yè)務(wù)使用場(chǎng)景出發(fā),每個(gè)接口至少一個(gè)場(chǎng)景覆蓋到,方便后期功能驗(yàn)證
5)、改造方案: 可行性分析,針對(duì)每一個(gè)接口,如何改造(OrientDB語(yǔ)句改為Nebula查詢語(yǔ)句),入圖(寫(xiě)流程)如何改造
6)、新系統(tǒng)設(shè)計(jì)方案: 輸出整理架構(gòu)圖,核心流程圖
3、項(xiàng)目目標(biāo)
完成圖數(shù)據(jù)庫(kù)數(shù)據(jù)源 OrientDB改造為Nebula,重構(gòu)老系統(tǒng)統(tǒng)一技術(shù)棧為Java,支持服務(wù)水平擴(kuò)展。
4、整體方案
我們采用了比較激進(jìn)的方案:
1、從調(diào)用接口入口出發(fā),直接重寫(xiě)底層老系統(tǒng),影響面可控
2、一勞永逸,方便后期維護(hù)
3、統(tǒng)一Java技術(shù)棧、接入公司統(tǒng)一服務(wù)框架,更利于監(jiān)控及維護(hù)
4、基礎(chǔ)圖數(shù)據(jù)庫(kù)應(yīng)用邊界清晰,后續(xù)上層應(yīng)用接入圖數(shù)據(jù)庫(kù)更簡(jiǎn)單
注:這里就貼調(diào)研階段畫(huà)的圖,圖涉及業(yè)務(wù),我這里就不列舉了。
5、灰度方案
** 1) 灰度方案**
寫(xiě)請(qǐng)求:采用同步雙寫(xiě)
讀請(qǐng)求:按流量從小到大陸續(xù)遷移、平滑過(guò)渡
** 2) 灰度計(jì)劃**
| 階段一 | 階段二 | 階段三 | 階段四 | 階段五 | 階段六 | 階段七 |
| 0% | 1‰ | 1% | 10% | 20% | 50% | 100% |
| 同步雙寫(xiě), 流量回放采樣對(duì)比,100%通過(guò)、預(yù)計(jì)灰度2天 | 灰度2天 | 灰度2天 | 灰度5天、此階段要壓測(cè) | 灰度2天 | 灰度2天 | - |
注:
-
1. 配置中心開(kāi)關(guān)控制,有問(wèn)題隨時(shí)切換,秒級(jí)恢復(fù)。
-
2. 讀接口遺漏無(wú)影響, 只有改到的才會(huì)影響。
-
3. 使用參數(shù) hash值作為key,確保同一參數(shù)多次請(qǐng)求結(jié)果一致、滿足 abs(key) % 1000 < X ( 0< X < 1000, X為動(dòng)態(tài)配置 ) 即為命中灰度。
題外話: 其實(shí)重構(gòu),最重要的就是灰度方案,這個(gè)我在之前文章也提到過(guò),本次灰度方案設(shè)計(jì)比較完善,大家重點(diǎn)看階段一、在灰度放量之前,我們用線上真實(shí)的流量去異步做數(shù)據(jù)對(duì)比,對(duì)比完全通過(guò)之后,再放量,本次數(shù)據(jù)對(duì)比階段比預(yù)期長(zhǎng)了一些(實(shí)際上用了2周時(shí)間,發(fā)現(xiàn)了很多隱藏問(wèn)題)。
6、數(shù)據(jù)對(duì)比方案
1) 未命中灰度流程如下:
先調(diào)用老系統(tǒng),再根據(jù)是否命中采樣(采樣比例配置 0% ~ 100% ),命中采樣會(huì)發(fā)送MQ,再在新系統(tǒng)消費(fèi)MQ,請(qǐng)求新系統(tǒng)接口,于老系統(tǒng)接口返回?cái)?shù)據(jù)進(jìn)行json對(duì)比,對(duì)比不一致發(fā)送企業(yè)微信通知,實(shí)時(shí)感知數(shù)據(jù)不一致,發(fā)現(xiàn)并解決問(wèn)題。
img
反之亦然!!
7、數(shù)據(jù)遷移方案
1)、 全量(歷史數(shù)據(jù)):腳本全量遷移,上線期間產(chǎn)生不一致從MQ消費(fèi)近3天數(shù)據(jù)
2)、增量:同步雙寫(xiě)(寫(xiě)的接口很少,寫(xiě)請(qǐng)求QPS不高)
8、改造案例 - 以子圖查詢?yōu)槔?/h3>
1)改造前
@Override
public MSubGraphReceive getSubGraph(MSubGraphSend subGraphSend) {
logger.info("-----start getSubGraph------(" + subGraphSend.toString() + ")");
MSubGraphReceive r = (MSubGraphReceive) akkaClient.sendMessage(subGraphSend, 30);
logger.info("-----end getSubGraph:");
return r;
}
2)改造后
定義灰度模塊接口
public interface IGrayService {
/**
* 是否命中灰度 配置值 0 ~ 1000 true: 命中 false:未命中
*
* @param hashCode
* @return
*/
public boolean hit(Integer hashCode);
/**
* 是否取樣 配置值 0 ~ 100
*
* @return
*/
public boolean hitSample();
/**
* 發(fā)送請(qǐng)求-響應(yīng)數(shù)據(jù)
* @param requestDTO
*/
public void sendReqMsg(MessageRequestDTO requestDTO);
/**
* 根據(jù)
* @param methodKeyEnum
* @return
*/
public boolean hitSample(MethodKeyEnum methodKeyEnum);
}
接口改造如下, newCoreService請(qǐng)求到new-core新服務(wù),接口業(yè)務(wù)邏輯和老系統(tǒng)接口保持一致、底層圖數(shù)據(jù)庫(kù)改為查詢Nebula
@Override
public MSubGraphReceive getSubGraph(MSubGraphSend subGraphSend) {
logger.info("-----start getSubGraph------(" + subGraphSend.toString() + ")");
long start = System.currentTimeMillis();
//1. 請(qǐng)求灰度
boolean hit = grayService.hit(HashUtils.getHashCode(subGraphSend));
MSubGraphReceive r;
if (hit) {
//2、命中灰度 走新流程
r = newCoreService.getSubGraph(subGraphSend); // 使用Dubbo調(diào)用新服務(wù)
} else {
//這里是原來(lái)的流程 使用的akka通信
r = (MSubGraphReceive) akkaClient.sendMessage(subGraphSend, 30);
}
long requestTime = System.currentTimeMillis() - start;
//3.采樣命中了發(fā)送數(shù)據(jù)對(duì)比MQ
if (grayService.hitSample(MethodKeyEnum.getSubGraph_subGraphSend)) {
MessageRequestDTO requestDTO = new MessageRequestDTO.Builder()
.req(JSON.toJSONString(subGraphSend))
.res(JSON.toJSONString(r))
.requestTime(requestTime)
.methodKey(MethodKeyEnum.getSubGraph_subGraphSend)
.isGray(hit).build();
grayService.sendReqMsg(requestDTO);
}
logger.info("-----end getSubGraph: {} ms", requestTime);
return r;
}
9、項(xiàng)目排期計(jì)劃
投入人力: 開(kāi)發(fā)4人,測(cè)試1人
主要事項(xiàng)及耗時(shí)如下:
| 方案設(shè)計(jì)階段 | 開(kāi)發(fā)階段 | 測(cè)試階段 | 灰度階段 |
| 1、流程梳理 2、畫(huà)流程圖、整理架構(gòu)圖 3、方案設(shè)計(jì) |
1、新服務(wù)項(xiàng)目搭建,Nebula操作類ORM框架封裝 2、接口改造(10多個(gè)接口改造) 3、MQ消費(fèi)改造 4、數(shù)據(jù)對(duì)比工具開(kāi)發(fā)(含企微通知) 5、數(shù)據(jù)遷移腳本開(kāi)發(fā) 6、接口聯(lián)調(diào) 7、代碼組內(nèi)CR |
1、功能測(cè)試 2、數(shù)據(jù)對(duì)比 3、100%流量老系統(tǒng)回歸測(cè)試 4、100%流量新系統(tǒng)回歸測(cè)試 5、生產(chǎn)數(shù)據(jù)遷移 |
1、分7個(gè)階段灰度,平滑過(guò)渡 2、生產(chǎn)數(shù)據(jù)實(shí)時(shí)對(duì)比 3、監(jiān)控&報(bào)警設(shè)施完善(這個(gè)在壓測(cè)之前完成,方案壓測(cè)的時(shí)候觀測(cè)指標(biāo)) 4、壓測(cè)(10%流量壓測(cè)) 5、數(shù)據(jù)備份與恢復(fù)演練(采用nebula快照備份)、擴(kuò)容演練 |
| 耗時(shí)1周 | 耗時(shí)3周 | 耗時(shí)2周 | |
10、所需資源
3臺(tái)Nebula機(jī)器 ,配置: 8核64G,2T SSD硬盤(pán)
6臺(tái)docker服務(wù),配置: 2核4G
四、重構(gòu)收益
經(jīng)過(guò)團(tuán)隊(duì)2個(gè)月奮斗,目前已完成灰度階段,收益如下
1、Nebula本身支持分布式擴(kuò)展,新系統(tǒng)服務(wù)支持彈性伸縮,整體支持性能水平擴(kuò)展
2、從壓測(cè)結(jié)果看,接口性能提升很明顯,可支撐請(qǐng)求遠(yuǎn)超預(yù)期
3、接入公司統(tǒng)一監(jiān)控、告警,更利于后期維護(hù)
五、總結(jié)
本次重構(gòu)順利完成,感謝本次一起重構(gòu)的小伙伴,以及大數(shù)據(jù)、風(fēng)控同學(xué)支持,同時(shí)也感謝Nebula社區(qū)(https://discuss.nebula-graph.com.cn/) ,我們遇到一些問(wèn)題提問(wèn),也很快幫忙解答。
歡迎關(guān)注微信公眾號(hào)“淺談架構(gòu)”,不定期分享原創(chuàng)技術(shù)文章。

