MySQl分表分库

一、综述

1、需求分析

随着数据量的增长,MySQL会有一系列问题,具体说来如下:

(1)容量问题
单库包含多张表,部分单表容量过亿,磁盘存储不足

(2)性能问题
单库处理能力有限,受制于连接池及机器资源等问题,TPS无法继续提升

(3)扩展问题
单一主库无法灵活的进行升级和扩展,只能通过挂多个读库的方式进行扩展。
读库本身只能解决读的业务场景;且读库需要通过主库进行数据同步,如果数量过多主库压力也会增大。

2、垂直拆分、水平拆分

在不考虑使用SSD硬盘等方式的情况下,垂直拆分水平拆分是电商比较通用的方式

(1)垂直拆分
<1>不同的业务拆分到多个子库
<2>对某个表,按照使用频率及业务关联度拆分为多张表

(2)水平拆分
水平拆分是把单表按某个规则把数据分散到多个表的拆分方式
常见规则有区间、取模Hash、其它Hash等。

3、关键点

(1)事务问题
不管是垂直拆分还是水平拆分,可能导致原先的数据库事务失效,需要对事物进行额外的改造。由于2PC、3PC对性能的消耗较大,电商一般通过最终一致性策略来确保数据的完整。

(2)查询问题
不使用分片的关键字进行查询,查询结果可能会分散在各个分库中,需要合并汇总返回。
<1>排序分页
尽量不支持,需要合并所有数据进行排序后再分页,性能太低
<2>跨分片join
需要做sql改造,或者引入全局表进行处理

(3)单表容量
分表的目标是保证每个数据表的数量保持在1000~5000万左右,在这个量级下,数据表的大小与性能是最理想的。

(4)主键生成
无法直接使用MySQL自增特性来生成主键ID,需要有统一的主键生成策略,有以下几种主键生成策略:
<1>Twitter的Snowflake
64位唯一Id(由41位的timestamp+ 10位自定义的机器码+ 13位累加计数器组成)
<2>UUID/GUID(一般应用程序和数据库均支持)
<3>MongoDB ObjectID(类似UUID的方式)
<4>Ticket Server(数据库生存方式,Flickr采用的就是这种方式)

(5)分片关键字
对于多个查询的关键字,只能按照一个关键字进行分表,如果按照另外的关键字去查询,则不知道是在哪个分片中。此时需引入索引表,建立关键字之间的索引关联,在不采用分片关键字进行查询时,先找出分片关键字进行查询。
<1>连续分片
区间分片。
可以快速定位分片进行高效查询,大多数情况下可以有效避免跨分片查询的问题。后期如果想对整个分片集群扩容时,只需要添加节点即可,无需对其他分片的数据进行迁移。容易出现数据热点问题,某一个库的压力过大。
<2>随机分片
常见的有Hash等。
随机分片的数据相对比较均匀,不容易出现热点和并发访问的瓶颈。但是,后期分片集群扩容起来需要进行数据迁移。

(6)机器扩容问题
动态增加机器,随机分片的情况下需要对原有数据做再次迁移。应当尽量在前面做好数据规划。

4、开源组件

(1)Sharding-JDBC
当当网:https://github.com/dangdangdotcom/sharding-jdbc
基于应用程序层面的DDAL(分布式数据库访问层)

(2)MyCat
http://mycat.io/
独立部署,对应用无侵入,其它的还有基于Go语言实现kingSharding,比较老牌的Atlas(由360开源)等。

二、行业案例

1、唯品会-订单系统

http://www.infoq.com/cn/articles/summary-and-key-steps-of-vip-orders-depots-table

(1)拆分策略
<1>按照用户id进行取模Hash算法,分成n个数据库、m组服务器。每组服务器存放四个数据库实例,即n=4*m。
<2>建立订单号与用户id的索引关系表。该表也做水平拆分,按照订单号进行Hash取模,策略同上。解决了按照orderId进行查询的操作,当请求到来时先从索引表中通过orderId查到分片关键字userId,再去查询订单。
<3>索引表放到集中缓存中,提升性能。

(2)无分片关键字的查询
其他条件的查询、统计操作引入分布式数据库中间件。

2、大众点评-订单系统

https://tech.meituan.com/dianping_order_db_sharding.html

(1)数据库拆分
按照业务特征拆分为多维度的MySQL集群,集群之间的数据通过PUMA进行同步。PUMA是大众点评内部开发的MySQL binlog实时解析服务。
这里写图片描述
(2)分片关键字
一般为用户id和商户id。

(3)分片策略
取模Hash进行切分进行分表分库。
按照用户id分片的库,包括32个数据库、32张表,数据库集群为8个,每个集群4个数据库。
按照商户id分片的库,包括8个数据库,8张表。

(4)扩容
<1>可以直接扩展为32个数据库集群
<2>如果32个集群也无法满足需求,那么将分库分表规则调整为(32*2^n)*(32/2^n),可以达到最多1024个集群
这里写图片描述
(5)主键生成
时间戳+用户标识码+随机数

三、时间价格表分表方案

1、mysql分片关键字及部署

(1)mysql容量
我们采用8个数据库,每个数据库8张表。假使每张表的容量极限为3000w,最大支持的数据量可达到64*3000w,即19.2亿数据量,初步预估能够满足。

(2)分片关键字
时间价格表中的商品id作为分片关键字。

(3)分片策略
取模Hash
采用8库8表,数据库路由:suppGoodsId%{库数量},表路由suppGoods/{库数量}%{表数量}
2台服务器,每台4个数据库,后期压力大可以挂主从。

(4)扩容方式

情景一、数据库性能问题瓶颈
2台服务器直接变为8台服务器,每个服务器1个数据库。
更新分表规则为(8*2^n)*(8/2^n),最大可分为64个数据库,这种情况下需要做数据迁移及数据清洗。

情景二、单表容量过大
需要再次进行切分,以2库2表为例,需要对表再次进行切分,切分为2库4表
这里写图片描述

2、数据初始化

使用取模算法进行分库
(1)原始表是mysql
<1>mysql先挂数据库主从
<2>停止主库写服务,等待从库全部完成
<3>更新分表路由规则后重启应用
<4>开启主库写服务
<5>关闭主从同步
<6>删除各个分片数据库中的冗余数据

(2)原始表是oracle
<1>mysql部署主从集群
<2>OGG数据同步至mysql主库
<3>停止oracle表的写服务,等待mysql主从数据库完成数据同步
<4>更新分表路由规则后重启应用
<5>关闭mysql集群的主从同步
<6>删除mysql各个分片数据库中的冗余数据
<7>删除oracle表中的原始数据

3、主键生成

(1)默认
类名称:com.dangdang.ddframe.rdb.sharding.keygen.DefaultKeyGenerator
该生成器采用snowflake算法实现,生成的数据为64bit的long型数据。 在数据库中应该用大于等于64bit的数字类型的字段来保存该值,比如在MySQL中应该使用BIGINT。
其二进制表示形式包含四部分,从高位到低位分表为:1bit符号位(为0),41bit时间位,10bit工作进程位,12bit序列位。
(2)包装使用
workerId的设置:先从配置文件中配置,如果没有采用IP后10位

4、柔性事务

这里写图片描述

5、监控

Druid连接池监控

6、使用sharding-jdbc注意点

(1)Spring中配置的分片列及逻辑表名,需要完全和mybatis mapper对应,大小写必须相同。
(2)在insert操作中,必须携带sharing分片的字段值。
(3)hintManager实现了AutoCloseable接口,可推荐使用try with resource自动关闭。
(4)sql语法支持程度
http://dangdangdotcom.github.io/sharding-jdbc/01-start/sql-supported/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值