分库分表

在使用关系型数据库时,随着用户数据的不断增大,单表的数据大小可能超过 1000w 或者 1000G 而出现索引膨胀。单机数据库的存储容量、连接数和处理能力也很难满足需求。在使用主从复制、读写分离、优化查询和索引后如果仍然达不到要求,就需要考虑数据库的切分(Sharding),切分角度主要有水平和垂直,方式主要为分表和分库。

分表

分表就是将一个表的数据分布在多个表中,查询时我们查询一个表就可以。单表数据量很大会影响数据库的执行性能,当数据到达几百万时通常就需要考虑分表。例如将表中的数据按照用户 ID 来分表,单个用户的数据就放在一个表中,操作时只操作这一张表就可以,并且可以控制每个表的数据记录在一个可以控制的范围内。

分库

分库就是将一个数据库的数据拆分到多个库中,访问的时候只访问一个数据库即可。

切分后出现的问题

  • 事务一致性的问题

    可以使用分布式事务来解决,例如 XA 接口。

  • 连接(join)问题

    可以将可以将原来的连接分解成多个单表查询,然后在用户程序中进行连接。

  • ID 唯一性问题

    可以使用全局唯一 ID(GUID)或者为每一个分片指定一个 ID 范围,也可以使用分布式 ID 生成器生成 ID。

水平切分

水平切分从横向的角度将一个表的数据分布到多个表中,但是每个表的结构都相同,所有表中的数据合起来就是全部数据。水平切分可以将数据均匀放更多的库里,然后用多个库来抗更高的并发,还有就是用多个库的存储容量来进行扩容。

img

水平切分解决了单表数据量多大的问题,提升了系统稳定性和负载能力,并且应用端的改造小,无需拆分业务模块。但是水平切分的数据扩展难度和维护量较大。

范围切分

按照一定的范围去切分。例如按照 ID 区间,将 userId 为 1~9999 的记录分到第一个库,10000~20000 的分到第二个库,以此类推。

范围切分的优点在于:1、单表数据量可控;2、由于是顺序存储,天然适合水平扩展,后期如果想对整个分片集群扩容时,只需要添加节点即可,无需对其他分片的数据进行迁移。3、使用分片字段进行范围查找时,连续分片可快速定位分片进行快速查询,有效避免跨分片查询的问题。但是范围切分会出现冷热数据,热点数据会成为性能瓶颈。例如按时间字段分片,有些分片存储最近时间段内的数据,可能会被频繁的读写。

Hash 取模切分

即采用 hash 取模 mod 来切分。例如将 Customer 表根据 cusno 字段切分到 4 个库中,余数为 0 的放到第一个库,余数为 1 的放到第二个库,以此类推。这样同一个用户的数据会分散到同一个库中,如果查询条件带有 cusno 字段,则可明确定位到相应库去查询。

Hash 取模切分的优点是数据分片比较均匀,不会出现热点和并发访问的瓶颈。但是在后期分片集群扩容时,需要迁移旧的数据,也容易出现跨分片查询的复杂问题。

垂直切分

垂直拆分是从纵向的角度,把一个有很多字段的表拆分为多个表,或者是多个库。拆分后每个表的结构都不相同,但都包含了原表的部分字段。一般来说,会将访问频率较高的字段放到一个表中,然后将访问频率较低的字段放入另外的表中。访问频率高的字段越少,使用缓存时就可以缓存更多的行,数据库的性能就更好。垂直切分也可以分为垂直分库和垂直分表。

img

垂直切分可以降低业务层面的耦合。在高并发场景下,垂直切分一定程度上还可以提升 I/O、数据连接数等性能。但是垂直切分后,部分表可能无法关联,只能通过接口聚合解决,会提升开发的复杂度。垂直切分也没有解决单表数据量过大的情况。

垂直分库

垂直分库就是根据业务耦合性,将关联度低的不同表存储在不同的数据库。做法与大系统拆分为多个小系统类似,按业务分类进行独立划分。与”微服务治理”的做法相似,每个微服务使用单独的一个数据库。(例如 如用户 User 一个库,商品 Producet 一个库,订单 Order 一个库)。

垂直分库。png

垂直分表

垂直分表是针对列进行的。如果某个表的字段较多,可以把不常用的字段或者长度较长的字段拆分到一张新的扩展表中。在字段较多的情况下,通过“大表拆小表”,更有利于维护与开发,也能避免跨页问题(一致性、排序等问题)。MySQL 底层是通过数据页存储的,一条记录占用空间过大会导致跨页,造成额外的性能开销。另外数据库以行为单位将数据加载到内存中,这样表中字段长度较短且访问频率较高,内存能加载更多的数据,命中率更高,减少了磁盘 IO,从而提升了数据库性能。

垂直分表。png

支持分库分表的中间件

常见的有:

  • cobar
  • sharding-jdbc
  • mycat

cobar:阿里 b2b 团队开发和开源的,属于 proxy 层方案。早些年还可以用,但是近几年未更新。不支持读写分离、存储过程、跨库 join 和分页等操作。

sharding-jdbc:当当开源的,属于 client 层方案。SQL 语法支持比较多,没有太多限制,支持分库分表、读写分离、分布式 id 生成、柔性事务(最大努力送达型事务、TCC 事务)。

mycat:基于 cobar 改造的,属于 proxy 层方案,支持的功能非常完善,而且目前非常火且不断流行。

参考

  1. 面试小抄-分库分表
  2. 数据库分库分表思路
  3. MySQL 分库分表策略
  4. CS-Note