数据库扩展解决了什么问题?
- 热备份,多活,故障切换
- 负载均衡、读写分离
分库分表的目标
分库,就是把一个数据库拆分成多个数据库;分表,就是把一张表拆成多张表。
垂直拆分
如果你有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)。
create table `order`(
...
)
partition by key()
partitions 16;
这种方式很简单,通过主键将订单表分成16份。但是这样的方式不解决两个重要的问题:
- 网络I/O的瓶颈
- 磁盘存储的瓶颈
我们在实战当中通常不考虑用Mysql默认的Partition,我们实战当中,会用到ShadingSpere工具进行分库分表。
上面的分法还产生一个麻烦的问题,如果真用id%16分表。那么对于相同用户id的订单,就可能被分到不同的表当中。这对于查询是一个很大的压力。
例如:
select * from `order` where user_id = 1000;
这条sql查询了,用户1000的所有订单,而订单已经被分布到16张表中了。所以有一种可能,需要一张一张订单表的查询,然后再汇总结果。
结论是,如果有这样的查询需求,那么订单表,就应该根据user_id
分区。
根据范围拆分
那么如果根据时间范围(created
字段)拆分订单表呢?比如2014年,2015年,2016年,2017年……2021年,2022年……?
这样会有什么优势吗?
决定是否这样拆分,那就要以数据说话。你要通过历史记录分析,看一看到底是根据用户id查询订单多,还是通过时间查询订单多。
核心逻辑是,我们不希望一次查询落到多个分区。
那么如果时间真的非常重要,而且用户id也非常重要,以时间和用户ID一起分区可不可以呢?当然这是可以的。
你可以把分区想象成一个函数,输入的是一行数据,返回的是一个数字。