背景
几年前参与了公司的溯源项目,数据库采用的是Mysql随着业务的增长,我们进行索引、硬盘(将机械硬盘替换为SSD硬盘)、读写分离等相关优化后,我们发现单库单单表模式的查询和存储速度还是难以满足业务需求,此时我们采用了分库分表的方案来解决此问题。
前言
关于分库分表所涉及核心内容如下:
什么时候需要采用分库分表
互联网系统设计需要考虑三高.但是并不是项目初期我们就需要考虑分库分表,而是存在如下的问题时,我们才需要进行相关的分库分表设计。
单库数据量太大
单个服务的磁盘空间是有限制的,如果并发压力下,所有的请求都访问同一个节点,肯定会对磁盘IO造成非常大的影响。
单表数据量太大
MySQL单库数据量在5000万以内性能比较好,超过阈值后性能会随着数据量的增大而变弱。
数据库连接
数据库连接是非常稀少的资源,如果一个库里既有标签、商品、订单相关的数据,当海量用户同时操作时,数据库连接就可能成为瓶颈。
常见分库分表方法
垂直分库
垂直分库是按照系统中的不同业务进行拆分,比如拆分成订单库、标签库、商品库,把它们部署在不同的数据库服务器。
垂直分表
垂直分表俗称的大表拆小表,将一些不常用的、数据较大或者长度较长的列拆分到另外一张表。
溯源项目中原来的标签表有50多个字段,经过垂直分表为标签基础信息表和标签附属表。
水平分表
水平分表是在同一个数据库内,把同一个表的数据按一定规则拆到多个表中。
水平分库
水平分库是把同一个表的数据按一定规则拆到不同的数据库中,每个库中的表结构都是一样的,且每个库可以放在不同的服务器上。
小结
-
垂直分表:将一个大表的字段按不常用的、数据较大或者长度较长的列原则拆分为多个表,既能使业务表结构清晰,也提升相关的性能。
-
垂直分库:按业务耦合松紧归类,分别存放在不同的库并分布在不同服务器,从而使访问压力被多服务器负载,大大提升性能,需要解决跨库所带来的复杂问题。
-
水平分库:把一个表的数据(按数据行)分到多个不同的库,每个库只有这个表的部分数据,这些库可以分布在不同服务器,从而使访问压力被多服务器负载,大大提升性能。需要解决跨库所带来的复杂问题。
-
水平分表:可以把一个表的数据(按数据行)分到多个同一个数据库的多张表中,每个表只有这个表的部分数据,从而提升性能。
分库分表常用策略
Range范围
Range即范围策略划分表,我们可以将表的主键,按照从01000万的划分为一个表,10002000万划分到另外一个表,以此类推。
优点
简单、数据均衡、扩容简单
缺点
负载不均衡,随着id的逐渐增会将最近一段时间内的数据汇集在一起,存在热点问题。
Hash取模
指定的路由key对分表总数进行取模,把数据分散到各个表中。
优点
简单、数据均衡、负载均衡,不会存在明显的热点问题。
缺点
扩容比较困难,需要迁移数据。
Range+Hash取模混合
既然range存在热点数据问题,hash取模扩容迁移问题,我们需要将两种策略进行结合,先采用先用range范围方案将数据分配到不同的库,然后再使用hash取模的方案将数据分配到不同的表。
分库分表引入问题
引入分库分表了带了许多问题,我们对此进行相关的优化。
事务问题
原来采用的时单数据库处理,采用分库分表后,业务拆分为多个数据库,事务变为分布式事务,关于分布式事务,我们引入分布式事务中间件来解决。
跨库john问题
原先单库的时候可以采用join直接进行关联查询,但是分库分表后,无法在进行join,需要将原先的一次查询完成的,现在需要拆分为多次查询。
集合排序问题
跨节点的count,order by,group by以及聚合函数等问题:可以分别在各个节点上得到结果后在应用程序端进行合并。
实现方案
分库分表的实现方案,目前常用的还是MyCat与Sharding-JDBC。
Mycat
MyCat是一个开源的分布式数据库系统,是一个实现了MySQL协议的服务器,前端用户可以把它看作是一个数据库代理,用MySQL客户端工具和命令行访问,而其后端可以用MySQL原生协议与多个MySQL服务器通信,也可以用JDBC协议与大多数主流数据库服务器通信,其核心功能是分表分库,即将一个大表水平分割为N个小表,存储在后端MySQL服务器里或者其他数据库里。
核心架构
Sharding-JDBC
定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
核心架构
相关对比
- Mycat:中间层方案,在应用程序和MySQL之间搭建一层Proxy
- Sharding-JDBC:轻量级JDBC框架,基于jdbc层面操作。
溯源项目优化
优化方案
针对溯源项目我们进行了如下优化:
垂直分表
我们将原有的标签表垂直分表为标签表和标签附属表。
垂直分库
将原来的溯源的单体数据库垂直分为标签库、商品库、订单库。
水平分库分表
我们按照业务每年2000万的增长速度,对标签表化分为20个库,每个库20个表,每张表500万数据进行了划分。这样水平分库分表后我们能支撑20亿的标签数据量.
技术选型
通过相关对比和研究,溯源项目最终的技术选型为Sharding-jdbc,之所以选择Sharding-jbdc存在如下优点
- 直接封装底层JDBC API,为增强版的JDBC驱动,旧代码迁移成本几乎为零.
- 轻量Java框架,使用客户端直连数据库,以jar包形式提供服务,无proxy代理层,无需额外部署。
- 分片策略灵活,可支持等号、between、in等多维度分片,也可支持多分片键
- SQL解析功能完善,支持聚合、分组、排序、limit、or等查询。
数据归档
我们将N年之前的数据迁移定期迁移到ElasticSearch中,从而保证Mysql中的数据在一个可以接受的范围。
其他问题
分库分表虽然能解决项目中的问题,但是也带来了其他的问题。
数据迁移
针对历史数据如何按照新的分库分表的切分规进行迁移,我们采用的是canal进行mysql的数据迁移,关于数据迁移这块,后续进行讲解。
报表问题
原先的报表查询只需要通过相关的SQl语句进行统计即可,但是分库分表后我们无法,只能将数据通过大数据的平台来进行相关的报表处理。
总结
本文通过优化项目介绍了分库分表的规则和实现,需要注意:架构是演变而来而不是设计而来,项目初期我们是无需设计分库分表,切莫进行过度设计,关于分库分表如有疑问,可以随时反馈,大家共同学习,共同进步。
我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。