在工作中,曾经做过一个项目,采用了哈希取模的方法进行水平分库,这种方法简单高效,但是在数据库规模有所变动的时候,需要做数据迁移。本文介绍一个自己拍脑袋想出来的一种简易的水平分库方案,以解决数据迁移的问题。
首先,我将数据库表中的字段分为三种类型:主键(通常是整型 id)、关键字段、非关键字段。关键字段指的是该字段在当前表结构中具有决定性意义,如图书借阅记录表中的 userId。
其次,主键必须包含分片信息,即分库的下标值,可通过对主键进行解析获得分库下标,直接定位到对应数据库。
然后,我们来讨论增删改查。
- 增:
- 增加包含关键字段的记录。对关键字段的值进行哈希,然后对当前数据库规模值取模(注意这里的当前,后面会解释时间点的意义),即:hash(key) % {count of db}(1),从而获取到该记录所应该落地的数据库下标。
- 增加不包含关键字段的记录。这种情况,可直接随机落地,注意分库信息一定要记录到主键中。
- 删:
- 根据主键删除特定记录。由于主键中记录了分库信息,因此可以快速定位到相应的分库。
- 根据关键字段删除特定记录。由于相同的关键字段值都会被路由到同一个数据库,因此使用公式(1)算出分库下标,即可定位到相应分库。
- 根据非关键字段删除特定记录。这种情况需要对所有数据库都执行相同语句。
- 改:同上。
- 查:同上。
以上说的情况是采用了哈希取模的方法进行水平分库,接下来要加入分片规模记录的概念(在个人项目中定义的接口为
IShardScaleRecord)。
分片规模记录保存了数据库规模的变动记录,具体如下:
{
{1531375099000, 1},// 2018/7/12 13:58:19 数据库规模为 1
{1531459618000, 2}// 2018/7/13 13:26:58 数据库规模为 2
}
加入了该记录以后,在数据库扩容并不做数据迁移的情况下,也无需全库操作。此时,我们再来讨论增删改查。
- 增:增加操作与之前并没有差别,需要注意的是当前这个时间点,在增加记录时 {count of db} 为最后一条分片规模记录的数据库规模大小。
- 删:
- 根据主键删除特定记录。由于主键中记录了分库信息,因此可以快速定位到相应的分库。
- 根据关键字段删除特定记录。这种情况需要分为三种情况。
- 指定了某个时间点。根据分片规模记录,获取到当前时间点的数据库规模代入公式(1)计算得到分库下标。
- 指定了某个时间段。根据分片规模记录,获取该时间段内的数据库规模列表,遍历该列表,使用公式(1)计算得到分库下标列表,同时对该列表中的分库进行删除操作。
- 未指定时间点(段)。遍历整个分片规模记录,使用公式(1)计算得到分库下标列表,同时对该列表中的分库进行删除操作。
- 根据非关键字段删除特定记录。这种情况需要对所有数据库都执行相同语句。
- 改:同上。
- 查:同上。
至此,本人拍脑袋瞎想的方案介绍完毕,欢迎各位大佬指导批评。
附上项目地址。