分库分表

185 阅读8分钟

前言

分库分表是数据库架构的一种最常见的解决方案。

发展历史

一个项目的发展历史
1.垂直分表
如果一开始数据太少,全部都是单机,不管是数据库,还是项目程序
1)单库,即所有的表放一个数据库。
2)碰到业务比较复杂的,字段比较多的,就放到附加信息表去,一般这样就够了。
这样做的好处是什么?关键要理解这一点,即背后为什么要这样做。如果所有的字段放同一个表,但是使用的时候并不是每次都要访问所有的数据,这个时候,好处就体现出来了,就是把单点变成多点,相当于每个表都分担了一部分请求连接压力。

而且,同样大的内存,可以放更多的记录数量,也就是请求连接数量,因为数据库是按一条记录来加载数据到内存的,每次请求的数据量更少,那么能够接受的连接数量就更多。

最重要的是,避免跨页,因为一条记录如果数据多大,可能会发生跨页,跨页查询导致速度慢。

2.垂直分库
如果表比较多,这个时候就要开始分库了
比如,支付系统,order订单库,account账户库。这样做的好处避免单点故障,以前是所有的表在一个机器,现在是不同的机器。

其次,单机接受请求能力和连接数量有限,多机自然更强大。

3.水平分区
原理 分表的一种,基于数据库厂商本身的分区功能。

实现
直接基于数据库本身的功能,不需要程序做其他处理,

4.水平分表
原理
其实就是多个表(物理表),只不过表名字是一样,另外,表名字的规则是:同样的表名字+序号。

实现
1)数据库表本身拆分为多个物理表
2)程序层面需要判断是哪一张表

更多的具体实现细节
待补充

垂直分表

垂直分库

分库之后带来的问题

跨库-join,跨库-分布式事务等。

水平分区

这里笔者推荐一个比较靠谱的过渡技术–“表分区”。主流的关系型数据库中基本都支持。不同的分区在逻辑上仍是一张表,但是物理上却是分开的,能在一定程度上提高查询性能,而且对应用程序透明,无需修改任何代码。笔者曾经负责优化过一个系统,主业务表有大约 8000W 左右的数据,考虑到成本问题,当时就是采用“表分区”来做的,效果比较明显,且系统运行的很稳定。


应用场景
分区适合数据不是太多,比如单表1000万使用索引,1亿分表,1000~1亿之间使用分区。


工作使用-支付系统
分区。因为程序层面没有任何改动。

分区

本质是什么? 逻辑上还是一个表,但是物理存储文件是多个文件。
优点是,磁盘io能力变强,因为现在是不同的文件读写。
分表的优点,是提高并发能力,因为单表的话,只要有锁,其他线程都不能访问。


表的逻辑划分? 表空间——块——page——记录。


表的物理存储划分? 一个表是一个物理存储文件。
一个分区,也是一个物理存储文件。


索引是基于物理,还是基于逻辑?
基于物理存储文件,每个物理存储文件都有自己的索引。


分区有索引吗?
有。每个分区有自己的独立分区索引。


参考
blog.51yip.com/mysql/1029.…

水平分表

分表

解决方案-merge分表
对代码透明。和分区一样的效果。其他解决方案不透明。


总表
1.总表
总表不是表
2.子表

分表比分区的优势在哪里?

因为分区是直接数据库厂商支持的功能,程序层面无需任何改动。而且,分区之后的每个区都有自己的索引(逻辑相同但是物理存储文件分开,且每个物理存储文件具有独立的索引,可以加快速度),分表之后也是自己的索引(逻辑分开且物理分开),那么分表和分区到底有什么区别呢?
分表,可以更好的在基于分表的基础之上做分库。
分区,不好分库,因为逻辑上仍然是同一个表,只是物理存储文件独立。


分区可以分库吗?
理论上可以,把分区物理存储文件放到各个机器上。但是,这只是把数据物理存储文件,放到了不同的机器。但是问题是,查询怎么办?索引怎么办?锁/同步怎么办?因为如果真的分库之后,就是跨库了,有很多问题都需要处理。

所以,分区,不建议分库。

水平分表且分库

就是在分表的基础之上,把不同的表不要放到一个机器上,而是放到多个机器上,机器越多,处理请求能力越强大,这也是互联网公司最常用的做法。


名词解释
基于分表的分库,又叫分片。分片这个词出现在很多场景描述中,1.比如 redis分片 2.数据库分片。其实,本质上是把单点变成多点。具体怎么变呢?看下面。

1.redis分片
redis的分片指的是一种负载均衡算法,即虚拟槽slot算法,总共1万个槽,每个节点平均分配一部分的槽,那么每个节点就是一个分片。

2.数据库分片
数据库也一样,就是数据太多了,那么就按时间维度分成多个表,分表之后每个表千万级别数据,分别存储在不同的机器节点上,那么这个时候每个节点就是一个分片。

最简单的理解,就是把每个分片当做一个机器节点。


工作使用-支付系统
没有分表,但是有分库,不过,它是把同一个库按时间维度分为新库和旧库(历史数据),比如,order库和order_history库。

带来的问题

1.跨分片的复杂查询,跨分片事务等。

2.挑战 虽然数据分片解决了性能、可用性以及单点备份恢复等问题,但分布式的架构在获得了收益的同时,也引入了新的问题。

面对如此散乱的分库分表之后的数据,应用开发工程师和数据库管理员对数据库的操作变得异常繁重就是其中的重要挑战之一。他们需要知道数据需要从哪个具体的数据库的分表中获取。

另一个挑战则是,能够正确的运行在单节点数据库中的SQL,在分片之后的数据库中并不一定能够正确运行。例如,分表导致表名称的修改,或者分页、排序、聚合分组等操作的不正确处理。

跨库事务也是分布式的数据库集群要面对的棘手事情。 合理采用分表,可以在降低单表数据量的情况下,尽量使用本地事务,善于使用同库不同表可有效避免分布式事务带来的麻烦。 在不能避免跨库事务的场景,有些业务仍然需要保持事务的一致性。 而基于XA的分布式事务由于在并发度高的场景中性能无法满足需要,并未被互联网巨头大规模使用,他们大多采用最终一致性的柔性事务代替强一致事务。

参考

www.infoq.cn/article/key…

www.infoq.cn/article/key… //实践

tech.meituan.com/2016/11/18/… //美团

dbaplus.cn/news-155-21…

juejin.cn/post/684490…

--- 其他 ---

join

基本使用方法

基于交集和并集示意图,非常好记。

1.交集
2.右表所有数据
交集 + 右表剩下数据
3.左表所有数据
交集 + 左表数据
4.并集,即所有数据
左表剩下数据 + 交集 + 右表剩下数据

注意:
1.内连接inner
只有交集是内连接 2.外连接outer
其他都是外连接,内连接必须显式写inner关键字,而外连接不需要显式写,因为默认就是外连接。

跨库问题

跨库的问题,是多表不在同一个库/机器。和join本身没有太多的关系,因为and也是一样跨库,如果是跨库的话。

分页查询

一般的分页查询

大数据情况下如何做分页?

可以参考阿里巴巴java开发手册上的答案

分表分库-解决方案

sharding-jdbc

mycat

merge分表

in和exist区别?

区别和本质原因
1.in
只查询一次数据库,然后缓存起来,后面都是基于内存。
因为基于内存,是遍历所有数据,所以适合数据少的情况。

2.exist
基于磁盘索引,每次都会查询数据库。
因为基于索引,所以适合数据量大的情况。


总结
由于in经常是在内表里出现,所以:
1.in
适合内表数据少,外表数据多的情况。
2.exist
适合内表数据多,外表数据少的情况。


代码

select count(1) from orders o where o.user_id in(select u.id from users u);
select count(1) from orders o where exists (select 1 from users u where u.id = o.user_id);