【分库分表】企业实战全流程总结
目录
背景
分库分表方案
整体思路
待分表的数据表
为什么决定进行分库分表
分库分表带来的问题
分表键分析
分表规则
分表技术实现
技术选型
查询方案改造
写方案改造
全局主键策略
背景
随着时间的推移,数据量不断的积累,数据存储和数据操作必将遇到、瓶颈,所以我们需要在不同数据规模下的针对数据库、中间件、应用服务器资源做出一定的规划;
针对数据增量的问题,应对方案通常为数据水平扩展和冷热数据隔离,冷热数据隔离将数据分成冷库和热库,冷库存放的是已经走到最终状态的数据,同时也是不常使用的数据;热库存放的未走到最终状态的数据,还需要在进行变更的、经常使用的数据;水平扩展即为对数据做分库分表,按照配置好的分片策略,将数据分开存储,本文主要针对后者进行方案分析。
分库分表方案
整体思路
待分表的数据表
为什么决定进行分库分表
- 根据业务类型,和业务容量的评估,来选择和判断是否使用分库分表。
- 当前数据库本身具有的能力和压力的评估。
- 数据库的物理隔离,例如减少锁的争用、资源的消耗和隔离等。
- 热点表较多,并且数据量大,可能会导致锁争抢,性能下降。
- 数据库的高并发,数据库的读写压力过大,可能会导致数据库或系统宕机。
- 数据库(MySQL5.7以下)连接数过高,会增加系统压力。
- 单表数据量大,如SQL使用不当,会导致io随机读写比例高。查询慢(大表上的B+树太大,扫描太慢,甚至可能需要4层B+树)
- 备份和恢复时间比较长。
分库分表带来的问题
- 事务一致性问题
- 跨节点关联查询 join 问题
- 跨节点分页、排序、函数问题
- 全局主键避重问题
- 数据迁移、扩容问题
以数据量最大的医嘱表为例,分析数据量和占用内存情况:上线将近4个月,医嘱650w+,每月170w+的增量,预计2个月后达到1000w级别数据量级。
表名 | 存量 | 每月增量 |
医嘱 | 650w | 170w |
目前占用内存大小总计13.08GB,数据8.63GB,索引4.45GB,卫生院的count(distinct 患者id)=509088(50w+)。
基于数据量和增长速度来看,还有其他同等业务体量的表,如执行计划、订单、流程和任务等。
分表键分析
基于重要的业务使用场景和代码进行分析
选择这个 Sharding Key 最重要的参考因素是,我们的业务是如何访问数据的,一旦做了分库分表,就会极大地限制数据库的查询能力。
分表规则
分库分表原则:分多少个库需要用并发量来预估,分多少表需要用数据量来预估
根据医嘱表增量,单表1000w为上限,预留10年的时间,计算需要多少个分库和分表:
分库:由于目前的业务并发量在可接受范围,且已有根据机构划分的动态数据源切换的方案,暂保留原有设计,暂不考虑分库;
分表:按照卫生院的增量,一年数据量2040w数据量,预留10年,分表总数为21张,转换成2的指数倍=>64张
表名 | 分库总数 | 分表总数 |
医嘱 | 机构数 | 64 |
分表技术实现
技术选型
业界组件 | 原厂 | 功能特性 | 备注 |
Meituan Atlas | 美团 | 读写分离、单库分表 | 目前已经在原厂逐步下架。 |
Cobar | 阿里(B2B) | Cobar 中间件以 Proxy 的形式位于前台应用和实际数据库之间,对前台的开放的接口是 MySQL 通信协议 | 开源版本中数据库只支持 MySQL,并且不支持读写分离。 |
MyCAT | 阿里 | 是一个实现了 MySQL 协议的服务器,前端用户可以把它看作是一个数据库代理,用 MySQL 客户端工具和命令行访问,而其后端可以用 MySQL 原生协议与多个 MySQL 服务器通信 | MyCAT 基于阿里开源的 Cobar 产品而研发 |
TDDL | 阿里淘宝 | 动态数据源、读写分离、分库分表 | TDDL 分为两个版本,一个是带中间件的版本,一个是直接 Java 版本 |
MTDDL | 美团点评 | 动态数据源、读写分离、分布式唯一主键生成器、分库分表、连接池及 SQL 监控 | |
DRDS | 阿里 | DRDS(Distributed Relational Database Service)专注于解决单机关系型数据库扩展性问题,具备轻量 (无状态)、灵活、稳定、高效等特性,是阿里巴巴集团自主研 | |
Sharding jdbc | apache 开源项目 | 完全兼容 JDBC 和各种 ORM 框架。适用于任何基于 Java 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。基于任何第三方的数据库连接池,如:DBCP,C3P0, BoneCP, Druid, HikariCP 等。支持任意实现 JDBC 规范的数据库。目前支持 MySQL,Oracle,SQLServer 和 PostgreSQL | Apache 项目,定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动 |
ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成。 他们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如Java同构、异构语言、容器、云原生等各种多样化的应用场景。
sharding-jdbc架构图
查询方案改造
针对查询条件包含分片键,sharding本身已支持,下面主要讨论不包含分片键的场景,可以归类以下几个场景:
基于已有查询条件可以找到分片键的
可以通过代码改造的形式,上下文参数传递或查询出分片键,传入已有查询条件中:
基于已有条件无法找到分片键的
考虑索引表法查询
索引表法是创建一个包含patient_id和相关查询条件的索引表,在插入医嘱时再插入一条数据到索引表中,由于冗余数据量较大,该方法暂不推荐。
考虑基因法查询
将分片键的信息保存在想要查询的列中,这样通过查询的列就能直接知道所在的分片信息,这种方法叫做基因法。
基因法的原理出自一个理论:对一个数取余2的n次方,那么余数就是这个数的二进制的最后n位数。
举例: 医嘱id=1234(10011010010),患者id=4329(1000011101001)
假设分16张表,按照基因法生成的医嘱id为19753
19753%16 = 4329%16 = 9
两种方式结合
在新老数据切换的时候,两者结合:
sharding-jdbc的广播路由
对于不携带分片键的SQL,则采取广播路由的方式。根据SQL类型又可以划分为全库路由、全库表路由、全实例路由、单播路由和阻断路由这5种类型。
全库表路由用于处理对数据库中与其逻辑表相关的所有真实表的操作,主要包括不带分片键的DQL和DML,以及DDL等。
写方案改造
全局主键策略
UUID
优点:UUID主键是最简单的方案,本地生成,性能高,没有网络耗时。
缺点:由于UUID非常长,会占用大量的存储空间;另外,作为主键建立索引和基于索引进行查询时都会存在性能问题,在InnoDB下,UUID的无序性会引起数据位置频繁变动(不是以主键的聚簇顺序存储),导致分页。
Snowflake分布式自增ID算法
业界使用比较多的有:Twitter、Mongdb objectID、美团的Leaf
优点:1、毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
2、不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
3、可以根据自身业务特性分配bit位,非常灵活。
缺点:强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态,美团的Leaf解决这个问题。
结合数据库维护主键ID表
此方案较为简单,但缺点也明显:存在单点问题,强依赖DB,当DB异常时,整个系统都不可用。
目前余杭分库方案采用这个方案,且数据库重点保障,可以继续延续该方案;
数据库sequence+分片键后4位组合 【推荐使用】
业务在保存分表数据前,根据SequenceGenerateUtil