
“本文由社区用户 @阿七从第一视角讲述其团队重构图数据库的过程,首发于阿七公众号「浅谈架构」”
读过我公众号文章的同学都知道,我做过很多次重构,可以说是“重构钉子户”,但是这次,重构图数据库 OrientDB 为 NebulaGraph(https://www.nebula-graph.com.cn/),可以说是我做过最艰难的一次重构。
那这篇文章就来聊聊,图数据库重构之路。
总结来说: 业务不了解,技术栈不熟悉
tips: 大家思考一个问题,在业务和技术栈都不熟的情况下,如何做重构呢?
下面介绍一下本次重构技术方案
猎户座的图数据库 OrientDB 存在性能瓶颈和单点问题,需升级为 NebulaGraph。
老系统是用使用技术栈无法支持弹性伸缩,监控报警设施也不够完善。
具体的使用痛点后续我将会写一篇文章具体讲述下,本篇就不详细展开了。
注:既然业务都不熟悉,那我们都调研了哪些东西呢?
完成图数据库数据源 OrientDB 改造为 NebulaGraph,重构老系统统一技术栈为 Java,支持服务水平扩展。
我们采用了比较激进的方案:
注:这里就贴调研阶段画的图,图涉及业务,我这里就不列举了。

| 阶段一 | 阶段二 | 阶段三 | 阶段四 | 阶段五 | 阶段六 | 阶段七 |
|---|---|---|---|---|---|---|
| 0% | 1‰ | 1% | 10% | 20% | 50% | 100% |
| 同步双写, 流量回放采样对比,100% 通过、预计灰度 2 天 | 灰度 2 天 | 灰度 2 天 | 灰度 5 天、此阶段要压测 | 灰度 2 天 | 灰度 2 天 | - |
注:
题外话:其实重构,最重要的就是灰度方案,这个我在之前文章《浅谈这些年做过的千万级系统重构项目》也提到过。本次灰度方案设计比较完善,大家重点看阶段一、在灰度放量之前,我们用线上真实的流量去异步做数据对比,对比完全通过之后,再放量,本次对比阶段比预期长了很多(实际上用了 2 周时间,发现了很多问题)。
未命中灰度流程如下:
先调用老系统,再根据是否命中采样(采样比例配置 0% ~ 100%),命中采样会发送 MQ,再在新系统消费 MQ,请求新系统接口,于老系统接口返回数据进行 JSON 对比。对比不一致,发送企业微信通知,实时感知数据不一致,发现并解决问题。

反之亦然!!
改造前:
@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;
}
改造后:
定义灰度模块接口
public interface IGrayService {
/**
* 是否命中灰度 配置值 0 ~ 1000 true: 命中 false:未命中
*
* @param hashCode
* @return
*/
public boolean hit(Integer hashCode);
/**
* 是否取样 配置值 0 ~ 100
*
* @return
*/
public boolean hitSample();
/**
* 发送请求-响应数据
* @param requestDTO
*/
public void sendReqMsg(MessageRequestDTO requestDTO);
/**
* 根据
* @param methodKeyEnum
* @return
*/
public boolean hitSample(MethodKeyEnum methodKeyEnum);
}
接口改造如下, kgpCoreService 请求到 kgp-core 新服务,接口业务逻辑和 orion-x 保持一致、底层图数据库改为查询 NebulaGraph:
@Override
public MSubGraphReceive getSubGraph(MSubGraphSend subGraphSend) {
logger.info("-----start getSubGraph------(" + subGraphSend.toString() + ")");
long start = System.currentTimeMillis();
//1. 请求灰度
boolean hit = grayService.hit(HashUtils.getHashCode(subGraphSend));
MSubGraphReceive r;
if (hit) {
//2、命中灰度 走新流程
r = kgpCoreService.getSubGraph(subGraphSend); // 使用Dubbo调用新服务
} else {
//这里是原来的流程 使用的akka通信
r = (MSubGraphReceive) akkaClient.sendMessage(subGraphSend, 30);
}
long requestTime = System.currentTimeMillis() - start;
//3.采样命中了发送数据对比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;
}
投入人力: 开发 4 人,测试 1 人
主要事项及耗时如下:

略,这里不展开讲述。
经过团队 2 个月奋斗,目前已完成灰度阶段,收益如下
本次重构顺利完成,感谢本次一起重构的小伙伴,以及大数据、风控同学支持,同时也感谢 NebulaGraph社区(https://discuss.nebula-graph.com.cn/),我们遇到一些问题提问,也很快帮忙解答。
谢谢你读完本文 (///▽///)
如果你想尝鲜图数据库 NebulaGraph,记得去 GitHub 下载、使用、(з)-☆ star 它 -> GitHub;和其他的 NebulaGraph 用户一起交流图数据库技术和应用技能,留下「你的名片」一起玩耍呀~
2023 年 NebulaGraph 技术社区年度征文活动正在进行中,来这里领取华为 Meta 60 Pro、Switch 游戏机、小米扫地机器人等等礼品哟~ 活动链接:https://discuss.nebula-graph.com.cn/t/topic/13970
