乐趣区

关于java:分区取模分库分表策略多表事务分库内闭环解决方案

简介:当表数据超过一定量级,就须要通过分表来解决单表的性能瓶颈问题;当数据库负载超过肯定水平线,就须要通过分库来解决单库的连接数、性能负载的瓶颈问题。本文将论述在不同状况下,让不同数量级表,在同一个业务 ID 的事务操作路由到同一分库中的计划,省去解决垮库事务的懊恼。

作者 | 雨庄
起源 | 阿里技术公众号

一 前言

技术同学都晓得,当表数据超过一定量级,咱们就须要通过分表来解决单表的性能瓶颈问题;当数据库负载超过肯定水平线,咱们就须要通过分库来解决单库的连接数、性能负载的瓶颈问题。

本文次要论述在同时满足以下业务场景:

分表分库存储
须要对分表数量不同的表进行同事务操作
这些表的分库分表策略依赖的 Sharding 业务 ID 统一
等状况下,让这些不同数量级表,在同一个业务 ID 的事务操作路由到同一分库中的计划,省去解决垮库事务的懊恼。

二 案例

1 背景

假如有 2 个数据库实例,须要保留商家订单明细和汇总 2 张表的数据,这 2 张表的 分库分表策略都用 shop_id 取模策略,按单表数据 500w 的准则进行分表分库:

(1)shop_order_detail 商家订单明细表,日均数据 6000w

(2)shop_order_stat 商家订单统计表,日均数据 2000w

配置实现后生成的库表:

而后咱们要做这么一件事件:在同一个事务中,新增用户订单明细胜利后,更新用户订单统计数据:

2 问题

此时,我要解决一笔 user_id = 3 的订单数据:

如图,执行新增 shop_order_detail 表操作的时候,操作被路由到了 DB0 中;执行更新 shop_order_stat 表操作的时候,操作被路由到了 DB1。这时候 这两个操作跨库了,无奈在同一个事务中执行,流程异常中断。

如果用 TDDL 组件的话就会报这样的错:

 Cause: ERR-CODE: [TDDL-4603][ERR_ACCROSS_DB_TRANSACTION] Transaction accross db is not supported in current transaction policy

三 解决方案

解决多表跨库事务的计划有很多,本文论述的是如下解决方案:

将 shop_order_stat 作为 shop_order_detail 的映射根底表,调整 shop_order_detail 的分表策略,让 shop_order_detail 和 shop_order_stat 的数据都路由到同一个库中。
但该计划的前提是指标表的表数量是映射根底表表数量的 N 倍数。比方 shop_order_stat 的总表数量是 4,shop_order_detail 的总表数量是 12,故 shop_order_detail 的总表数是 shop_order_stat 总表数的 3 倍。

shop_order_detail 新分表分库策略的推导思路如下:

1 调整分库策略

首先,咱们看 shop_id 在 0~11 范畴内,用 shop_id % 4 分库分表策略 shop_order_stat 的 sharding 分布图:

用 shop_id % 12 分库分表策略 shop_order_detail 的 sharding 分布图:

图中看出,两张表都是依据 shop_id 做 sharding,但现有同一个 shop_id 有可能会被路由到不同的库中,导致跨库操作。

此时,我只须要把 shop_order_detail 的分库策略调整为跟 shop_order_stat 统一,保障同一个 shop_id 能路由到同一个 DB 分片中就能解决这个问题。调整后的 sharding 分布图:

但调整完分库策略后,本来的表映射策略就生效了:

本来的 shop_id = 5 数据能够通过 shop % 12 = 5 的取模策略映射到 DB0 的 shop_order_detail_05 表上。调整完分库策略后,shop_id = 9 被路由到了 DB0 中,通过 shop % 12 = 9 的取模策略会映射到 shop_order_detail_09 这张表上,但 shop_order_detail_09 这张表不在 DB0 中,所以操作失败了。

这时候,咱们须要调整分表策略,把 shop_id = 9 的数据既映射到 DB0 中的 shop_order_detail_05 表中。

2 分区取模策略

首先,以 shop_order_stat 的单库表数量 2 作为分块大小,总表数量 4 作为分区大小,对 shop_id=[0~11]进行分区操作,并且将 shop_id 依据分块大小取模:

以后分库数量为 2,shop_order_stat 的单库表数量为 6,计算出跨库步长 = 分库下标 * 单库表数量:

依据分区下标和分块大小,计算出分区步长 = 分区下标 * 分块大小,最初依据分块取模数 + 跨库步长 + 分区步长就能定位到最终的分表下标了:

这样就实现了把 shop_id = 9 的数据既映射到 DB0 中的 shop_order_detail_05 表中的工作。

四 计算公式

分表下标路由策略计算公式:

分表下标 = 业务 ID 取模 % 分块大小 + 业务 ID 取模 / 分块大小 单库表数量 + 业务 ID 取模 / 分区大小 分块大小
业务 ID 取模 = 业务 ID % 总表数量
分区大小 = 指标映射表的总表数量
分块大小 = 指标映射表的单库表数量
以下面的案例为例,调整 shop_order_detail 的分库分表路由策略:

(1)shop_order_stat 商家订单统计表

(2)shop_order_detail 商家订单明细表

TDDL sharding-rule 配置代码示例:

Java 代码示例:

long shopId = 9;
int dbs = 2;
int tables = 12;
int oneDbTables = 6;
int partitionSize = 4;
int blockSize = 2;
int sharding = (int) (shopId % tables);
// 指标分库
int dbIndex = (int) (shopId % partitionSize / dbs);
// 指标分表
int tableIndex = sharding % blockSize + sharding % partitionSize / blockSize * oneDbTables + sharding / partitionSize * blockSize;

五 结尾

我是本地生存外卖商家经营研发团队中的一员,在理论业务场景的设计中遇到了多表事务分库内闭环的问题,没有找到适宜的案例参考,才孵化出这个解决方案。

目前该计划曾经在落地上线,有雷同业务场景需要的同学可间接套用计算公式既可,欢送大家交换沟通。
原文链接
本文为阿里云原创内容,未经容许不得转载。

退出移动版