MySQL分库分表下实现异构业务关系绑定

业务背景

在业务量初期,数据量很小,大多数开发人员都选择了采用单库单表进行研发方案设计和构建。单库单表的好处是开发快,业务模型构建与库表映射简单,便于理解和沟通。随着业务增长,伴随单库单表而来的是查询瓶颈问题,首先在单表突破百万甚至千万,在已有索引的前提下查询优化空间并不多,而且数据变更往往要小心翼翼,一旦有大事务会直接拖慢整个数据库的查询性能,连锁雪崩效应带来的后果不堪设想,往往我们会对单库单表进行垂直和水平拆分以达到优化查询的目的。

问题描述

在水平拆分的过程中,通常都是以一个字段作为切分键对数据进行拆分,一般我们会使用用户ID等业务互通的字段。在水平拆分后,这里引申出一个问题,那就是当单表时,任何字段的检索都可以通过一条SELECT语句完成;在拆分后,由于切分键字段负责路由数据归属哪个库哪张表,在水平拆分数据之后切分键数据变成了必填字段,无论查询什么都需要至少或包含切分键字段参与,但是实际业务场景外部仅仅知道非切分键字段进行查询,需要对该场景进行解决。

切分键路由问题

单库单表、分库分表对比如下,水平拆分后业务字段中只有切分键字段可路由到数据,也即任何查询都要携带切分键信息才可以,要么通过切分键查询数据,要么通过切分键+任何业务字段进行查询。

表结构 字段定义 分库分表切分键字段
单库单表 字段A,字段B 不需要
分库分表 字段A,字段B 业务字段A

如下图是单库单表做水平拆分,这里切分键是使用的userId,根据不同的userId进行数据库表归属。单表数据被拆分到多库表中,降低了数据量集中在单表,但是只能通过userId或userId+业务字段进行查询,无法单独通过userName、certificateNo等非切分键字段独立查询。
MySQL分库分表下实现异构业务关系绑定

解决方案

表结构设计

当数据库表水平拆分后,设计表结构如下

核心字段定义 分库分表切分键字段
业务表 业务字段A,业务字段B 业务字段A
绑定关系表 绑定字段,被绑定字段 被绑定字段(这里是业务表未路由字段,即业务字段A之外的字段)

以用户信息表举例,如下

业务字段定义 分库分表切分键字段
用户信息表 用户ID,姓名,身份证 用户ID
绑定关系表 身份证(绑定字段),用户ID(被绑定字段) 身份证(绑定字段)
  • 1.业务表正常水平拆分,以通用的核心业务字段做切分键进行数据路由
  • 2.增加绑定关系表,与业务表同等进行水平拆分,以被绑定关系的业务字段作为切分键进行数据路由

流程设计

业务请求 数据库 异步消息 补偿任务 数据持久化 1.持久化数据 2.初始化绑定标志位 绑定MQ 数据库事务: 1.数据库持久化 2.异步MQ 变更标志位 完成绑定标志位 数据补偿 扫描初始化绑定标志位 绑定MQ 补偿处理 变更标志位 完成绑定标志位,数据最终一致 业务请求 数据库 异步消息 补偿任务
  • 1.业务请求发起后通过数据库事务进行数据持久化和绑定MQ发送这两部操作,这部操作是依赖数据库的事务原子性来包装数据正常持久化和绑定MQ的发送的
  • 2.在数据持久化时,对绑定字段进行标志位初始化,表示业务数据持久化成功,但绑定字段未持久化完成,这里是一个中间态,如果绑定数据出现异常,补偿任务也可通过该标志位进行数据补偿
  • 3.异步消息负责处理绑定数据。由于业务表、绑定关系表路由字段不同,数据会路由到不同库表,因此无法通过数据库事务进行操作,这里通过异步绑定方式进行绑定关系持久化,实现最终一致
  • 4.补偿任务会定时轮询业务数据的标志位,在一定时间内未完成绑定业务的数据会进行补偿,重发MQ,数据最终一致

其他方案

MySQL分库分表下实现异构业务关系绑定

常见的解决方案之一是可以通过MySQL的binlog日志汇总到第三方数据作业平台,通过HBase+ElasticSearch等手段进行数据聚合,这里不对该方式进行介绍,仅谈论利用纯关系型数据库解决异构场景的方法。