• 数据库重构之路,以 OrientDB 到 NebulaGraph 为例


    “本文由社区用户 @阿七从第一视角讲述其团队重构图数据库的过程,首发于阿七公众号「浅谈架构」”

    原文出处:https://mp.weixin.qq.com/s/WIJNq-nuuAGtMjYo5rPLyg

    一、写在前面

    读过我公众号文章的同学都知道,我做过很多次重构,可以说是“重构钉子户”,但是这次,重构图数据库 OrientDB 为 NebulaGraph(https://www.nebula-graph.com.cn/),可以说是我做过最艰难的一次重构。

    那这篇文章就来聊聊,图数据库重构之路。

    二、难点在哪里

    1. 历史包袱重,原来使用 OrientDB 系统是2016年开始开发的,逻辑很复杂,历史背景完全不清楚。
    2. 业务不了解,我们是临时接的大数据需求,之前没有参与过这块业务,完全不了解。
    3. 技术栈不了解,图数据库是第一次接触(团队中也没有人了解),OrientDB 和 NebulaGraph 之前都没有接触过,原来老系统大部分代码是 Scala 语言写的,系统中使用的 HBase、Spark、Kafka,对于我们也比较陌生。
    4. 时间紧迫

    总结来说: 业务不了解,技术栈不熟悉

    tips: 大家思考一个问题,在业务和技术栈都不熟的情况下,如何做重构呢?

    三、技术方案

    下面介绍一下本次重构技术方案

    1、迁移背景

    猎户座的图数据库 OrientDB 存在性能瓶颈和单点问题,需升级为 NebulaGraph。

    老系统是用使用技术栈无法支持弹性伸缩,监控报警设施也不够完善。

    具体的使用痛点后续我将会写一篇文章具体讲述下,本篇就不详细展开了。

    2、调研事项

    注:既然业务都不熟悉,那我们都调研了哪些东西呢?

    1. 对外接口梳理:梳理系统所有对外接口,包括接口名、接口用途、请求量 QPS、平均耗时,调用方(服务和 IP);
    2. 老系统核心流程梳理:输出老系统整理架构图,重要的接口(大概 10 个)输出流程图;
    3. 环境梳理:涉及到的需要改造的项目有哪些,应用部署、MySQL、Redis、HBase 集群 IP,及目前线上部署分支整理;
    4. 触发场景:接口都是如何触发的,从业务使用场景出发,每个接口至少一个场景覆盖到,方便后期功能验证;
    5. 改造方案:可行性分析,针对每一个接口,如何改造(OrientDB 语句改为 NebulaGraph 查询语句),入图(写流程)如何改造;
    6. 新系统设计方案: 输出整理架构图,核心流程图。

    3、项目目标

    ​完成图数据库数据源 OrientDB 改造为 NebulaGraph,重构老系统统一技术栈为 Java,支持服务水平扩展。

    4、整体方案

    我们采用了比较激进的方案:

    1. 从调用接口入口出发,直接重写底层老系统,影响面可控;
    2. 一劳永逸,方便后期维护;
    3. 统一 Java 技术栈、接入公司统一服务框架,更利于监控及维护;
    4. 基础图数据库应用边界清晰,后续上层应用接入图数据库更简单。

    ​注:这里就贴调研阶段画的图,图涉及业务,我这里就不列举了。

    5、灰度方案

    灰度方案
    • 写请求:采用同步双写
    • 读请求:按流量从小到大陆续迁移、平滑过渡
    灰度计划
    阶段一阶段二阶段三阶段四阶段五阶段六阶段七
    0%1‰1%10%20%50%100%
    同步双写, 流量回放采样对比,100% 通过、预计灰度 2 天灰度 2 天灰度 2 天灰度 5 天、此阶段要压测灰度 2 天灰度 2 天-

    注:

    1. 配置中心开关控制,有问题随时切换,秒级恢复。
    2. 读接口遗漏无影响, 只有改到的才会影响。
    3. 使用参数 hash 值作为 key,确保同一参数多次请求结果一致、满足 abs(key) % 1000 < X ( 0< X < 1000, X 为动态配置 ) 即为命中灰度。

    题外话:其实重构,最重要的就是灰度方案,这个我在之前文章《浅谈这些年做过的千万级系统重构项目》也提到过。本次灰度方案设计比较完善,大家重点看阶段一、在灰度放量之前,我们用线上真实的流量去异步做数据对比,对比完全通过之后,再放量,本次对比阶段比预期长了很多(实际上用了 2 周时间,发现了很多问题)。

    6、数据对比方案

    未命中灰度

    未命中灰度流程如下:

    先调用老系统,再根据是否命中采样(采样比例配置 0% ~ 100%),命中采样会发送 MQ,再在新系统消费 MQ,请求新系统接口,于老系统接口返回数据进行 JSON 对比。对比不一致,发送企业微信通知,实时感知数据不一致,发现并解决问题。

    反之亦然!!

    7、数据迁移方案

    1. 全量(历史数据):写脚本全量迁移,上线期间产生不一致从 MQ 消费近 3 天数据
    2. 增量:同步双写(写的接口很少,写请求 QPS 不高)

    8、改造案例 - 以子图查询为例

    改造前:

    @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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    改造后:

    定义灰度模块接口

    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);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    接口改造如下, 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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    9、项目排期计划

    投入人力: 开发 4 人,测试 1 人

    主要事项及耗时如下:

    10、所需资源

    略,这里不展开讲述。

    四、重构收益

    经过团队 2 个月奋斗,目前已完成灰度阶段,收益如下

    1. NebulaGraph 本身支持分布式扩展,新系统服务支持弹性伸缩,整体支持性能水平扩展;
    2. 从压测结果看,接口性能提升很明显,可支撑请求远超预期;
    3. 接入公司统一监控、告警,更利于后期维护。
    五、总结

    本次重构顺利完成,感谢本次一起重构的小伙伴,以及大数据、风控同学支持,同时也感谢 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

  • 相关阅读:
    003:如何画出成交量的柱状图
    Vue2.0 —— Vue.set(vm.$set) 源码探秘
    SCI/SSCI选刊神器之——Elsevier Journal Finder
    解密prompt系列27. LLM对齐经验之如何降低通用能力损失
    面试突击62:group by 有哪些注意事项?
    Docker 运行percona tokudb 引擎
    斯坦福机器学习 Lecture2 (假设函数、参数、样本等等术语,还有批量梯度下降法、随机梯度下降法 SGD 以及它们的相关推导,还有正态方程)
    研发效能认证学员作品:持续集成与持续部署:软件高质量的关键丨IDCF
    GIT 首次提交
    Tomcat监控指标判断应用“死了“吗
  • 原文地址:https://blog.csdn.net/weixin_44324814/article/details/132832301