分库分表

分库分表主要是为了解决因数据量太大而导致查询慢的问题以及应对高并发的问题;如果数据量太大就分表,若并发请求量高就分库;中大型项目中,一旦遇到数据量比较大,都知道对数据进行拆分,有垂直拆分水平拆分两种。

  • 垂直拆分:从业务角度拆分多个库
  • 水平拆分:同一个业务数据量大,进行水平拆分。Redis集群就是典型的水平分片

选择分片键

如何选择一个合适的列作为分表的分片键非常重要,将直接影响分库分表的效果,选择分片链有一个最重要的参考因素业务是如何访问数据的;比如把订单ID作为分片键来诉分订单表,拆分之后若按照订单ID来查询订单,就需要先根据订单ID和分片算法计算所要查的这个订单具体在哪个分片上,也就是哪个库的哪张表,然后再去那个分片执行查询操作即可;

但当用户打开我的订单页面时,查询条件是用户ID,由于这里没有订单ID,因此无法知道所要查询的订单具体在哪个分片上,若要强行查询的话只能把所有的分片都查询一遍,再合并查询结果,这个过程比较麻烦,且性能很差,对分页也很不友好;

若把用户ID作为分片键,也会面临同样的问题,使用订单ID作为查询条件时无法定位到具体的分片上;这个问题的解决办法是,在生成订单ID时把用户ID的后几位作为订单ID的一部分;这样按订单ID查询的时候,就可以根据订单ID中的用户ID找到分片;所以在我们的系统中订单ID从唯一ID服务获取ID后,还会将用户ID的后两位拼接,形成最终的订单ID;

若有商家希望查询自家家店的订单,以及与订单相关的各种报表,对订单做了分库分表,就没法解决了;一般的做法是,把订单里数据同步到其他存储系统中,然后在其他存储系统里解决该问题。比如可以再构建一个以店铺ID作为分片键的只读订单库,专供商家使用,或者数据同步到Hadoop分布式文件系统HDFS中,然后通过一些大数据技术生成与订单相关的报表;

常用分片策略

  • 取余\取模 : 优点均匀存放数据,缺点扩容非常麻烦
  • 按范围分片 : 比较好扩容, 数据分布不够均匀
  • 按时间分片 : 比较容易将热点数据区分出来。
  • 按枚举值分片 : 例如按地区分片
  • 按目标字段前缀指定进行分区:自定义业务规则分片

水平分片从理论上突破了单机数据量处理的瓶颈,且扩展相对自由,是分库分表的标准解决方案。一般在系统设计阶段就应该根据业务耦合松紧来确定垂直分库、分表方案,在数据量及访问压力不是特别大的情况,首先考虑缓存读写分离索引技术等方案。若数据量极大,且持续增长,再考虑水平分库、分表方案。

分库分表缺点

虽然数据分片解决了性能可用性以及单点备份恢复等问题,但是分布式的架构在获得收益的同时,也引入了非常多新的问题。

  • 事务一致性问题:原本单机数据库有很好的事务机制能保证数据一致性,但是分库分表后,由于数据分布在不同库甚至不同服务器,不可避免会产生分布式事务问题
  • 跨节点关联查询问题:没有分库时可很容易进行跨表关联查询,但是在分库后表被分散在不同数据库,无法关联查询。需要将关联查询拆分成多次查询,然后将获得结果拼装
  • 跨节点分页、排序函数:跨节点多库进行查询时limit分页order by排序等问题,就变得比较复杂。需要先在不同分片节点中将数据排序返回,然后将不同分片返回的结果集进行汇总再排序。这时非常容易出现内存崩溃的问题
  • 主键避重问题:由于表中数据同时存在不同数据库中,无法再使用自增主键,某个分区数据库生成的ID无法保证全局唯一,因此需要单独设计全局主键,以避免跨库主键重复问题
  • 公共表处理参数表数据字典表等都是数据量较小变动少,且属于高频联合查询的依赖表。这一类表一般需要在每个数据库中都保存一份,且所有对公共表的操作都要分发到所有的分库去执行
  • 运维工作量:面对散乱的分库分表之后的数据,应用开发工程师和数据库管理员对数据库的操作都变得非常繁重

分库分表时机

单表记录达到500W这个级别,或单表容量达到2GB,建议进行分库分表。而考虑到分库分表需要对数据进行再平衡,所以若要使用分库分表,就要在系统设计之初就详细考虑好分库分表的方案,这里要分两种情况。

  • 一般对用户数据这类后期增长比较缓慢的数据,一般可按照三年左右的业务量来预估使用人数,按照标准预设好分库分表的方案。
  • 对于业务数据这一类增长快速且稳定的数据,一般则需要按照预估量的两倍左右预设分库分表方案,且由于分库分表后期扩容是非常麻烦的,所以在进行分库分表时,尽量根据情况多分一些表

在设计分库分表方案时,尽量兼顾业务场景和数据分布。在支持业务场景的前提下,尽量保证数据能够分得更均匀。

一旦用到了分库分表,就会表现为对数据查询业务的灵活性有一定的影响,进行排序分页聚合等操作,很容易扛不住。尽量在分库分表的同时,再补充设计一个降级方案,如将数据转存一份到ES,ES可实现更灵活的大数据聚合查询。

常见分库分表组件

由于分库分表之后,数据被分散在不同的数据库、服务器。因此对数据的操作就无法通过常规方式完成,且还带来了一系列的问题。

ShardingSphere

Sharding-JDBC是当当网研发的开源分布式数据库中间件,他是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBCSharding-ProxySharding-Sidecar(计划中)这3款相互独立的产品组成。 他们均提供标准化的数据分片分布式事务数据库治理功能,可适用于如Java同构、异构语言、容器、云原生等各种多样化的应用场景。

MyCat

MyCat基于阿里开源的Cobar产品研发,Cobar的稳定性、可靠性、优秀的架构和性能以及众多成熟的使用案例使得MyCat一开始就拥有一个很好的起点。MyCat虽然是从阿里技术体系中出来的,但跟阿里其实没什么关系。

DBLE

DBLE该网站包含几个重要产品。其中分布式中间件可以认为是MyCAT的一个增强版,专注于MySQL的集群化管理。另外还有数据传输组件和分布式事务框架组件可供选择。

分库分表方案

hash取模range范围方案是分库分表方案中常用的方案;分库分表方案最主要就是路由算法,把路由的key按照指定的算法进行路由存放。

hash取模方案

hash的方案就是对指定的路由key分表总数进行取模,可以参考HashMap源码。

优点:是可以将数据均匀放到各个分表中不会出现热点问题

缺点:是数据迁移扩容会比较困难。因为若之前分表是4,现在分表变成了8,由于取模基数变化导致之前的数据可能会找不到。要解决这样的问题,就需要将之前的数据重新按照新的取模基数做hash方案把数据进行迁移,放到新规划的分表中。但是对某些不允许停机做数据迁移的业务就会非常痛苦。

range范围方案

range方案比较简单,就是把一定范围内的订单,存放到一个表中;如id=12放到0表中,id=1300万的放到1表中。设计这个方案时就是前期把表的范围设计好。通过id进行路由存放。

优点:有利于将来的扩容,不需要做数据迁移。

缺点:有热点问题。

扩展

hash是可以解决数据均匀的问题,range可以解决数据迁移问题,可以将两种方案结合,在一定的范围内数据均匀,每次扩容肯定会先设计好这次扩容的范围大小,只要保证这次的范围内的数据均匀就行了。

可以先定义一个group的概念,首先通过范围range定位是哪个group组,然后根据hash方案定位是哪个DB,再根据range方案定位哪个Table。

例如对10进行取模,如果值为【0,1,2,3】就路由到DB_0,【4,5,6】路由到DB_1,【7,8,9】路由到DB_2。1000万以内的id都均匀的分配到DB_0,DB_1,DB_2三个数据库中的Table_0表中。

扩容的时候只需要再设计一个group02组就行了。

设计是比较简单的,就3张表,把group,DB,table之间建立好关联关系就行了。

group与DB的关系

table与db的关系

在开发的时候把三张关联数据保存到缓存中。