1、什么时候考虑分库分表?
阿里的《Java开发手册》上是说单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。
其实这个数字也不是定死的,应该是根据具体的硬件情况来确定。我们可以通过调整INNODB_BUFFER_POOL_SIZE 来让MySQL有足够的内存来加载表的索引,那查询也没有太大的问题。而 INNODB_BUFFER_POOL_SIZE是依赖于服务器的硬件水平比如内存大小,一般占服务器内存的70%~80%左右(因为要给操作系统和其他应用留一部分内存),如果MySQL没有足够的内存一次性从磁盘中加载并储存表的索引,那么在查询的时候需要进行额外的磁盘IO来将索引读入内存,降低数据库的性能。因此,有时候改善服务器的硬件配置也能缓解SQL查询效率低的问题。
分库分表一般是在经过优化索引、集群的读写分离等一系列优化之后,还存在查询效率不理想的情况下才进行的,如果业务数据量不大的话没必要这么折腾。
分库分表有水平切分和垂直切分2种方式,一般会先采用垂直切分,水平切分是大招。
2. 垂直切分
垂直切分有垂直分库和垂直分表
2.1垂直分表
垂直分表是将单表中的一部分“列”划分到另外一个表中。目的是为了减少SQL语句查询扫描的行数。那到底要将哪些列分离出来呢?
可以从这些角度考量:
- 高频访问和低频访问的列,比如下图2.1的《用户信息表》的收货地址只有在购买配送的时候才会访问,相对于用户名和电话这些热点数据来说算是冷数据,可以将收货地址单独划分成一个表
- 大文本字段 text、blob,收货地址它算是一个大文本字段,如果频繁在客户端和数据库中的网络中传输也不大合适。
- 如果经常关联在一起查询的字段也没必要拆分,跨表跨库的join查询还是很麻烦的。
图2.1
一般来说,《用户信息表》中 1个用户有3个左右的收货地址,500万个用户大概就有1500万行记录。为了查询这500万个用户的信息,得扫描1500万行数据,无形之中就降低了查询的效率。并且,“收货地址”只有在购买配送的时候才会使用,相对用户登录等高频行为来说算是低频的行为。我们可以将用户信息这样的热点数据和查询相对少的“收货地址” 划分成2个表,划分之后,我们查询用户的信息的时候就可以减少SQL扫描的行数,没必要因为收货地址而去多扫描几行,提高查询效率。
2.2 垂直分库
垂直分库划分的考量主要是要规划出一个相对独立的服务模块,像微服务的用户服务模块、商品模块、订单模块等。
图2.2
比如用户库可以划分为普通用户库、企业用户库,分别对应普通用户服务功能、企业服务功能,它们作为微服务中相对独立的业务功能,比如普通用户服务模块一般不会去访问企业用户数据库。
2.3 垂直切分优缺点
优点:
- 减少SQL扫描的行数,比如把“收货地址” 与用户信息热点数据分离出来,提高查询用户信息的效率
- 业务细分,将用户库划分为普通用户库和企业用户库,C端用户和企业用户办理的业务不完全相同,划分之后便于分级管理。
缺点:
- 跨库join查询比较麻烦
- 单个表的数据后期还是会很大。
3.水平切分
当《用户表》(见图2.1右下)的用户记录膨胀到了1000万行,严重影响了查询的效率时,垂直分表也没有办法解决问题,我们就考虑进行水平切分。
水平切分的常见策略有范围切分、hash切分等。
3.1 范围切分
这种切分方式使用于数据行本身是有序的,比如说日志表(如下图3.1)就是按时间顺序创建的,可以根据create_time,查询的时候可以借助一个映射表来快速定位到哪个库的哪个表。
3.2 hash切分
hash切分可以让热点数据均匀分布到各个库的表中,防止热点数据集中于单个库导致容易达到性能瓶颈。这种切分方式不要求数据本身是否有序,如果日志表采用hash切分让记录均匀分布的话反而查询不方便。
具体的切分方式我借用了别人的图,划分的依据是
- 先选一个字段作为拆分依据,比如我用MEMBER_ID作为划分依据
- 然后 hash(MEMBER_ID) mod N,其中N是分库的数量或者划分表的数量,一般是取2的n次方,为了让数据均匀分布
图中是以MEMBER_ID字段划分,预计要划分出1024个库,那么就通过hash(Pavarottil 7) mod 1024= 537来确定要划分到分库3里面。
确定了在哪个数据库之后,要再确定划分到库里面的哪个表,也是采用同样的方法,先hash再取mod。
3.3 水平切分优缺点
优点:
- 解决单表数据量过大的问题
- 均匀分布的数据解决热点数据集中导致单个库压力大的问题
缺点:
跨库join查询效率比较差
分库和分表的操作是可以结合进行的,可以划分为单库多表和多库多表,垂直切分和水平切分也可两者结合使用。