分库分表常见面试问题
- 1、什么是分库分表
-
- [1.1 分表和分区的区别](#1.1 分表和分区的区别)
- 2、怎么分库分表
-
- [2.1 到底分多少库,分多少表合适](#2.1 到底分多少库,分多少表合适)
- 3、分表字段选择
- 4、分表算法
- 5、全局ID
- 6、分库分表会带来什么问题
- 7、分库分表之后,表还不够怎么办
- 8、为什么喜欢使用2的次幂进行分库分表
- 9、数据倾斜会带来什么问题,如何解决
- 10、分库分表之后怎么进行join操作
如有侵权,请联系~
如有错误,也欢迎批评指正~
1、什么是分库分表
分库指的是将数据分布在多个数据库实例中。每个数据库实例只存储部分数据,通常是通过某种分片策略(如哈希、范围等)来决定数据的分布。
分库解决的问题是: 并发量大的问题。随着并发量的增加,数据库的连接池可能就会打满,成为瓶颈。虽然数据库的连接池可以修改,但也不是可以无限的增加。可以通过增加数据库的实例以此来提供更多的数据库连接,从而提升系统的并发度。
分表是指将一个数据库中的表拆分成多个表,以减少单个表的数据量,从而提高查询性能。可以在同一个数据库中分表,也可以在多个数据库中分表。
分表解决的问题是: 单表太大影响存储和查询的效率问题。数据库是使用B+树进行存储的,一般的B+树是3-4层,如果太多就会影响查询效率,增加磁盘的IO次数,增加耗时。
分表时机: 单表记录不能超过2000万或者单表大小不能超过2G。【阿里规范:单表不能超过500万】(可以根据页大小16K,三层计算得出)。并不是说只有满足这个条件才开始分表,而是预计未来你的流量肯定会超过这个,一开始就要分表。例如用户表、日志表、订单表。
1.1 分表和分区的区别
数据过大的时候,我们不仅仅可以分表,也可以选择分区。表存储主要是依赖两个文件:.frm文件【存储表结构定义】和.ibd文件【存储数据】。
分区之后表面上还是一张表【同一个.frm文件,.ibd文件不同,存储文件如下形式:order.frm、order_p1.ibd、order_p2.ibd】,数据库自己会组织各个分区。分区不宜太多,因为数据库启动的时候会加载所有的分区文件,如果太多超过机器所能允许打开的最大文件数量,则会报错;
而分表之后就是不同的表【.frm文件和.ibd文件均不同,存储文件如下形式:order_1.frm、order_p1.ibd、order_1.frm、order_p2.ibd】,操作数据库的时候就要去指定表。
2、怎么分库分表
拆分方式主要有两种:横向拆分和纵向拆分。
横向拆分: 将所有的数据记录拆分到多张表中,使得每个表中的数据数量都有所降低。
纵向拆分: 将每行记录中的部分字段上的数据拆分到其他表中。这种每个表中的数据记录数量不会变化,只是存储降低了。一般是一条记录中的某些字段比较常用,有些字段不常用,将不常用的字段拆分出去。
2.1 到底分多少库,分多少表合适
按照单表能够存储的最大容量2000万计算:
分表数 = 预计最大的容量 / 2000万 ->2的次幂。即,最大容量/2000万得到的数向上取整得到最接近的2的次幂
分库数主要是根据并发量来计算,可以按照如下预估:
分库数 = 分表数 / 8
3、分表字段选择
分表字段的选择肯定是结合业务情况进行选择。例如订单需要分库分表,一般是按照买家id作为分表字段。因为如果使用卖家id作为分表字段,可能会存在数据倾斜,因为有些头部的卖家会有大量的订单,并且买家查询也会比较多。
如果卖家查询怎么办?
- 基于binlog进行实时的同步一个以卖家id为维度的分表【只用来查询,不进行DML操作】。如果不想占用这么大的空间,卖家表可以只存储买家id和卖家id,通过卖家id查询到买家id,然后再进行利用买家表进行查询【如果通过卖家查询的QPS不大,并且数据量没那么大,是不是不需要分库分表】。
- 将卖家id和买家id存储在缓存,如redis中
- 将表同步到ES中,或者是同步到分布式数据库中,如HBASE
- 基因算法。例如虽然按照买家id进行分库分表,可以将买家id分库分表的数据拼接到订单id上【如订单id-买家id】,这样进行订单查询的时候也可以利用后面的买家id进行分库分表,知道去哪个表中查询。
4、分表算法
- 直接对字段进行取模。如直接对userid进行取模
- hash取模。如果分表字段不是数字类型,需要先对字段求hash,再取模。如name
- 使用关键字。如按照创建时间进行分库分表
- 一致性Hash。为了解决二次扩容问题,降低数据迁移成本。
5、全局ID
对数据进行了分库分表,每张表都有自己的主键,不同表之间就会有相同的主键。如果想所有的记录都具有全局唯一性,则需要增加一个字段,全局ID。全局ID的生成方式:
- UUID算法
- 雪花算法、Leaf算法
- 使用一张表专门用来生成全局ID,每个记录都去这张表中获取全局ID
- 使用redis等存储全局ID,每个记录都去redis中获取全局ID,redis自增incr
6、分库分表会带来什么问题
- 每次查询都需带着分表字段,否则将会对所有库和表进行扫描
- 数据库事务失效,必须使用分布式数据库
- 针对于部分分页查询或者是排序将会失效
7、分库分表之后,表还不够怎么办
我们在第一次分库分表的时候尽量避免这种情况的发生。如果业务突然暴增,确实出现了不够的情况,怎么办?
- 可以将历史的数据进行归档;
- 二次分表。二次分表肯定涉及到数据迁移,怎么可以减少二次分表带来的成本【数据迁移】:
- 按照2的次幂进行分库分表
- 一致性hash
8、为什么喜欢使用2的次幂进行分库分表
- 在进行对分表字段【或者是hash之后的分表字段】取余的时候,可以将取余%操作转换为与操作【如,&(01111)】,提高计算效率
- 二次分表的时候数据迁移少,成本低
- 在进行分库分表的时候每个数据库分配到的表格比较均匀
9、数据倾斜会带来什么问题,如何解决
数据倾斜是指在分库分表的时候,由于分表算法或者分表字段选择不合理等原因出现了数据分配不均匀,某些节点的数据比其他节点多。这会导致:
- 机器资源使用不均衡
- 某个节点的性能出现瓶颈
- 查询性能下降
- 影响其他业务查询:这个大表中其他的少量数据查询也会变得很慢
怎么预防出现数据倾斜?
- 选择合适的分表字段
- 使用复合分片,结合多个分片字段进行分片
- 虚拟分片。先使用分片算法分成多个虚拟的分片,然后虚拟的分片再进行映射到时机的物理分片上【物理分片数量远远小于虚拟的分片数量】
如果出现了数据倾斜,那怎么解决呢?
- 二次分表。将这部分数据在进行拆分
- 将这个数据倾斜严重的数据隔离出来单独使用一个数据库进行存储
10、分库分表之后怎么进行join操作
- 应用层处理。所有的数据都读取到应用层,然后程序进行过滤操作
- 数据库中间件,类似于数据库的一层代理,如MyCat数据库中间件
- 在设计数据库的时候允许一些冗余字段,以减少join操作
- 将数据同步到ES等搜索引擎中,ES进行聚合,进行查询