随着当今系统中的数据量越来越庞大,当我们设计系统时经常会关心数据库的性能,以及数据库是否需要做分库分表处理。数据库是否要分库分表需要由业务吞吐量、数据库品牌、数据量等多方面决定,分库分表也还分为水平切分和垂直切分。这里仅描述不同场景下,数据库做水平的情况。
我理解的数据库分库分表本质上的目的就是一次数据库查询操作所涉及的数据量,通过架构设计和数据库设计,把每一次数据库交互涉及的数据量缩小。
1. 单一key条件下的分库分表
单key查询条件是最简单的一类业务查询,假设有user表
有业务查询,按照id查询用户信息,就是单key查询条件,对应的语句是
select * from user where id='xxxxx'
当user表的数据量较小时在id列上有索引(一般是主键)的情况下,这条语句效率挺高;随着业务增长系统内用户增加后,效率会逐渐下降,不同数据库产品的下降趋势不一,性能开始下降的阈值也和表内数据量有关,例如MySQL理论上单表5000万数据会出现性能急剧下降的情况,但如果是字段很多的表,在几百万数据的时候就会出现性能下降。
对于单key类业务的表,一般会选择按照这个key的情况来进行水平切分,以user表为例的话 key就是id。水平切分一般有by range和by hash两种常见方式。
- By range
By range是指通过id的值的范围去把数据切分到不同的表或者数据库里,比如说id在1~100000的数据放在user_t1表,id在100001~200000的数据放在user_t2表。
这种切分的优点:
切分简单,软件架构路由规则简单;线性扩容方便,再多数据,再多加几个表或者库就好。
缺点也很明显:
在id分布不均匀时,会出现某几个表热度很高,而其他表访问量低的情况。
- By hash
By hash是解决By range方式中id分布不均匀的场景的解决方案,通过对id进行哈希后的值再拆分,无论原来id分布情况如何,经过哈希后都会变成分布较为均匀的值。
优点:
切分简单,数据分布均匀
缺点:
扩容时需要用新的哈希算法rehash一下,会引起数据迁移,需要在扩容时考虑数据如何平滑迁移。
数据平滑迁移一般可以这么做
假设我们原来的分库是下面这样,通过id取2的模的方式分库。
需要扩容时,采取通过id取4的模的方式分库,可以先配置好应用,再中断已有2套数据库的主备复制的方式扩容,无数据迁移,如下图所示。
平滑扩容后对4个库里的数据进行清洗,删除不要的数据,再重新进行主备库搭建后,完成平滑扩容。
2. 双key条件下的分库分表
但是实际应用中,一般不会只按一个维度进行数据访问,按单一key方式分库分表后,其他维度的数据访问,就变得需要遍历所有库才能完成了。假设60%是通过id查询,30%是通过name查询,还有10%是其他维度的查询组合。这种情况下,按id分表意味着name和其他维度的查询需要遍历所有的库才能得出结果。在name和id的查询需要兼顾的时候,通过id分库对于name维度查询就是无法接受的结果。
对于这种查询,可以通过在id里融合name信息的方式做分表来解决,假设通过id = uuid + hash(name)方式生成用户的id列。再通过hash(id里由name哈希来的部分)进行分库。这样具有相同hash值的name列也会在同一个库里,通过id查询时通过uuid里hash(name)的部分也可以定位出该id的数据位于哪个库。
说的很抽象,举个例子,name%8 = 3, id = uuid(63)+“3”,分库策略为对id最后1位模2,这样就既能保证通过id的查询能直接定位到库(取最后一位的数字模2),也能保证通过name的查询能直接定位到库(模8后再模2)。在这个例子的前提条件下,可以使id上60%的查询和name上30%的查询不需要遍历全库,且最高支持分8片(最高支持的分片数取决于name字段的哈希结果,模16就可以最多支持16分片了)。