一、前期准备
目前公司的某张记录表,已经7千万条数据了,考虑到以后业务的增长,故考虑将其拆分成32张数据表;

项目是采用ORM来进行查询数据库的,在这里便显示出ORM的强大便利性了,在做数据表的拆分的时候,业务使用方不必做任何更改,仅仅修改model层的数据表的读取就OK,整体来说对业务是无感知的;
数据表的拆分,是根据用户ID进行拆分的,相关model层代码,大致如下,这里仅展示其中一个model:
<?php/*** 根据uids获取任务记录多条* @param array $userIds* @param int $taskId* @return array*/public static function getTaskRecordByUids($userIds = [], $taskId = 0){if (empty($userIds) || empty($taskId)) {return [];}$n = Env::isOnline() ? 32 : 2;$userGroup = [];foreach ($userIds as $userId) {$tableNum = $userId % $n;$userGroup[$tableNum][] = $userId;}$taskInfo = [];foreach ($userGroup as $group) {$info = self::dynamic(['user_id' => $group[0]])->select(['user_id', 'task_id', 'task_status', 'task_num', 'action_time', 'create_time'])->whereIn('user_id', $group)->where('task_id', $taskId)->get();if (empty($info)) {$info = [];} else {$info = $info->toArray();}$taskInfo = array_merge($taskInfo, $info);}return $taskInfo;}
该model,是根据userId和taskId查询用户数据,大致的流程如下: (1)根据userId,把用户按照所需映射表进行分组; (2)从相应数据表中查找到相应的数据; (3)合并各个查找结果
注意:
在大数据量的数据查询的过程中,要带上 LIMIT 分页查询,避免查询量过大导致内存溢出;
二、数据迁移
前期将数据的查询切换到新的数据表,还相对简单一些,个人觉得重点难题还是在业务在无感知的情况下,实现旧数据分配到新的数据表,且要具备高可用的性能,能够在出现异常的情况下能够迅速补救;这里列举几种常见的方案,根据场景,本人采用的是第二种方案:
2.1 方案一:停服切库
顾名思义,该方式是在数据库停止的情况下,进行切换分表;大致分为以下几个步骤:
(1)同步数据库。首先通过binlog或其他方式将大表的数据同步给各个小表;
(2)在两张表差异比较小的时候停服,完全同步数据,然后切换数据表;
优势
- 稳定、不容易出错;
- 数据表中各个数据的先后写入关系能够保持一致;
劣势
- 会停服一段时间
2.2 方案二:双写再切表
(1)数据同步双写,读数据仍然读旧数据
(2)脚本将差异数据进行同步
(3)将读写切换到新表

优势
- 不需要停服就能直接进行
- 机动性较高
劣势
- 仅适合添加完不再更改的数据
- 无法从主键判断数据的先后关系
- 局限性较大,需要编写额外的同步脚本
- 会有出现更新丢失的问题
2.3 方案三:增加公共缓存区
(1)增加一个公共缓存区域,将最新的写入数据存放到公共缓冲区;
(2)利用binlog或其他的方式,将数据进行同步到一个差异很少的程度;
(3)停止从公共缓冲区写入数据,切换数据表的写入为新表的写入;
(4)从缓冲区重新写入数据;

优势
- 不需要停服,对用户无感知
缺点
- 会有短暂的延时
- 公共缓冲区的一段时间内无法停止使用,会稍微增大成本
三、总结
根据不同的场景,选择不同的切库方式;
对于仅有插入无更新的数据,建议采用第二种方式进行切换;
如果有更新数据的话,可以采用第一种,当一切准备就绪的话,如果短暂停服可以接受的话,稳定性还是比较好的;
第三种方案为前两种方案的中和,实际操作的话,可能会稍微繁复一些,需要考虑的很多问题,出错的概率也稍微大一些;
