在实际的应用场景中,当一个单表的数据量过亿时写入和查询的压力就会倍增,拆表在一定程度上是解决数据库单表瓶颈的方案之一,
在本次要拆分的表中有以下特征:
- 单表已过2亿
- 表数据量每天增量50万左右
- 表结构比较单一,用户关注用户行为表,用户关注一个用户会往表中写入一条数据。
表结构如下:
tb_user_friends:
uid, fid, from, create_date, inverse_date(反向索引时间戳)
其中表索引:
PRIMARY KEY (`uid`,`fid`),
KEY `idx_uid` (`uid`,`inverse_date`),
KEY `idx_fid` (`fid`,`inverse_date`),
如何选择拆分键
选择拆分键的选择往往由查询决定,常见的查询语句如下:
// 获取用户的关注列表
select fid from tb_user_friends where uid = xxx order by inverse_date limit 20;
// 获取用户的粉丝列表
select uid from tb_user_friends where fid = xxx order by inverse_date limit 20;
这就保证了我们查询用户的粉丝或者关注都需要从单一的一个表中能够获取到,所以如果单一的按照uid进行拆分,会导致查询粉丝需要从多张表中查询,这是不能接受的,所以这里分别以uid,fid作为拆分键拆成两个主表如下:
tb_user_followers_000:
uid, fid, from, create_date, inverse_date
tb_user_fans_000:
uid, fid, from, create_date, inverse_date
结构和上面的表保持一致,但索引如下:
PRIMARY KEY (`uid`,`fid`),
KEY `idx_uid` (`uid`,`inverse_date`),
这样查询变成了:
// 获取用户的关注列表
select fid from tb_user_followers where uid = xxx order by inverse_date limit 20;
// 取用户的粉丝列表.
select fid from tb_user_fans where uid = xxx order by inverse_date limit 20; // 获
粉丝查询和关注查询从两张表进行查询。
由于需要保证拆分后的表大小不能超过200w,所以最后选择的拆分的大小是每个主表128张分表。
数据迁移方案
首先是代码的迁移,因为一个表变成了两个表,所以底层变更如下:
- 关注需要更改为写入到两个表,取消关注删除两个表数据。
- 查询分别从相应的表中查询。
- 老数据需要同步到新表中。
写入的 sql 如下:
INSERT IGNORE INTO tb_user_followers_000 (uid,fid,from,create_date,inverse_date)
SELECT uid,fid,from, create_date,inverse_date FROM tb_user_friends WHERE uid % 128 = 0;
INSERT IGNORE INTO tb_user_fans_000 (uid,fid,from,create_date,inverse_date)
SELECT fid,uid,from,create_date,inverse_date FROM tb_user_friends WHERE fid % 128 = 0;
但为了保证主从不能延迟过高,以及数据的完整性,我们选择的导入方案如下:
- 需要保证业务读写正常,同时导数据又不要太慢,开的4个导数据进程按时间分批次导入。
- 导入的是上线前一天的所有数据,上线当天将增量数据再次导入。
- 因为导入的都是增量数据,所以上线后,需要将DELETE的sql通过脚本删除新表中的数据,这样就保证了数据的完整性。
可能存在的问题
- 因为是粉丝关注表,如果突然某个人火了,某个表的增量将会非常大。
- 查询粉丝的或者关注总数的查询将需要循环查询,解决方案,将这些数维护到一个表中。