首先,接着上篇博文:Mysql千万数据级分表设计及实现方案已经分析了自增id作分表key和全局服务id(16位)作分表key进行分表的两种设计方案。自增id优势在于简单,直接哈希取模即可分表完成。根据全局服务生成的16位用户id(或订单id)则需关注分表键生成策略,根据生成策略确定取模算法,保证数据尽量均匀分布。另外采取uid还得关注热点用户导致的数据热点问题,若出现热点问题,可针对热点数据做缓存处理,减少某几张表的数据存取压力。
下面根据项目实践来进一步总结分析分库分表设计及落地方案。
以库中最大数据量作考量,分析如下:
1、交易账单表当前数据量:8kw
2、2017年增长:8kw
根据dba建议考虑分表后单表数据量不超过1kw估算,8kw/16=0.5kw *2年=1kw,若分成16张表可基本满足两年的数据增长。
在选择分表键,有两个点需要重点关注:
1、采用外部键作为分表键
因为一般查询业务都是根据订单id,用户id等具备业务属性key调用交易,选择这些key的好处在于业务线直接传入分表键,无需二次映射操作,直接路由即可。而劣势在于,键的生成策略外部控制,与业务线强耦合,且类型等受制。
对于公司如果提供全局的id生成,且各系统均采取该方式维护,则可以采取外部键。
2、选择自身生成id(pay_id)为分表键
业务线查询业务key(b_U_id|order_id),此时业务线对pay_id是无感知的,与交易交互仅通过业务线id或订单id,则需要:
方案1:考虑先查出分表key后路由(方案:查取缓存、或创建bid-pay_id的映射表)
方案2:根据业务线传入id做一次查询。每次操作多余一次查询,且查询分表在无分表key情况下,依次查。存在效率问题
最终选取2中方案1,建立业务表与分表键的映射表,且映射表同样进行分表,一次业务交互两次路由。
1、常见拆分场景
a). 大字段的垂直切分。单独将大字段建在另外的表中,提高基础表的访问性能,原则上在性能关键的应用中应当避免数据库的大字段
b). 按照使用用途垂直切分。例如企业物料属性,可以按照基本属性、销售属性、采购属性、生产制造属性、财务会计属性等用途垂直切分
c). 按照访问频率垂直切分。例如电子商务、Web 2.0系统中,如果用户属性设置非常多,可以将基本、使用频繁的属性和不常用的属性垂直切分开
2、拆分原则
一般讲长度短、访问率高的字段,拆分进入base表。其他字段放入extend表中。因为mysql通过行进行buffer缓存,长度短意味着能一次性存储更多行数的记录,最大量减少磁盘访问。
1、常见拆分场景
a). 比如在线电子商务网站,订单表数据量过大,按照年度、月度水平切分(时间维度range)
b). Web 2.0网站注册用户、在线活跃用户过多,按照用户ID范围等方式,将相关用户以及该用户紧密关联的表做水平切分(用户id range)
c). 例如论坛的置顶帖子,因为涉及到分页问题,每页都需要显示置顶贴,这种情况可以把置顶贴水平切分开来,避免取置顶帖子时从所有帖子的表中读取
3、两种方式的常见拆分方案:
1、hash取模:简单,路由方便;扩容不方便,进行rehash 二次扩容。
一致性哈希:普通哈希优化-->一致性哈希
普通哈希case:
1、10个id,分2张表数据分布如下:
node1 |
0 |
2 |
4 |
6 |
8 |
node2 |
1 |
3 |
5 |
7 |
9 |
2、数据扩容,分成4张表数据分布如下(rehash操作):
node1 |
0 |
4 |
8 |
node2 |
1 |
5 |
9 |
node3 |
2 |
6 |
|
node4 |
3 |
7 |
|
数据迁移量:2、3、6、7 共4各节点数据需进行迁移,40%(其实这种计算方式不恰当,因为随着数据量增加,同样由2扩容到4,迁移量肯定小于40%)
3、不进行double扩容,而是采取增加或减少某个节点(仍然需要rehash)。例如增加1各节点后,数据分布:
node1 |
0 |
3 |
6 |
9 |
node2 |
1 |
4 |
7 |
|
node3 |
2 |
5 |
8 |
|
数据迁移量:2,3,4,5,8,9 共6各节点数据需进行迁移,60%
一致性哈希case:
先简单介绍原理:
1、将node节点数和id按照相同哈希算法(如md5、ascii)先计算出一个哈希值,则计算后的node值则可以看做分布在一个环上的节点。
2、将每个id计算的哈希值与node哈希值做对比,小于某node的,均分布在node后。
3、则此时进行节点新增时,再将需新增的节点按照同样的算法计算出哈希,按大小与原node哈希进行对比确定在环中的分布位置。再重复2中操作,确定需要迁移的数据。
同样10个id,先分2张表,采用一致性哈希算法操作如下:
1)计算分表键id及分表table(node)对应哈希值
id |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
哈希值 |
192 |
196 |
200 |
204 |
208 |
212 |
216 |
220 |
224 |
22 |
node |
a |
g |
z |
node哈希值 |
203 |
209 |
228 |
2)按照大小对比,进行分表操作
node a(203)所有id哈希值小于203的存放在203后 |
同理 node g(209) |
同理 node z(228) |
新增 node y(216) |
0:192 1:196 2:200 |
4:208 |
5:212 6:216 7:220 8:224 9:228 |
|
3)此时再增加节点node 3,算出哈希值为216,则只需要将大于228的数据而小于216的部分,迁移到216上,即只需要迁移5、6
node a(203)所有id哈希值小于203的存放在203后 |
同理 node g(209) |
同理 node z(228) |
新增 node y(216) |
不变 |
不变 |
7:220 8:224 9:228 |
5:212 6:216 |
总结
节点和分表key按照统一规则计算出一个哈希值(虚拟值),以相同的对比规则按照大小方式进行排列,增加删除节点总是在某一个范围内进行小部分迁移,从而达到减少数据迁移量的目的。
2、除哈希取模外,还可通过range进行计算
range: 0-1亿 table0 ;1亿到2亿 table1; 扩容方便;问题:id必须自增,新用户活跃度高,数据热点问题,新增数据表,数据分布不均。