1、什么是分库分表
  1. 什么是分库分表?什么是垂直分表?什么是水平拆分?什么是Sharding?
  2. 分库分表是为了解决什么问题?
  3. 分库分表有什么优点?
  4. 分库分表有什么策略

分库分表的基本思想就是:把原本完整的数据拆分成多个部分。放在不同的数据库或表上。分库分表一定是为了支撑高并发、数据量大两个问题的。
垂直拆分

垂直拆分,是把一个有很多字段的表给拆分成多个表,或者是多个库上。一般来说,会将较少的、访问频率较高的字段放在一个表中,然后将较多的、访问频率低的字段放到另外一个表中。因为数据库是有缓存的,访问频率较高的行字段越少,就可以在缓存里缓存更多的行,性能就越好。这个一般在表层面做的较多一些

image-20200114211639899.png
一般情况下满足以下条件就可以考虑扩容了:

  • MySQL 单库超过 5000 万条记录,Oracle 单库超过 1 亿条记录,DB压力就很大。
  • 单库超过每秒 2000 个并发时,而一个健康的单库最好保持在每秒 1000 个并发左右,不要太大。

在数据库的层面使用垂直切分将按数据库中表的密集程度部署到不同的库中,例如将原来的电商数据库垂直切分成商品数据库、用户数据库等。
水平拆分

水平拆分又称Sharding,它是将同一个表中的记录拆分到多个结构相同的表中,当单表数据量太大时,会极大影响SQL 执行的性能。分表是将原来一张表的数据分布到数据库集群的不同节点上,从而缓解单点的压力。

image-20200114211203589.png
一般来说,单表有200万条数据的时候,性能就会相对差一些,需要考虑分表了。但是这也要视集体情况而定,可能是 100万条,也有可能是500万条,SQL越复杂,就最好让单表数据行数越小。


分库分表前 分库分表后
并发支持情况 单机部署,扛不住高并发 从单机到多机,能承受的并发增加多倍
磁盘使用情况 单机磁盘容量几乎撑满 拆分为多库,数据库服务器磁盘使用率大大降低
SQL执行性能 单表数据量太大,SQL 越跑越慢 单表数据量减少,SQL 执行效率明显提升

分库分表策略

  • 哈希取模:hash(key) % N 或 id % N
    • 优点:可以平均分配每个库的数据量和请求压力(负载均衡)。
    • 缺点:扩容麻烦,需要数据迁移。
  • 范围:可以按照ID或时间划分范围。
    • 优点:扩容简单。
    • 缺点:这种策略容易产生热点问题。
  • 映射表:使用单独的一个数据库来存储映射关系。(不建议使用)
    • 优点:扩容简单,可以解决分布式ID问题。
    • 缺点:存储映射关系的数据库也可能成为性能瓶颈,且一旦宕机,分库分表的数据就无法工作。

2、分库分表中间键
  1. 你用过哪些分库分表中间件,简单介绍一下?
  2. 不同的分库分表中间件各自有什么特性,有什么优缺点?
  3. 分库分表中间件技术如何选型?

常见的分库分表中间件

  • Cobar(opens new window)- 阿里 b2b 团队开发和开源的,属于 proxy 层方案,就是介于应用服务器和数据库服务器之间。应用程序通过 JDBC 驱动访问 cobar 集群,cobar 根据 SQL 和分库规则对 SQL 做分解,然后分发到 MySQL 集群不同的数据库实例上执行。早些年还可以用,但是最近几年都没更新了,基本没啥人用,差不多算是被抛弃的状态吧。而且不支持读写分离、存储过程、跨库 join 和分页等操作。
  • TDDL(opens new window)- 淘宝团队开发的,属于 client 层方案。支持基本的 crud 语法和读写分离,但不支持 join、多表查询等语法。目前使用的也不多,因为还依赖淘宝的 diamond 配置管理系统。
  • Atlas(opens new window)- 360 开源的,属于 proxy 层方案,以前是有一些公司在用的,但是确实有一个很大的问题就是社区最新的维护都在 5 年前了。所以,现在用的公司基本也很少了。
  • sharding-jdbc(opens new window)- 当当开源的,属于 client 层方案。确实之前用的还比较多一些,因为 SQL 语法支持也比较多,没有太多限制,而且目前推出到了 2.0 版本,支持分库分表、读写分离、分布式 id 生成、柔性事务(最大努力送达型事务、TCC 事务)。而且确实之前使用的公司会比较多一些(这个在官网有登记使用的公司,可以看到从 2017 年一直到现在,是有不少公司在用的),目前社区也还一直在开发和维护,还算是比较活跃,个人认为算是一个现在也可以选择的方案。
  • Mycat(opens new window)- 基于 cobar 改造的,属于 proxy 层方案,支持的功能非常完善,而且目前应该是非常火的而且不断流行的数据库中间件,社区很活跃,也有一些公司开始在用了。但是确实相比于 sharding jdbc 来说,年轻一些,经历的锤炼少一些。

分库分表中间件技术选型
建议使用的是 sharding-jdbc 和 mycat。

  • sharding-jdbc(opens new window)这种 client 层方案的优点在于不用部署,运维成本低,不需要代理层的二次转发请求,性能很高,但是如果遇到升级啥的需要各个系统都重新升级版本再发布,各个系统都需要耦合 sharding-jdbc 的依赖。其本质上通过配置多数据源,然后根据设定的分库分表策略,计算路由,将请求发送到计算得到的节点上。
  • Mycat(opens new window)这种 proxy 层方案的缺点在于需要部署,自己运维一套中间件,运维成本高,但是好处在于对于各个项目是透明的,如果遇到升级之类的都是自己中间件那里搞就行了。

通常来说,这两个方案其实都可以选用,但是我个人建议中小型公司选用 sharding-jdbc,client 层方案轻便,而且维护成本低,不需要额外增派人手,而且中小型公司系统复杂度会低一些,项目也没那么多;但是中大型公司最好还是选用 mycat 这类 proxy 层方案,因为可能大公司系统和项目非常多,团队很大,人员充足,那么最好是专门弄个人来研究和维护 mycat,然后大量项目直接透明使用即可。

3、分库分表的问题
  1. 分库分表常见问题有哪些?
  2. 如何解决分库分表中的问题?

分布式事务
方案一:使用数据库事务

  • 优点:交由数据库管理,简单有效
  • 缺点:性能代价高,特别是 shard越来越多时

方案二:由应用程序和数据库共同控制

  • 优点:性能上有优势
  • 缺点:需要应用程序在事务控制上做灵活设计。如果使用了 spring 的事务管理,改动会面临一定困难。
  • 原理:将一个跨多个数据库的分布式事务分拆成多个仅处于单个数据库上面的小事务,通过应用程序总控各个小事务。

跨节点Join
只要进行切分,跨节点 join 的问题是不可避免的,但是良好的设计和切分却可以减少此类问题的发生。解决这以问题的普遍做法是分两次查询实现,在第一次查询的结果集中找出关联数据的id,根据这些id 发起第二次请求得到关联数据。

跨节点的 count、order by、group by 以及聚合函数
这些是一类问题,因为它们都需要基于全部数据集合进行计算。多数的代理都不会自动处理合并工作。
解决方案:与解决跨节点 join 问题的类似,分别在各个节点上得到结果后在应用程序端进行合并。和 join 不同的是每个节点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。但如果结果集很大,对应用程序内存的消耗是一个问题。
业务角度上的解决方案:

  • 如果是在前台应用提供分页,则限定用户只能看前面 n 页,这个限制在业务上也是合理的,一般看后面的分页意义不大(如果一定要看,可以要求用户缩小范围重新查询)。
  • 如果是后台批处理任务要求分批获取数据,则可以加大 page size,比如每次获取 5000 条记录,有效减少分页数(当然离线访问一般走备库,避免冲击主库)。
  • 分库设计时,一般还有配套大数据平台汇总所有分库的记录,有些分页查询可以考虑走大数据平台。

分布式ID
一旦数据库被切分到多个物理节点上,我们将不能再依赖数据库自身的主键生成机制。一方面,某个分区数据库自生成的 ID 无法保证在全局上是唯一的;另一方面,应用程序在插入数据之前需要先获得 ID,以便进行 SQL 路由。
一些常见的主键生成策略:

  • 使用全局唯一 ID:GUID。
  • 为每个分片指定一个 ID 范围。
  • 分布式 ID 生成器 (如 Twitter 的 Snowflake 算法)。

数据迁移、容量规划、扩容等问题
来自淘宝综合业务平台团队,它利用对 2 的倍数取余具有向前兼容的特性(如对 4 取余得 1 的数对 2 取余也是 1)来分配数据,避免了行级别的数据迁移,但是依然需要进行表级别的迁移,同时对扩容规模和分表数量都有限制。总得来说,这些方案都不是十分的理想,多多少少都存在一些缺点,这也从一个侧面反映出了 Sharding 扩容的难度。