对分库分表的实战经验

背景

一个服务、一个系统运行的时间越长,瓶颈出现的点也会越多,大多数会集中在DB的瓶颈上, 起初我们可以针对性的做一些缓存, 但治标不治本, 最后我们不得不得去考虑对DB做分库分表。

什么是分库分表

分库分表是一个词,但他终究是三件事情:
第一个是分库,相当于把不同的表落在不同的库里,以提高整个服务的QPS上限, 如果你的服务存储容量不成问题,可以及时地归档删去数据,核心是连接以及QPS达到上限,这时候就考虑分库,不要考虑分表, 把之前一个库里的多个表拆到不同的库里。
第二个是分表,分表主要的问题是之前的这个表数据量过大、查询性能非常差, 一般以我们的经验一个表里出现上千万条数据,当然这个数据跟行的宽度也很大的关系,或者说这个数据的存储达到了一定量级, 我才会考虑去拆表。
第三个, 才是我们不得不做的分库分表,也就说这个库就QPS并发量高,数据量又大。

单表容量预估

我们知道DB的底层是B+树来做的索引引擎, 所以我们以此来分析, 数据存储的越多,树的层级就会越多,层级越多,IO次数就会越多,导致查询性的会下降。 所以我们一般建议树的层级设定在三到四层以内。

以此来估算,一个数据页是16 KB,树的每一个节点,对应一个数据页,包括跟节点、非叶子节点和叶子节点。 每一个非叶子节点存储着主键和指向叶子节点对应数据页的指针, 而叶子节点存储着实际的数据,每一个数据行会存储在一个数据页中。

所以能存多少条记录就=叶子节点的数量*每个叶子节点中能存的数量

叶子节点的数量=根节点之下的每一层非叶子节点的数量 ^ (树高-1)

所以我们只要知道非叶子节点能存储的节点数量、树高、以及叶子节点中能存的行数量,就可以估算错误,这个表最好能存储多少条记录。

已知一个节点存储量是16 KB, 作为非叶子节点, 存储的只有一个主键+指针, 我们假设主键是bigint的8字节数据和6字节的指针,那么一个非叶子节点 可以存储161024/(8+6)=1170个 二层节点, 所以我们假设B+树是三层, 但只会有两层的非叶子节点, 也就是会有11701170=1368900个叶子节点。

一个叶子节点的容量也是16 KB,假设我们存储的一行数据是1KB, 那么一个叶子的节点大概能存储16条数据。
所以理论上三层高度的B+树可存储的数据条数为1170117016=2000万条记录。

分表的方式

常见有两种分表方式,一种是横向拆分也叫水平拆分,一种是纵向拆分,也要垂直拆分。
横向拆分实际上就是把一个表里的数据放到多个表里, 纵向拆分就是把一个表结构拆成多个表结构。

分表我们一般是按照2的幂去做分表数量, 为什么呢?因为使用2的幂, 如果后续我们要做进一次的分表的时候,只需要迁移一半的数据就好, 比如说之前是取模4,现在改成取模8, 我们需要将之前分片的数据拿出来,按照取模8计算,只有大于4的时候我们才进行迁移。同样这样设计也可以大大降低数据迁移的难度。

对于分表数量要考虑三个因素:
第一个是存量数据。
第二个是业务预计的增量数据,还有你的业务数据要保存年限,与存量数据计算一个总和。
第三是你们的库里可以存储多少数量?可以按照上述的方式计算一下。
最后向上去二的幂向上去数字即可。

分表字段如何选择

这个没有确定性,根据你的实际业务慎重选择就行, 一般如果出现了非分表字段的查询,大概率是binlog同步靠ES去解决。
一定要选择好,避免出现数据倾斜。
至于分表算法的话,最常见的是取模, 因为他可以事先知道你有多少个库多少个表, 而且具有稳定性, 对于同一个ID,你能确定知道他在哪一个表里。
还有一个是按照关键字,比如说日期。
最后是hash, 几乎不怎么用。

分库分表带来的问题

第一个就是唯一ID, 单表的话靠自增主键就可以实现, 分表之后,自增主键就会出现冲突。
一般有两个解决方案:一个 靠UUID 雪花算法生成, 还有一个是靠redis的incr去做。

第二个就是你的所有读操作和写操作都要带着分表字段,因为他要知道要去哪个库哪张表去查询数据如何,不带的话默认就会全表扫描,性能极差, 所以在开发过程中可能要注意这些事情。

第三个就是事务不好处理了, 我们考虑要做一些分布式事物了。

第四个就是对于 非分表字段,以前的单表的查询:分页操作、排序操作都会失效。 不得不考虑靠ES实现了。

第五个就是如果分表之后表还是不够用,就不好处理了,所以既然要分表就一定要考虑好,提前多分点数据,该归档归档。 如果不得不重新分就要做数据迁移,很麻烦。

第六个就是可能会出现数据倾斜,单个分片性能出现瓶颈, 如果在业务上不好解决 就只能对单个分片,再做一次分库分表。 也很麻烦,尽量避免解决方案的话,要根据实际的业务出发。 前置在设计的时候一定要选择合适的分片键,例如用户ID就是一个很好的分片键。或者去考虑一些复合分片, 例如将户外ID地做一次取模分片,再将日期做一次取模分片。

第七个就是多表的join操作不好进行了, 一般得考虑在应用层去join了, 优点是灵活,但缺点了、数据量大,网络开销性能就会受到影响, 或者我们考虑ES去做了, 所以我们也得到一些结论, 如果做了分库分表后续就离不开ES了。

第八个就是对于模糊查询也得只能靠ES。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值