• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

千万级订单j机构查询优化

武飞扬头像
迪壳
帮助72

项目背景及优化效果

公司运行了一套ERP系统 , 已经上线4年多 , 交付给十多个部门数百人使用 .由于项目迭代中需求缺少整理,代码也疏于优化 , 导致订单维度的信息有多达四五百项, 分散在四个有着上百字段千万级宽表上 , 查询时还要再额外关联七八张小表 , 因此查询效率非常低 , 经过优化后速度得到明显提升,前端交互也更便捷,达到了预期效果,故抛砖引玉分享下自己的一些心得

早期的一些优化尝试

使用从库进行查询

最先想到的自然是增加MySql从库规模, 进行读写分离 . 但由于底层结构并未发生变化 , 只能缓解并发查询时主库的压力,并不能提高单次查询时的速度, 因此增加从库只能作为一种提高数据库并发的方式

订单表水平拆分

之后的一段时间在网上看了不少分库分表相关的文章 , 也想着依葫芦画瓢对系统进行改造 .当时的想法是订单主表保持不变,把数据复制一份并根据日期进行分区,存储在原数据库的不同分区表上 , 但仍旧存在几个问题:

  • 只对其中一张订单主表进行了分区 , 其他表因为没有该时间字段从而无法分许,导致实际的效果并不十分理想

  • join表的数量并未减少 , 查询条件依旧散落在几个不同的表上

  • 订单的更新操作分布在三四个系统的几十个方法中. 并没有一个统一的更新入口,从而导致分区表无法实时获取源订单表的数据更新,

使用云DB替换MySQL

公司的云服务厂商在了解我们查询的难题后,主动邀请我们尝试云DB . 当时看了下云DB的宣传页 , 感觉性能还是很强悍的 , 不过在实测中发现提升非常有限 . 之后了解了一些云DB的架构 , 了解到它主要是通过分布式架构解决单机TPS以及数据体积的瓶颈 , 所以宣称的性能提升也主要针对一些简单的增删改查 , 和我目前的场景还是有些出入 . 不过经过这次折腾也明白一个道理 , 如果不彻底改造查询的设计 , 仅仅通过提高数据库的性能或类型已经无法引起查询效率的质变 .

使用binlog实时拼接订单数据

初识canal与Debezium

之后的一段时间,查询优化的进度都处于停滞状态,但也在满满的积累一些数据库方面的知识,并先后发现了canal和Debezium这两款中间件, 也了解了binlog等相关知识 .作为binlog parser他们的功能和运行方式十分相似 ,通过把自己伪装成一个MySQL从库 , 与主库建立链接 , 之后就可以从主库上实时获取binlog , 而binlog则包含着数据变更的记录 . 当时想借助binlog完成流程通知 , 有点类似于Spring 中AOP的用法 .之后突然意识到既然我能捕捉到数据库的变化, 那能否把几张表join之后的结果集经过加工后放入到另一个数据库中,加速查询?

设计新的查询架构

此时已经明确了优化方向 , 原有的数据和结构不做调整 , 先通过Debezium监听数据库 , 把订单的变化投送到Kafka中, 之后解析Kafka消息 , 把订单信息实时聚合并同步到另一个数据库中 . 使四张大表组合成一张单表 , 同时在聚合中剔除冗余字段以及非查询相关字段 , 最后拼装的单表只有五六十个字段 , 大大缩小了表体积.

以上这些步骤,全都是我个人进行的一些探索,在之后我才发现这其实是一个很典型的ETL 流程,它有三个主要步骤:

  • Extract: 对应于我用Debezium监听MySql中的数据,投送到Kafka的过程

  • Transform: 对应于我从Kafka接受数据 ,并把数据进行清洗转换的过程.

  • Load: 对应于我把处理之后的结果集放入到新的数据库中.

现在已经有了订单数据 ,但由于新的数据库并没有公司名称,用户名称等基础信息 ,所以需要解决字典名称转换的问题 . 该问题的处理方案与订单类似 , 即需要使用Debezium监听这些字典表 , 不同的是这些映射关系存储在java内存中 ,当前端需要显示相应信息时,利用注解和反射在java层完成字典key到value的翻译 . 此时的架构如下:

订单表的水平拆分

首先,最容易想到的是根据时间,以月为单位将订单表进行水平拆分.之后我准备用订单所属公司进行二次拆分,然而这时候却遇到了一些麻烦 .这里也简单介绍下我们系统 , 我们系统是一个多租户的平台 ,一般情况下此时可以使用取模或者范围进行订单的水平拆分 , 但是问题就出在某些用户的权限是跨租户的 , 那就意味着他们查询订单时需要检索多个分区表 ,这样的效率会比较低 ,并且代码也比较复杂, 一时间有点陷入僵局.

功夫不负有心人, 我突然想到能否从业务上优化这种场景 , 经过一番调查和统计后的确找到了一个比较好的拆分方法. 我根据公司的实际业务情况把他们分为三大类 , 对于使用频率最高的一线用户而言他们基本上不会有跨大类查询的权限.而对于二线的财务等人员, 他们虽然有跨大类的权限 ,但是由于业务情况 ,单次查询时其实只关注单个大类下的信息.因此后端设计了三个分区表 ,前端部分也有些调整 .当用户有多个大类权限时,新增一个下拉框, 并让用户手动选择查询哪个大类.此时后端架构如下:

在进行 ETL和数仓搭建的时候 ,数据的一致性,实时性或查询方式与直接使用原生数据源可能有所差异 , 这时候需要做一些取舍.可以考虑实际的场景,根据业务方的需求灵活做出判断.比如在这一环节 ,我在前端针对部分用户增加了一个下拉框 ,换取了公司维度的水平拆分 , 提高了查询速度.

订单表的垂直拆分

在之前的设计中为了提高查询速度 , 只筛选了一些最基础的字段 , 但这样并不能完全满足业务方的需求 , 但如果把所有的字段信息都加进去 , 又会降低查询效率. 思考了一番之后 , 我决定再进行一次垂直拆分, 将查询字段放入到一张表 , 而一些仅作展示的字段则放置到另外一张表 . 最终的结构如下:

总结

此时系统的后端框架已经设计完成 , 我就按照上述步骤对订单信息进行了分表 ,之后查询效率也得到了非常大的提升. 当然除了后端,前端和数据库方面也进行了比较多的优化, 有空的时候也会记录下当时的步骤.

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanfekhc
系列文章
更多 icon
同类精品
更多 icon
继续加载