分库分表解决300亿记录存储的三个方案方法

分库分表解决300亿记录存储的三个方案方法

分库分表解决什么问题?

  • 分表:单表记录急剧增加,单表物理存储或单表查询遇到瓶颈;
  • 分库:上游服务增多,单库的连接数遇到瓶颈,此时需要考虑分库,分库后吞吐量将成倍增长;

场景

我们看下在线酒店预订的一个场景。
据统计全国有酒店30万家,客房总数1347万间,按照每个酒店有10个售卖房型(标准间、大床房、双床房…)算就有300万个售卖房型。
我们需要存储每个房型未来365天的房价、房量共20亿的数据,这只是一个分销商的接入接入记录。
假设我们接入了15分销商,房价、房量的记录就达到了300亿。
梳理下已知的数据,酒店数据30万,房价数据150亿,房量数据150亿;
下面探讨下这300亿的记录如何分库分表存储。

300亿记录怎么存?

酒店记录在百万级,所以一张表就可以存储;
房价记录150亿,所以需要考虑分库分表存储,我们使用16个库每个库16张表(共256张表)进行存储,每个表大约存0.58亿记录(硬件好加上读写分离一般无压力);
房量记录150亿,存储同上也为 16*16 分库分表;
小结:数据库分为三类酒店、房价、房量库,酒店库1x1,房价库16x16,房量库16x16;

数据路由?

考虑相同酒店ID的信息需要落到同个分片中,我们可以使用hash取模的方式;
这里使用我们使用BKDRHash算法,该算法简单高效(java字符串hash也使用这个算法);
比如存储一个酒店的价格信息:通过hashHotel(酒店ID)%16得到DB索引,在hashPrice(酒店ID)%16得到DB中价格表的索引;
应该会发现一个问题,就是用相同酒店ID去做hash取模,索引会一直是同一个!!!
这个问题解决就要用到BKDRHash算法的一个特性了,算法中可以加入一个因子,比如酒店片区的因子为131,而价格hash函数的因子为31。
这样就算使用同一个酒店ID也会产生不同的hash结果。
可以参考下面的code:
‘’’
private int patitionCount = 16;
//酒店片区hash函数
private int getBKDRHashByHotel(String key) {
int seed = 131; //因子取值:31, 1313, 13131, 131313, …
int hash = 0;
for (int i = 0; i < key.length(); i++) {
hash = (hash * seed) + key.charAt(i);
}
return hash;
}
//价格片区hash函数
private int getBKDRHashByPrice(String key) {
int seed = 31; //因子取值:31, 1313, 13131, 131313, …
int hash = 0;
for (int i = 0; i < key.length(); i++) {
hash = (hash * seed) + key.charAt(i);
}
return hash;
}
‘’’

其它路由方案

基因因子分片

基因分库法: 使用基因的思想,从一个维度的信息里,摘取了一个分库基因,其他维度信息里也全会带上,使得所有维度的信息都能通过此分库基因完成分库.
关于分库方法,参考理论:
He meant that taking number mod 2^n is equivalent to stripping off all but the n lowest-order (right-most) bits of number.
即: 一个数取余2的n次方,那么余数就是这个数的二进制的最后n位数。所有我们可以位操作符把高位清零就可以得到余数.
优点:通过ID即可分析所属片区,保证数据落到同一个分片中;
不足:需要提前规定酒店ID、价格ID、房量ID的主键;

一致性Hash分片

建议使用这个算法,可以保证原有ID的命名规范,同时通过虚拟节点的加入避免了数据倾斜问题;
在BKDRHash算法上简单修改可以实现虚拟节点的加入;
也可以网上找个现成的算法改下;

关于扩容

数据库拆分一般是业务发展到一定规模后的优化和重构,为了支持业务快速上线,很难一开始就分库分表,垂直拆分还好办,改改数据源就搞定了,一旦开始水平拆分,数据清洗就是个大问题,为此,我们经历了以下几个阶段。

第一阶段

  • 数据库双写(事务成功以老模型为准),查询走老模型。
  • 每日job数据对账(通过DW),并将差异补平。
  • 通过job导历史数据。

第二阶段

  • 历史数据导入完毕并且数据对账无误。
  • 依然是数据库双写,但是事务成功与否以新模型为准,在线查询切新模型。
  • 每日job数据对账,将差异补平。

第三阶段

  • 老模型不再同步写入,仅当订单有终态时才会异步补上。
  • 此阶段只有离线数据依然依赖老的模型,并且下游的依赖非常多,待DW改造完就可以完全废除老模型了。

总结

以上是对300亿数据分库分表的一种实现方案。为了保证高可用还需要进行读写分离,同时做好监控体系,包括但不限于:服务器的磁盘容量、磁盘IO、CPU负载;
主从同步延时、慢查询、db缓存刷新频率等指标。
阿里巴巴《Java 开发手册》提出单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。
不同硬件环境下对于分库分表的阈值也有差异,主要还是需要看DB缓存、服务器的磁盘IO。
服务器磁盘IO良好的情况下,单表1亿查询也是没问题的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值