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

沙海 2021年5月11日05:05:51Java评论84字数 4245阅读14分9秒阅读模式
摘要

速读摘要

速读摘要文章源自JAVA秀-https://www.javaxiu.com/22754.html

当数据库负载超过一定水平线,我们就需要通过分库来解决单库的连接数、性能负载的瓶颈问题。执行更新shop_order_stat表操作的时候,操作被路由到了DB1。我只需要把shop_order_detail的分库策略调整为跟shop_order_stat一致,保证同一个shop_id能路由到同一个DB分片中就能解决这个问题。9的取模策略会映射到shop_order_detail_09这张表上,但shop_order_detail_09这张表不在DB0中,所以操作失败了。文章源自JAVA秀-https://www.javaxiu.com/22754.html

原文约 1949 | 图片 12 | 建议阅读 4 分钟 | 评价反馈文章源自JAVA秀-https://www.javaxiu.com/22754.html

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

原创 雨庄 阿里技术 文章源自JAVA秀-https://www.javaxiu.com/22754.html

收录于话题文章源自JAVA秀-https://www.javaxiu.com/22754.html

#Java45文章源自JAVA秀-https://www.javaxiu.com/22754.html

#数据存储与数据库6文章源自JAVA秀-https://www.javaxiu.com/22754.html

分区取模分库分表策略:多表事务分库内闭环解决方案文章源自JAVA秀-https://www.javaxiu.com/22754.html

一  前言文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

技术同学都知道,当表数据超过一定量级,我们就需要通过分表来解决单表的性能瓶颈问题;当数据库负载超过一定水平线,我们就需要通过分库来解决单库的连接数、性能负载的瓶颈问题。文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

本文主要阐述在同时满足以下业务场景:文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

  • 分表分库存储文章源自JAVA秀-https://www.javaxiu.com/22754.html

  • 需要对分表数量不同的表进行同事务操作文章源自JAVA秀-https://www.javaxiu.com/22754.html

  • 这些表的分库分表策略依赖的Sharding业务ID一致文章源自JAVA秀-https://www.javaxiu.com/22754.html

等情况下,让这些不同数量级表,在同一个业务ID的事务操作路由到同一分库中的方案,省去解决垮库事务的烦恼。文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

二  案例文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

1  背景文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

假设有2个数据库实例,需要保存商家订单明细和汇总2张表的数据,这2张表的 分库分表策略都用shop_id取模策略,按单表数据500w的原则进行分表分库:文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

(1)shop_order_detail 商家订单明细表,日均数据6000w文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

分表数量6000w / 500w = 12张表
分表策略shop_id % 12
分库策略shop_id % 12 / 2
单库表数量12 / 2 = 6张表

(2)shop_order_stat 商家订单统计表,日均数据2000w文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

分表数量2000w / 500w = 4张表
分表策略shop_id % 4
分库策略shop_id % 4 / 2
单库表数量4 / 2 = 2张表

配置完成后生成的库表:文章源自JAVA秀-https://www.javaxiu.com/22754.html

分区取模分库分表策略:多表事务分库内闭环解决方案文章源自JAVA秀-https://www.javaxiu.com/22754.html

然后我们要做这么一件事情:在同一个事务中,新增用户订单明细成功后,更新用户订单统计数据:文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

分区取模分库分表策略:多表事务分库内闭环解决方案文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

2  问题文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

此时,我要处理一笔 user_id = 3 的订单数据:文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

分区取模分库分表策略:多表事务分库内闭环解决方案文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

如图,执行新增shop_order_detail表操作的时候,操作被路由到了DB0中;执行更新shop_order_stat表操作的时候,操作被路由到了DB1。这时候 这两个操作跨库了,无法在同一个事务中执行, 流程异常中断。文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

如果用TDDL组件的话就会报这样的错:文章源自JAVA秀-https://www.javaxiu.com/22754.html

文章源自JAVA秀-https://www.javaxiu.com/22754.html

    ### Cause: ERR-CODE: [TDDL-4603][ERR_ACCROSS_DB_TRANSACTION] Transaction accross db is not supported in current transaction policy
    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    三  解决方案文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    解决多表跨库事务的方案有很多,本文阐述的是如下解决方案:文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    将shop_order_stat作为shop_order_detail的映射基础表,调整shop_order_detail的分表策略,让shop_order_detail和shop_order_stat的数据都路由到同一个库中。文章源自JAVA秀-https://www.javaxiu.com/22754.html

    但该方案的前提是目标表的表数量是映射基础表表数量的N倍数。比如shop_order_stat的总表数量是4,shop_order_detail的总表数量是12,故shop_order_detail的总表数是shop_order_stat总表数的3倍。文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    shop_order_detail新分表分库策略的推导思路如下:文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    1  调整分库策略文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    首先,我们看shop_id在0~11范围内,用shop_id % 4分库分表策略shop_order_stat的sharding分布图:文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    分区取模分库分表策略:多表事务分库内闭环解决方案文章源自JAVA秀-https://www.javaxiu.com/22754.html

    用shop_id % 12分库分表策略shop_order_detail的sharding分布图:文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    分区取模分库分表策略:多表事务分库内闭环解决方案文章源自JAVA秀-https://www.javaxiu.com/22754.html

    图中看出,两张表都是根据shop_id做sharding,但现有同一个shop_id有可能会被路由到不同的库中,导致跨库操作。文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    此时,我只需要把shop_order_detail的分库策略调整为跟shop_order_stat一致,保证同一个shop_id能路由到同一个DB分片中就能解决这个问题。调整后的sharding分布图:文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    分区取模分库分表策略:多表事务分库内闭环解决方案文章源自JAVA秀-https://www.javaxiu.com/22754.html

    但调整完分库策略后,原本的表映射策略就失效了:文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    分区取模分库分表策略:多表事务分库内闭环解决方案文章源自JAVA秀-https://www.javaxiu.com/22754.html

    原本的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中,所以操作失败了。文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    这时候,我们需要调整分表策略,把shop_id = 9的数据既映射到DB0中的shop_order_detail_05表中。文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    2  分区取模策略文章源自JAVA秀-https://www.javaxiu.com/22754.html

    首先,以shop_order_stat的单库表数量2作为分块大小,总表数量4作为分区大小,对shop_id=[0~11]进行分区操作,并且将shop_id根据分块大小取模:文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    分区取模分库分表策略:多表事务分库内闭环解决方案文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    当前分库数量为2,shop_order_stat的单库表数量为6,计算出跨库步长=分库下标*单库表数量:文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    分区取模分库分表策略:多表事务分库内闭环解决方案文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    根据分区下标和分块大小,计算出分区步长=分区下标*分块大小,最后根据分块取数+跨库步长+分区步长就能定位到最终的分表下标了: 文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    分区取模分库分表策略:多表事务分库内闭环解决方案文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    这样就完成了把shop_id = 9的数据既映射到DB0中的shop_order_detail_05表中的工作。文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    四  计算公式文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    分表下标路由策略计算公式:文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    分表下标 = 业务ID取模 % 分块大小 + 业务ID取模 / 分块大小 * 单库表数量 + 业务ID取模 / 分区大小 * 分块大小文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    • 业务ID取 = 业务ID % 总表数量文章源自JAVA秀-https://www.javaxiu.com/22754.html

    • 分区大小 = 目标映射表的总表数量文章源自JAVA秀-https://www.javaxiu.com/22754.html

    • 分块大小 = 目标映射表的单库表数量文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    以上面的案例为例,调整shop_order_detail的分库分表路由策略:文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    (1)shop_order_stat 商家订单统计表文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    分表数量4张表
    分表策略shop_id % 4
    分库策略shop_id % 4 / 2
    单库表数量4 / 2 = 2张表

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    (2)shop_order_detail 商家订单明细表文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    分表数量12张表
    分表策略

    // 总表数取模
    def index = shop_id % 12(shop_order_detail总表数量);
    // 分块下标
    def blockIndex = index % 2(shop_order_stat单库表数量);
    // 跨库步长
    def dbStep = index % 4(shop_order_stat总表数量) / 2(shop_order_stat单库表数量) * 6(shop_order_detail单库表数量);
    // 分区步长
    def partitionStep = index / 4(shop_order_stat总表数量) * 2(shop_order_stat单库表数量);
    // 分表下标 = 分块下标 + 跨库步长 + 分区步长
    returnblockIndex + dbStep + partitionStep;文章源自JAVA秀-https://www.javaxiu.com/22754.html

    分库策略shop_id % 4 / 2
    单库表数量12 / 2 = 6张表

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    TDDL sharding-rule配置代码示例:文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

    文章源自JAVA秀-https://www.javaxiu.com/22754.html

      <bean id="shop_order_stat"class="com.taobao.tddl.rule.TableRule"> <property name="dbNamePattern"value="{0000}"/> <property name="dbRuleArray"value="(#shop_id,1,4#.longValue() % 4).intdiv(2)" /> <property name="tbNamePattern"value="shop_order_stat_{0000}"/> <property name="tbRuleArray"value="#shop_id,1,4#.longValue() % 4" /></bean><bean id="shop_order_detail"class="com.taobao.tddl.rule.TableRule"> <property name="dbNamePattern"value="{0000}"/> <property name="dbRuleArray"value="(#shop_id,1,4#.longValue() % 4).intdiv(2)" /> <property name="tbNamePattern"value="shop_order_detail_{0000}"/> <property name="tbRuleArray"> <value> def index = #shop_id,1,12#.longValue() % 12;return index % 2 + (index % 4).intdiv(2) * 6 + index.intdiv(4) * 2 </value> </property> <property name="allowFullTableScan"value="true"/></bean>
      文章源自JAVA秀-https://www.javaxiu.com/22754.html

      文章源自JAVA秀-https://www.javaxiu.com/22754.html

      Java代码示例:文章源自JAVA秀-https://www.javaxiu.com/22754.html

      文章源自JAVA秀-https://www.javaxiu.com/22754.html

      文章源自JAVA秀-https://www.javaxiu.com/22754.html

        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;
        文章源自JAVA秀-https://www.javaxiu.com/22754.html

        文章源自JAVA秀-https://www.javaxiu.com/22754.html

        五  结尾文章源自JAVA秀-https://www.javaxiu.com/22754.html

        文章源自JAVA秀-https://www.javaxiu.com/22754.html

        我是本地生活外卖商家运营研发团队中的一员,在实际业务场景的设计中遇到了多表事务分库内闭环的问题,没有找到适合的案例参考,才孵化出这个解决方案。 文章源自JAVA秀-https://www.javaxiu.com/22754.html

        文章源自JAVA秀-https://www.javaxiu.com/22754.html

        目前该方案已经在落地上线,有相同业务场景需求的同学可直接套用计算公式既可,欢迎大家交流沟通。文章源自JAVA秀-https://www.javaxiu.com/22754.html

        免费领取电子书文章源自JAVA秀-https://www.javaxiu.com/22754.html

        《阿里巴巴大数据及 AI 实战》文章源自JAVA秀-https://www.javaxiu.com/22754.html

        本书将深度剖析淘宝、高德、友盟+、1688、优酷、阿里妈妈、阿里影业大数据实战场景,是 2020 不容错过的企业大数据实战手册。文章源自JAVA秀-https://www.javaxiu.com/22754.html

        扫码加阿里妹好友,回复“阿里ai”获取吧~(英文字母小写,若扫码无效,可通过微信号alimei4、alimei5、alimei6、alimei7直接添加)文章源自JAVA秀-https://www.javaxiu.com/22754.html

        分区取模分库分表策略:多表事务分库内闭环解决方案文章源自JAVA秀-https://www.javaxiu.com/22754.html

        阅读原文文章源自JAVA秀-https://www.javaxiu.com/22754.html

        继续阅读
        速蛙云 - 极致体验,强烈推荐!!!购买套餐就免费送各大视频网站会员!快速稳定、独家福利社、流媒体稳定解锁!速度快,全球上网、视频、游戏加速、独立IP均支持!基础套餐性价比很高!这里不多说,我一直正在使用,推荐购买:https://www.javaxiu.com/59919.html
        weinxin
        资源分享QQ群
        本站是JAVA秀团队的技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
        沙海
        匿名

        发表评论

        匿名网友 填写信息

        :?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

        确定