亿级订单系统分库分表技术方案

一、需求背景

随着公司业务的发展,订单系统的数据量和访问量日益增长,单库单表的架构已经无法满足我们的需求。主要面临以下问题:

  1. 数据量大:单一数据库存储所有订单数据,导致数据量过大,影响查询效率。
  2. 并发压力大:大量用户同时访问系统,产生高并发请求,对数据库造成较大压力。
  3. 扩展性差:当需要对订单表进行改动时,大量的数据造成表结构修改时间变长

二、业务现状

  1. 订单表有主键orderId和唯一索引orderNo,orderId依赖数据库自增,orderNo自定义生成并且后4位为类用户id,内部场景用orderId进行数据传递,外部场景用orderNo进行数据传递

解决方案:去除数据库自增,将orderId和orderNo进行合并,将自定义编号作为唯一主键,内外部场景全部采用orderNo进行数据传递

  1. 订单表超过10个索引,部分索引用于C端场景,部分索引用于B端场景

解决方案:将OLTP中的订单数据同步到OLAP数据库中;确保所有C端场景能够命中分库分表的分片键;将B端分析场景进行剥离,例如查询订单列表,在OLAP数据库中进行查询,再根据订单主键CRUD时,再走OLTP数据库

  1. 订单表中存在部分老数据,老orderNo没有采用后4位是类用户id,并且老orderNo中可能存在字母

解决方案:借助Flink将老订单编号数据进行清洗(数据清洗技术方案另行商议),统一采用现有订单编号规则

  1. 订单表存在一些拓展表,比如订单位置、订单申诉记录等,有些采用orderNo进行关联,有些采用orderId进行关联

解决方案:和第1点一样,统一采用主键进行关联,并且需要保证关联字段为分片键。如果有分库,最好将拓展表也进行分库分表,并且需要保证拓展表的分库分表规则和原表一致

  1. 订单表中字段过多,有计费、开票、退款等非核心数据

解决方案:考虑纵向拆分,可以考虑这几个方面:是否核心、更新是否频繁、字段长度是否过大

三、技术选型

1、分库分表和分布式数据库对比

理解普通数据库、分布式数据库、云原生数据库、云原生分布式数据库区别

  • 普通数据库:通常采用传统的垂直架构,由单一的数据库服务器提供数据存储和查询服务。
  • 分布式数据库:采用水平分片的方式,将数据分散到多个数据库服务器上,实现数据的高可用性和可扩展性。
  • 云原生数据库:基于云计算技术构建,具有容器化、微服务等特性,可以快速部署和扩展。
  • 云原生分布式数据库:结合了分布式数据库和云原生数据库的特点,实现高性能、高可用、可扩展的数据库服务。
产品名称PolarDB-分区表OceanBasePolarDB-XTIDB传统分库分表
开发团队阿里-阿里云团队阿里-蚂蚁团队阿里-阿里云团队PingCAP团队根据具体实现和需求可能有多个开发团队参与
类型云原生数据库分布式数据库云原生分布式数据库分布式数据库传统数据库
应用场景HTAP(行列混合)HTAP(行列混合)OLTP(行存索引)HTAP(行列混合)OLTP(行存)
产品优势一写多读、共享分布式存储、计算与存储分离、自动读写分离、高速链路互联、数据可靠性和强一致性、维护成本很低、支持列存索引可满足金融级容灾标准、水平扩展、支持多租户资源隔离、支持列存索引、支持空间索引支持数据强一致性、支持水平扩展、列存索引(灰度中)水平扩展、实时HTAP、行列混合灵活性强、不需要进行数据库迁移
产品劣势分区算法只能采用单一key、行存节点进行了分区,列存节点也会分区、分区表不支持空间类型成本比PolarDB贵一倍、涉及到数据库迁移成本较高、涉及到数据库迁移、列存索引还在灰度中、OLAP依赖并行计算、目前不支持mysql8.0不属于阿里云体系、运维成本较高、涉及到数据库迁移、不支持空间类型考虑分布式事务兼容性、需要集成中间件
分片原理推荐使用分区表代替分库分表基于分布式技术的分片、无共享架构实现数据的分散存储和处理。基于MySQL内核的分片、通过特定的存储计算分离架构实现数据的分散存储和处理。基于TiKV内核的分片、通过Raft协议实现数据的一致性复制和分散存储。根据业务需求和自定义的分片规则进行数据分散存储和处理;可以根据具体的实现方式采用不同的分片算法和策略。
文档地址PolarDBOceanBasePolarDB-XTIDBShardingSphere

2、分库分表方案对比

中间件名称ShardingSphere-JDBCShardingSphere-ProxyMycat
类型客户端分表数据库代理数据库代理
开发团队ApacheApacheMycat社区
优势轻量级、易于集成、支持多种数据源、提供分布式事务和读写分离功能、功能丰富、支持多种数据源、提供分布式事务、读写分离、分布式主键生成等功能、业务无侵入、支持异构语言开源免费、功能完善、支持多种数据源、提供分布式事务、读写分离、分布式序列等功能、适用于各种项目
劣势代码改造、集成分布式事务困难、配置较为繁琐性能损耗略高、需要单独部署维护社区支持有限、维护和更新不及时、性能损耗略高、需要单独部署维护
总结适用于对性能要求较高,代码侵入性可接受的OLTP应用支持异构语言,独立于应用程序部署,适用于 OLAP 应用以及对分片数据库进行管理和运维的场景。开源免费,但社区支持有限、维护和更新不及时。独立于应用程序部署,适用于 OLAP 应用以及对分片数据库进行管理和运维的场景。

四、唯一ID方案

1、数据库自增或者Redis自增

优点:最简单。 缺点:单点风险、单机性能瓶颈、暴露业务量

2、Snowflake算法

image.png
上图为二进制bit位,总长度为2^64次方,刚好Long可以存下。10进制字符长度为19位

优点:高性能高可用、易拓展
缺点:需要独立中心节点,时钟回拨可能造成ID重复、没有带业务标识、41位

3、Random算法

例如UUID

优点:简单
缺点:生成ID较长,有重复几率

4、美团分布式ID生成-Leaf

Leaf-snowflake:通过集群部署,自动剔除时钟回拨的节点,可以避免Snowflake时钟回拨ID重复的问题
Leaf-segment:在数据库自增方案上进行了改进,加了批量生成和本地缓存,解决了自增性能瓶颈和单节点风险

优点:高可用、解决了自增和Snowflake部分缺点
缺点:弱依赖ZooKeeper,需要独立部署Leaf系统

5、自定义业务ID

订单类型(1)+ 业务类型(1) + 时间戳yyMMddHHmmss(12) + 随机数(4) + 类用户id(后4位,不足用0代替)

优点:单机、有业务属性、有用户标识、有顺序性
缺点:过万QPS情况下容易出现ID冲突

五、分片键和分片策略选择

1、时间范围(Range)

适合数据有明显的时间属性,例如日志表、记录表、统计表等

优点:天然分片,好扩展,方便范围查询和排序操作,也可以方便数据归档
缺点:数据可能分布不均匀,易引起单机负载过大的问题

2、租户ID(List)

适合数据具有明显业务标识,例如Saas系统中的表按照租户ID、订单表按照订单类型、工单表按照工单类型

优点:可以根据具体的属性值进行分片,方便根据属性值进行查询和过滤操作
缺点:分片规则不好维护,可能产生数据倾斜,数据不好扩容

3、自定义业务ID(Hash)

常用于互联网C端场景,例如根据用户ID分片,可以轻松的根据用户ID查找用户所有数据。需要根据业务体量做好数据分片规划

优点:数据分布均匀,可以实现负载均衡
缺点:数据扩容困难,范围查询效率较低

六、最佳实践

结合上诉技术选型对比,并从数据迁移成本、可维护性考虑,最终决定采用ShardingSphere-JDBC分库分表方案

订单数据流转图.jpg

(一)改造点梳理:

1、数据库结构改造

  • 创建新订单表,将orderNo和orderId进行合并,统一采用自定义编号,自定义编号长度会超过long最大值,需要采用string类型
  • 将有orderId字段的相关表进行改造,统一通过自定义编号进行关联
  • 将订单表中多余字段进行剥离(待定

2、代码改造

  • 区分OLTP请求和OLAP请求,并从数据源进行隔离
  • 引入ShardingJDBC,并配置分片键、分库分表数据源、分布式事务代理
  • join查询改造,分表后的关联查询必须带有分片键

3、数据清洗

  • 对不符合规则的老订单号进行清洗,生成新订单号,并记录新老订单号关系
  • 对有关联orderNo的表进行清洗,将老订单号替换成新订单号
  • 对有关联orderId的表进行改造清洗,将orderNo注入到有orderId的关联表中

4、数据分片

  • 现在日订单高峰期20W,按照当前业务5倍进行规划,并预计10年订单量。日订单量100W,年订单量3.6亿,10年订单量36亿
  • 允许单表最大订单量约5000W,36亿/5000W = 72,为了满足一致性hash原则2^n,取64张表

5、分布式事务Seata接入

参考SpringCloud多数据源接入Seata和ShardingJDBC最佳实践

(二)实施步骤:

1、新建库、表结构

在新订单库中,新建各个表结构

2、批量生成新订单号

离线生成新订单号并记录新老订单号关系表order_new_relation(包含order_id、old_order_no,new_order_no)

3、新建Flink同步作业

old_order表当成源表、order_new_relation当成维表、new_order表当成结果表

同步订单表:

  • 如果有老订单编号,则去order_new_relation查找新订单编号
  • 根据订单表的order_no分片规则,确定在哪一张表,将订单表的order_no同步到新订单表的orderId中

同步订单拓展表:

  • 判断拓展表的主键是orderId还是orderNo,如果是orderId,则需要通过order_new_relation找到order_no,并根据order_no进行分表
  • 如果是orderNo,则判断是否是老orderNo,如果是,则去order_new_relation找到新order_no,并根据新order_no进行分表

六、总结

本方案结合一段时间内未来业务规模、运维成本、服务器成本等多种因素进行考虑,并分析了分区表、分布式数据库、分库分表的区别和优劣势。也简单介绍了一下分库分表需要考虑的唯一ID、分片键、分片算法等问题,并结合实际业务简单梳理了一下改造方案。本文采用停机迁移分库分表方案,如果想要不停机迁移,可以参考大众点评分阶段实施。
备注:如果只分表不分库也能满足需求的话,分区表其实也是一个不错的选择,不用引入其它第三方组件,mysql就原生支持,并且对开发比较友好。但是PolarDB分区表不支持多个分区键用同一个分区规则,并且PolarDB列存数据也会按照行存的进行分区,有一定的性能损耗。

七、参考资料

TIDB技术内幕-说存储

大众点评订单系统分库分表实践

美团-分布式ID生成系统Leaf介绍

SpringCloud多数据源接入Seata和ShardingJDBC最佳实践

  • 22
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL分库分表是一种常用的数据库分布式架构设计技术,用于解决大规模数据存储和查询的性能瓶颈问题。它将一个大的数据库拆分成多个小的数据库(分库),每个小数据库再将数据表按照某种规则拆分成多个小表(分表)。 分库分表的优势包括: 1. 横向扩展性:通过分库分表,可以将数据存储在多个物理服务器上,提高系统的并发处理能力和数据存储容量。 2. 提高查询性能:将数据拆分成多个小表后,每个小表的数据量减少,查询效率提高。 3. 负载均衡:通过对数据进行分散存储,可以将负载均衡到多个数据库节点上,避免单一节点的性能瓶颈。 4. 容灾备份:多个数据库节点可以实现数据的冗余备份,提高系统的可用性和可靠性。 在实际应用中,可以采用水平切分和垂直切分两种方式进行分库分表。水平切分是指按照某种规则将数据行划分到不同的数据库中,例如按照用户ID或地理位置进行划分;垂直切分是指按照数据列进行划分,将不同的列存储在不同的数据库中,例如将用户基本信息和用户关注信息存储在不同的数据库中。 分库分表也带来了一些挑战,例如跨库事务的处理、数据一致性、分布式索引等问题需要额外的处理。因此,在设计和实施分库分表方案时,需要综合考虑系统的需求、数据规模、负载情况以及应用程序的复杂度等因素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值