-
背景
用于存储用户签到数据表中的数据量超过1500万条,在进行查询时相对缓慢决定对其进行分表。 -
步骤
a. 分析业务场景
该表的使用者为签到用户和运营人员。
签到用户进行签到时在表中记录的相关信息:uid、时间、ip等。签到用户侧对数据库使用的基本特点是:单条记录写入,访问量大,服务要求高可用。
运营人员在前端通过uid查询用户的签到信息,业务场景如:用户投诉某天的签到没有统计。运营人员侧需求的基本特点是:大量的批量分页查询需求,或许需要增加多种查询方式。b. 明确属性查询范围
该表的属性查询内容主要为uid,即运营人员查询用户的uid。
c. 方案设计:
采用水平分表的方式,按照时间或uid进行分表。
按照时间:比如半年
优势:1.扩容简单,只需按照时间增加表即可;2. 查询的数据为近期数据,只需在一个表中按照近期时间查询即可;3.切分策略简单;4.需要修改部分写入和查询的代码:写入操作需要按照时间更新,查询操作需要根据查询数据量进行多表查询。
劣势:1.请求量不均,总是在新表写入数据;2.当需要查询用户所有签到数据时需要访问所有表,时间慢;3.数据量不均,新增的表数据量会非常少。
因为劣势2不选择此方案,有1000多个用户签到时间超过600天,证明存在一直签到的用户,那么若按时间分配查询他们的会比较慢。查询逻辑会多写一部分代码。请求量和数据量均比较小,此两项可以不考虑
按照uid:
哈希法
优势:1. 数据量均衡;2.请求量均衡。
劣势:1.扩容麻烦,若增加新表则需重新哈希,可能会导致数据迁移;2. 写入操作需要更新按照uid哈希结果进行写入,查询操作需要按照uid哈希结果进行查询。若增加新表则需修改代码。
选择此方案:对3取余扩容会在两年后进行,无论采用什么方案都会修改代码
范围法
优势:1.策略简单;2.扩容简单。
劣势:1.数据量不均;2.不方便增加新用户,现在才4万左右,若增加新用户可能需要增加新表。最终结果:哈希法(对3取余)
采用此方案的原因:
数据分布均匀,表中数据对3取余后,各个字表中数据量分布均匀,数据增长量分布均匀,按照目前的增长速度大概两年后每个字表中的数据达到1000万。
mysql> select count(distinct uid),count(uid) from user_*** where uid % 3 = 0 and create_time > 1532238674000 and create_time < 1533189074000;
+---------------------+------------+
| count(distinct uid) | count(uid) |
+---------------------+------------+
| 14105 | 66926 |
+---------------------+------------+
1 row in set (14.55 sec)
mysql> select count(distinct uid),count(uid) from user_*** where uid % 3 = 1 and create_time > 1532238674000 and create_time < 1533189074000;
+---------------------+------------+
| count(distinct uid) | count(uid) |
+---------------------+------------+
| 13994 | 66237 |
+---------------------+------------+
1 row in set (14.46 sec)
mysql> select count(distinct uid),count(uid) from user_*** where uid % 3 = 2 and create_time > 1532238674000 and create_time < 1533189074000;
+---------------------+------------+
| count(distinct uid) | count(uid) |
+---------------------+------------+
| 13964 | 66300 |
+---------------------+------------+
1 row in set (17.93 sec)
mysql> select count(distinct uid),count(uid) from user_*** where uid % 3 = 0;
+---------------------+------------+
| count(distinct uid) | count(uid) |
+---------------------+------------+
| 346305 | 5288253 |
+---------------------+------------+
mysql> select count(distinct uid),count(uid) from user_*** where uid % 3 = 1;
+---------------------+------------+
| count(distinct uid) | count(uid) |
+---------------------+------------+
| 345785 | 5324079 |
+---------------------+------------+
mysql> select count(distinct uid),count(uid) from user_*** where uid % 3 = 2;
+---------------------+------------+
| count(distinct uid) | count(uid) |
+---------------------+------------+
| 346966 | 5328743 |
+---------------------+------------+
d. 执行方案
(1)在保证用户正常使用的前提下,对数据库进行分表。需要在维持旧表的同时,让数据双写,同时写入新表和旧表,此时让数据往新表导入。