数据库扩展解决了什么问题?

  1. 热备份,多活,故障切换
  2. 负载均衡、读写分离

分库分表的目标

分库,就是把一个数据库拆分成多个数据库;分表,就是把一张表拆成多张表。

垂直拆分

如果你有10张表,拆成2两个库,每个库5个表,这样的操作叫做垂直分库。因此垂直拆分,是业务导向的。比如你要把一个数据库拆分成交易数据库和商品库。
垂直分表也是如此,比如你要把一张表中若干的列拆分成另一张表。
你要把一个订单表拆分成订单的主要信息表,和备注表,还有额外信息表,这就是垂直拆分。

水平拆分

那么什么是水平拆分呢?
水平拆分是一个空间的拆分概念。我们通常也称之为partition或sharding。
我们将一张表,在不改变结构的情况下,将一部分行放入表1,另一部分行放入表2,依次类推——这就是水平拆分。

partition

partition翻译过来是分区,磁盘的分区,我们经常称为disk partition。Mysql是行存储,所以Mysql表分区后,被分成了若干个相同结构的表。例如一个订单表1亿条数据,表结构不变,分成10个订单表,每个1000W行数据。

sharding

sharding翻译过来是分片。在Google著名的Google File System论文中,分布的存储片段,就被称为分片。你可以思考一下,这两个相似的单词,隐含着不同的含义:sharding更倾向于数据被分布式存储

如果仅仅是表进行水平拆分, 库不拆分行不行呢?

当然不行!!
原因很简单,只拆表不拆库。那所有的表还在这一个库里,而这一个库的数据,都存在于一台机器上,这样会有几个压力扛不住:

  • 存储空间会不足
  • 网络压力会很大
  • 单机的CPU可能会忙不过来
  • 数据冗余数量可能会不足,当然你可以做热备份

如何水平拆分

请你思考,水平拆分,还是垂直拆分难。垂直拆分,需要根据业务去思考,难度不在技术上。水平拆分,难度在于技术上。
水平拆分有多种选择,接下来为你介绍下:

根据ID % N

比如你要把订单表,拆分成16张表;你就要把订单表中的每一条数据都拷贝到一张新表里。在Mysql中,你可以通过一条SQL语句进行分区(Partition)。

  1. create table `order`(
  2. ...
  3. )
  4. partition by key()
  5. partitions 16;

这种方式很简单,通过主键将订单表分成16份。但是这样的方式不解决两个重要的问题:

  • 网络I/O的瓶颈
  • 磁盘存储的瓶颈

我们在实战当中通常不考虑用Mysql默认的Partition,我们实战当中,会用到ShadingSpere工具进行分库分表。
上面的分法还产生一个麻烦的问题,如果真用id%16分表。那么对于相同用户id的订单,就可能被分到不同的表当中。这对于查询是一个很大的压力。
例如:

  1. select * from `order` where user_id = 1000;

这条sql查询了,用户1000的所有订单,而订单已经被分布到16张表中了。所以有一种可能,需要一张一张订单表的查询,然后再汇总结果。
结论是,如果有这样的查询需求,那么订单表,就应该根据user_id分区。

根据范围拆分

那么如果根据时间范围(created字段)拆分订单表呢?比如2014年,2015年,2016年,2017年……2021年,2022年……?
这样会有什么优势吗?
决定是否这样拆分,那就要以数据说话。你要通过历史记录分析,看一看到底是根据用户id查询订单多,还是通过时间查询订单多。
核心逻辑是,我们不希望一次查询落到多个分区。
那么如果时间真的非常重要,而且用户id也非常重要,以时间和用户ID一起分区可不可以呢?当然这是可以的。
你可以把分区想象成一个函数,输入的是一行数据,返回的是一个数字。