收藏记录表:维护用户和收藏信息之间的关系
字段名 | 数据类型 | 长度 | 主键 | 描述 |
---|---|---|---|---|
id | varchar | 36 | Yes | UUID |
user_id | varchar | 32 | No | 用户Id |
resource_id | bigint | 20 | No | 收藏资源Id |
resource_name | varchar | 512 | No | 收藏资源名称 |
author | varchar | 512 | No | 资源创作者 |
album | varchar | 512 | No | 资源所属专辑 |
resource_type | varchar | 10 | No | 收藏资源类型 1:歌曲 2:节目 3:广播 |
数据库一般采用Master-Slave复制模式的MySQL架构,只能够对数据库的读进行扩展,而对数据库的写入操作还是集中在Master上,并且单个Master挂载的Slave也不可能无限制多,Slave的数量受到Master能力和负载的限制。
因此,需要对数据库的吞吐能力进行进一步的扩展,以满足高并发访问与海量数据存储的需要。
为了使数据能够较为均衡地分不到多张表中,并且不影响正常的查询,我们需要制定合适的分表策略。
按百万用户划分、每张表存储一万个用户的收藏信息,即需要划分出来128张表。
如果业务场景会随着时间规律增长,可考虑使用按时间日期分表,如每天的员工的打卡记录,假设某公司有20W员工每人每天打卡两次,即:
那么我们就可以考虑按照日期进行分表存储。 如:work_record_20180620…work_record_20181231
哈希取模代码见下:
// 分表数量
private static final int SUB_TABLE_NUM = 128;
public static final int uidConvertSubNum(String uid) {
int h;
// 将不规则的uid转为'随机、无规则'的大数
int hashUid = (uid == null) ? 0 : (h = uid.hashCode()) ^ (h >>> 16);
// 将散列值与长度做与运算得到下标值,等同于取模运算
int subNum = hashUid & (SUB_TABLE_NUM - 1);
return subNum;
}
在分表前请注意!
即uid为1230521,分表数为128时算出来的表序号为0,当分表数量增大为256时,算出来的表序号为7,这样在路由时就会出现找不到的情况。
如果出现这种情况的话,我们需要进行数据迁移,假如原有
扩容前/表序号 | 0 | 1 | 2 |
---|---|---|---|
uid | 0 | 1 | 2 |
uid | 3 | 4 | 5 |
uid | 6 | 7 | 8 |
uid | 9 | 10 | 11 |
扩容后/表序号 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
uid | 0 | 1 | 2 | 3 | 4 | 5 |
uid | 6 | 7 | 8 | 9 | 10 | 11 |
分表之后,数据被分配到不同的表中(类似于分片),不能再借助数据库自增长特性直接生成,否则会造成不同分片上的数据表主键会重复。
分表的实质还是在一个数据库上进行的操作,很容易受数据库IO性能的限制。 无法给数据库的并发处理能力带来质的提升。面对高并发的读写访问,当数据库master服务器无法承载写操作压力时,不管如何扩展slave服务器,此时都没有意义了。 因此,我们必须换一种思路,对数据库进行拆分,从而提高数据库写入能力,这就是所谓的分库。
用户触发收藏操作不是高并发行为,暂时不考虑分库。