本篇将以“订单中心”为例,介绍“多key”类业务,随着数据量的逐步增大,数据库性能显著降低,数据库水平切分相关的架构实践。

什么是“多key”类业务

所谓的“多key”,是指一条元数据中,有多个属性上存在前台在线查询需求。

订单中心业务分析

订单中心是一个非常常见的“多key”业务,主要提供订单的查询与修改的服务,其核心元数据为:

Order(oid, buyer_uid, seller_uid, time, money, detail…);

其中:

  • oid为订单ID,主键
  • buyer_uid为买家uid
  • seller_uid为卖家uid
  • time, money, detail, …等为订单属性

数据库设计上,一般来说在业务初期,单库单表就能够搞定这个需求,典型的架构设计为:

  • order-center:订单中心服务,对调用者提供友好的RPC接口。
  • order-db:对订单进行数据存储。

随着订单量的越来越大,数据库需要进行水平切分,由于存在多个key上的查询需求,用哪个字段进行切分,成了需要解决的关键技术问题:

  • 如果用 oid 来切分,buyer_uid 和seller_uid 上的查询则需要遍历多库。
  • 如果用buyer_uid 或seller_uid 来切分,其他属性上的查询则需要遍历多库。

总之,很难有一个完全之策,在展开技术方案之前,先一起梳理梳理查询需求。

订单中心数据查询需求分析

  • 还是那句话,任何脱离业务的架构设计都是耍流氓,在进行架构讨论之前,首先要对业务进行简要分析,看看表结构上有哪些查询需求。

根据业务经验,订单中心往往有以下几类业务需求:

(1)用户侧,前台访问,最典型的有三类需求

订单实体查询:通过oid查询订单实体,90%都是这种需求。

用户订单列表查询:通过buyer_id分页查询用户历史订单列表,9%流量属于这种需求。

商家订单列表查询:通过seller_uid分页查询商家历史订单列表,1%流量属于这类需求。

  • 前台访问的特点是:吞吐量大,服务要求高可用,对一致性要求较高。其中商家对一致性要求较低,可以接受一定程度的延迟。

(2)运营侧,后台访问。

根据产品、运营需求,访问模式各异:

按照时间,架构,商品和详情来进行查询

  • 后台访问的特点:运营侧的查询基本上是批量的分页查询,访问量低,对可用性一致性的要求不高,允许秒甚至十秒级别的查询延迟。

订单中心数据查询需求解决方案-运营侧

后台运营侧的查询需求各异,基本是批量的分页查询,计算量和返回数据量较大,比较消耗数据库性能。

如果前台业务和后台业务公用一批服务和一个数据库,有可能导致,由于后台的“少数几个请求”的“批量查询”的“低效”访问,导致数据库的cpu偶尔瞬时100%,影响前台正常用户的访问(例如,订单查询超时)。

前台与后台访问的查询需求不同,对系统的要求也不一样,故应该两者解耦,实施“前台与后台分离”的架构设计

前台:业务架构不变,站点访问,服务分层,数据库水平切分。

后台:业务需求则抽取独立的 web/service/db 来支持,解除系统之间的耦合,对于“业务复杂”、“并发量低”、“无需高可用”“能接受一定延时”的后台业务:

  1. 可以去掉 service 层,在运营后台 web 层通过 dao 直接访问数据层。
  2. 可以不需要反向代理,不需要集群冗余。
  3. 可以通过 MQ 或者线下异步同步数据,牺牲一些数据的实时性
  4. 可以使用更契合大量数据允许接受更高延时的“索引外置”或者“HIVE”的设计方案。

订单中心数据查询需求解决方案-用户侧

明确了订单中心的访问需求后,问题转化为,前台的oid,buyer_id,seller_id如何来进行数据库的水平切分呢?

多个维度的查询较为复杂,对于复杂系统设计,可以逐步简化。

需要同时满足以下条件:

1.根据buyer_uid%n,可以定位到数据库

2.根据oid%n,可以定位到数据库

3.根据seller_uid%n,可以定位到数据库

以上业务是一个

  • 1:N(1个买家:N个订单)
  • N:N(1个买家:N个卖家, 1个卖家:N个买家)的业务场景,
  • 对于“多对多”的业务,水平切分应该使用“数据冗余法”

假设没有seller_uid

订单中心,假设没有seller_uid 上的查询需求,而只有 oid 和buyer_uid 上的查询需求,就蜕化为一个“1对多”的业务场景,对于“1对多”的业务,水平切分应该使用“基因法”。

再次回顾一下,什么是分库基因?

通过buyer_uid 分库,假设分为16个库,采用buyer_uid %16的方式来进行数据库路由,

所谓的模16,其本质是buyer_uid 的最后4个bit决定这行数据落在哪个库上,这4个bit,就是分库基因

也再次回顾一下,什么是基因法分库?

在订单数据 oid 生成时,oid 末端加入分库基因,让同一个buyer_uid 下的所有订单都含有相同基因,落在同一个分库上。

如上图所示,buyer_uid=666 的用户下了一个订单:

  • 使用 buyer_uid%16 分库,决定这行数据要插入到哪个库中。
  • 分库基因是 buyer_uid 的最后4个 bit,即1010。
  • 在生成订单标识 oid 时,先使用一种分布式 ID 生成算法生成前 60bit(上图中绿色部分)。
  • 将分库基因加入到 oid 的最后4个 bit(上图中粉色部分),拼装成最终64bit的订单 oid(上图中蓝色部分)。

通过这种方法保证,同一个用户下的所有订单oid,都落在同一个库上,oid的最后4个bit都相同,于是:

  • 通过 buyer_uid%16 能够定位到库。
  • 通过 oid%16 也能定位到库。

假设没有oid

订单中心,假设没有 oid 上的查询需求,而只有 buyer_uid 和 seller_uid 上的查询需求,就蜕化为一个“多对多”的业务场景,对于“多对多”的业务,水平切分应该使用“数据冗余法”。

如上图所示:

  • 当有订单生成时,通过 buyer_uid 分库,oid 中融入分库基因,写入 DB-buyer 库。
  • 通过线下异步的方式,通过 binlog+canal,将数据冗余到 DB-seller 库中。
  • buyer 库通过buyer_uid 分库,seller 库通过seller_uid 分库,前者满足 oid 和buyer_uid 的查询需求,后者满足seller_uid 的查询需求。

数据冗余的方法有很多种:

  • 服务同步双写。
  • 服务异步双写。
  • 线下异步双写(上图所示,是线下异步双写)。

不管哪种方案,因为两步操作不能保证原子性,总有出现数据不一致的可能,高吞吐分布式事务是业内尚未解决的难题,此时的架构优化方向,并不是完全保证数据的一致,而是尽早的发现不一致,并修复不一致。

最终一致性,是高吞吐互联网业务一致性的常用实践。保证数据最终一致性的方案有三种:

  • 冗余数据全量定时扫描。
  • 冗余数据增量日志扫描。
  • 冗余数据线上消息实时检测。

这些方案细节在“多对多”业务水平拆分的文章里详细展开分析过,便不再赘述。

oid/buyer_uid/seller_uid同时存在

通过上述分析:

  • 如果没有seller_uid ,“多key”业务会蜕化为“1对多”业务,此时应该使用“基因法”分库:使用buyer_uid 分库,在oid中加入分库基因
  • 如果没有oid,“多key”业务会蜕化为“多对多”业务,此时应该使用“数据冗余法”分库:使用buyer_uid 和seller_uid 来分别分库,冗余数据,满足不同属性上的查询需求
  • 如果 oid/buyer_uid/seller_uid 同时存在,可以使用上述两种方案的综合方案,来解决“多key”业务的数据库水平切分难题。

订单中心数据库切分方法--数据冗余法

• 当有订单生成时,通过buyer_uid分库,oid中融入分库基因,写入DB-buyer库
     • 通过线下异步的方式,通过binlog+canal,将数据冗余到DB-seller库中
     • buyer库通过buyer_uid分库,seller库通过seller_uid分库,前者满足oid和buyer_uid的查询需求,后者满足seller_uid的查询需求

为什么要冗余数据?

互联网数据量很大的业务场景,往往数据库需要进行水平切分来降低单库数据量。

水平切分会有一个patitionkey,通过patition key的查询能够直接定位到库,但是非patitionkey上的查询可能就需要扫描多个库了。

此时常见的架构设计方案,是使用数据冗余这种反范式设计来满足分库后不同维度的查询需求。

  • 例如:订单业务,对用户和商家都有订单查询需求:

Order(oid,info_detail);

T(buyer_uid,seller_uid,oid);

如果用buyer_uid来分库,seller_uid的查询就需要扫描多库。

如果用seller_uid来分库,buyer_uid的查询就需要扫描多库。

此时可以使用数据冗余来分别满足buyer_uid和seller_uid上的查询需求:

T1(buyer_uid,seller_uid,oid)

T2(seller_uid,buyer_uid,oid)

同一个数据,冗余两份,一份以buyer_uid来分库,满足买家的查询需求;一份以seller_uid来分库,满足卖家的查询需求。

订单中心数据库切分方法|如何实现数据冗余

1.服务同步双写

服务同步双写,即由服务层同步写冗余数据。

流程如图:

(1)业务应用代用服务层,写入数据

(2)服务层将数据写入DB1

(3)服务层将数据写入DB2

(4)服务层返回新增数据成功给业务应用

  • 优点:
  1. 简单,服务层由单写,改为两次写人
  2. 数据一致性较高,双写成功后才返回
  • 缺点:
  1. 因为由单写变为了两次写入,请求时间增长
  2. 数据仍有可能不一致(数据写入DB1后,服务宕机或重启,则数据无法写人DB2)

2.线下异步双写

为了屏蔽“复杂性”,数据双写由线下服务或者任务来完成,不再由服务层完成。

流程如图:

(1)业务应用代用服务层,写入数据

(2)服务层将数据写入DB1

(3)服务层返回新增数据成功给业务应用

(4)数据会被写入到数据库的log中

(5)线下服务或者任务读取数据库log

(6)线下服务或者任务插入T2数据

  • 优点:
  1. 数据双写与业务完全解耦
  2. 请求处理时间短
  • 缺点:
  1. 返回业务新增成功时,会存在一个数据不一致的时间窗口,但能保证最终一致性
  2. 数据一致性依赖于线下服务或者任务的可凹陷

原文:https://blog.csdn.net/sunhuiliang85/article/details/78418546

参考:https://blog.csdn.net/qq_18145031/article/details/77867853

分库分表:订单中心,多key业务如何进行数据库切分相关推荐

  1. Java互联网架构-Mysql分库分表订单生成系统实战分析

    分库分表的必要性 首先我们来了解一下为什么要做分库分表.在我们的业务(web应用)中,关系型数据库本身比较容易成为系统性能瓶颈,单机存储容量.连接数.处理能力等都很有限,数据库本身的"有状态 ...

  2. 如何彻底解决烦人的 MySQL 分库分表问题?写一个更好的数据库!

    作者 | 黄东旭 责编 | 郭   芮 我还清楚记得,五年前的这个时候,当时还在豌豆荚,午后与刘奇和崔秋的闲聊关于未来数据库的想象,就像一粒种子一样,到了今天看起来也竟枝繁叶茂郁郁葱葱,有点感慨.按照 ...

  3. mysql分库分表中间件6_当当开源sharding-jdbc,轻量级数据库分库分表中间件

    近期,当当开源了数据库分库分表中间件sharding-jdbc. Sharding-JDBC是当当应用框架ddframe中,从关系型数据库模块dd-rdb中分离出来的数据库水平分片框架,实现透明化数据 ...

  4. 当当网mysql分库分表策略_当当开源sharding-jdbc,轻量级数据库分库分表中间件

    近期,当当开源了数据库分库分表中间件sharding-jdbc. Sharding-JDBC是当当应用框架ddframe中,从关系型数据库模块dd-rdb中分离出来的数据库水平分片框架,实现透明化数据 ...

  5. 数据库改造:怎样用MySQL对10亿级订单量进行分库分表?

    一.背景 随着公司业务增长,如果每天1000多万笔订单的话,3个月将有约10亿的订单量,之前数据库采用单库单表的形式已经不满足于业务需求,数据库改造迫在眉睫. 二.订单数据如何划分 我们可以将订单数据 ...

  6. 10亿级订单系统分库分表设计思路

    一.背景 随着公司业务增长,如果每天1000多万笔订单的话,3个月将有约10亿的订单量,之前数据库采用单库单表的形式已经不满足于业务需求,数据库改造迫在眉睫. 二.订单数据如何划分 我们可以将订单数据 ...

  7. 商城订单模块实战 - 数据库设计、ABA问题处理、读写分离分库分表

    引言 订单系统可以说是整个电商系统中最重要的一个子系统,因此订单数据可以算作电商企业最重要的数据资产.这篇文章我们来看看在我们的商城系统中订单服务是如何实现的,特别是在设计和实现一个订单系统的过程中有 ...

  8. 数据库分库分表,何时分?怎样分?

    点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! 一. 数据切分 关系型数据库本身比较容易成为系统瓶颈,单机存储容量.连接数.处理能力都有限.当单表 ...

  9. 史上更全面的数据库分库分表、数据一致性、主键分配思路!

    作者:butterfly100 cnblogs.com/butterfly100/p/9034281.html 一. 数据切分 关系型数据库本身比较容易成为系统瓶颈,单机存储容量.连接数.处理能力都有 ...

最新文章

  1. Java传输对象模式
  2. 队列的链式存储和实现(C语言)【队列】(8)
  3. 【struts2】struts2配置文件—struts.properties
  4. ​4种实现多列布局css
  5. python是动态语言_Python是动态语言:动态添加或删除属性、方法
  6. 中移4G模块-ML302-OpenCpu开发-51单片机串口转I2C
  7. NumPy:数组计算
  8. oc传参数给js_【一句话攻略】彻底理解JS中的回调(Callback)函数
  9. 小米路由青春版-2.1.26开发版固件
  10. 地图历史大反转!GISer如何查看历史影像
  11. Word 文档乱码-请选择使文档可读的编码 重启电脑-不是解决方法的解决方法(可能已经晚了)
  12. C#项目之 GMap.net 标记点及 绘制多点之间的距离
  13. 小米手环2来电不震动,来电不提醒怎么办
  14. DAY10微信小程序项目开发技术总结
  15. 顺应数字化转型趋势化解“上云”风险,擎天Enclave保障数据安全
  16. iOS textField属性详解
  17. Java流程控制——用户交互Scanner
  18. Photoshop CC 2017 For Mac安装教程
  19. 思科AP常见型号和WiFi标准POE标准对应表
  20. 3. 描述性统计分析

热门文章

  1. 产品经理与项目经理的区别?
  2. CSS的引入方式:行内样式表(行内式)、内部样式表(嵌入式)、外部样式表(链接式)
  3. 微电子技术和计算机技术的区别,信息技术和计算机技术的区别是什么?
  4. 应用随机过程笔记(一):随机过程的定义
  5. 网站统计功能的设计和实现
  6. android给EditText加金额的属性,带元,角和分,如¥12.34
  7. 销售管理系统哪种好?
  8. win11和win10哪个好 win11和win10对比分析
  9. 直击物联网安全痛点,青莲云亮相2017中国(上海)国际物联网大会
  10. 华为(huawei)USG6000的CLI命令行综合配置之Ensp真机连接 USG6000防火墙