一、前期准备

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

image.png

项目是采用ORM来进行查询数据库的,在这里便显示出ORM的强大便利性了,在做数据表的拆分的时候,业务使用方不必做任何更改,仅仅修改model层的数据表的读取就OK,整体来说对业务是无感知的;

数据表的拆分,是根据用户ID进行拆分的,相关model层代码,大致如下,这里仅展示其中一个model:

  1. <?php
  2. /**
  3. * 根据uids获取任务记录多条
  4. * @param array $userIds
  5. * @param int $taskId
  6. * @return array
  7. */
  8. public static function getTaskRecordByUids($userIds = [], $taskId = 0)
  9. {
  10. if (empty($userIds) || empty($taskId)) {
  11. return [];
  12. }
  13. $n = Env::isOnline() ? 32 : 2;
  14. $userGroup = [];
  15. foreach ($userIds as $userId) {
  16. $tableNum = $userId % $n;
  17. $userGroup[$tableNum][] = $userId;
  18. }
  19. $taskInfo = [];
  20. foreach ($userGroup as $group) {
  21. $info = self::dynamic(['user_id' => $group[0]])
  22. ->select(['user_id', 'task_id', 'task_status', 'task_num', 'action_time', 'create_time'])
  23. ->whereIn('user_id', $group)
  24. ->where('task_id', $taskId)
  25. ->get();
  26. if (empty($info)) {
  27. $info = [];
  28. } else {
  29. $info = $info->toArray();
  30. }
  31. $taskInfo = array_merge($taskInfo, $info);
  32. }
  33. return $taskInfo;
  34. }

该model,是根据userId和taskId查询用户数据,大致的流程如下: (1)根据userId,把用户按照所需映射表进行分组; (2)从相应数据表中查找到相应的数据; (3)合并各个查找结果

注意:
在大数据量的数据查询的过程中,要带上 LIMIT 分页查询,避免查询量过大导致内存溢出;

二、数据迁移

前期将数据的查询切换到新的数据表,还相对简单一些,个人觉得重点难题还是在业务在无感知的情况下,实现旧数据分配到新的数据表,且要具备高可用的性能,能够在出现异常的情况下能够迅速补救;这里列举几种常见的方案,根据场景,本人采用的是第二种方案:

2.1 方案一:停服切库

顾名思义,该方式是在数据库停止的情况下,进行切换分表;大致分为以下几个步骤:

(1)同步数据库。首先通过binlog或其他方式将大表的数据同步给各个小表;
(2)在两张表差异比较小的时候停服,完全同步数据,然后切换数据表;

优势

  • 稳定、不容易出错;
  • 数据表中各个数据的先后写入关系能够保持一致;

劣势

  • 会停服一段时间

2.2 方案二:双写再切表

(1)数据同步双写,读数据仍然读旧数据
(2)脚本将差异数据进行同步
(3)将读写切换到新表

image.png

优势

  • 不需要停服就能直接进行
  • 机动性较高

劣势

  • 仅适合添加完不再更改的数据
  • 无法从主键判断数据的先后关系
  • 局限性较大,需要编写额外的同步脚本
  • 会有出现更新丢失的问题

2.3 方案三:增加公共缓存区

(1)增加一个公共缓存区域,将最新的写入数据存放到公共缓冲区;
(2)利用binlog或其他的方式,将数据进行同步到一个差异很少的程度;
(3)停止从公共缓冲区写入数据,切换数据表的写入为新表的写入;
(4)从缓冲区重新写入数据;

image.png

优势

  • 不需要停服,对用户无感知

缺点

  • 会有短暂的延时
  • 公共缓冲区的一段时间内无法停止使用,会稍微增大成本

三、总结

根据不同的场景,选择不同的切库方式;

对于仅有插入无更新的数据,建议采用第二种方式进行切换;

如果有更新数据的话,可以采用第一种,当一切准备就绪的话,如果短暂停服可以接受的话,稳定性还是比较好的;

 第三种方案为前两种方案的中和,实际操作的话,可能会稍微繁复一些,需要考虑的很多问题,出错的概率也稍微大一些;