破局主键重复问题的坎坷路

这篇具有很好参考价值的文章主要介绍了破局主键重复问题的坎坷路。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

伴随着业务的不断发展,逐渐由单库单表向分库分表进行发展。在这个过程中不可避免的一个问题是确保主键要的唯一性,以便于后续的数据聚合、分析等等场景的使用。在进行分库分表的解决方案中有多种技术选型,大概分为两大类客户端分库分表、服务端分库分表。例如 Sharding-JDBC、ShardingSphere、 MyCat、 ShardingSphere-Proxy、Jproxy(京东内部已弃用)等等。

在这个燥热的夏天,又突然收到告警,分库分表的主键冲突了,这还能忍?不,坚决不能忍,必须解决掉!后面咱们慢慢道来是如何破局的,如何走了一条坎坷路……

翻山第一步

咱们的系统使用的是ShardingSphere进行分库分表的,大概的配置信息如下:(出于信息的安全考虑,隐藏了部分信息,只保留的部分内容,不要在意这些细节能说明问题即可)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:sharding="http://shardingsphere.apache.org/schema/shardingsphere/sharding"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	   http://www.springframework.org/schema/beans/spring-beans.xsd http://shardingsphere.apache.org/schema/shardingsphere/sharding http://shardingsphere.apache.org/schema/shardingsphere/sharding/sharding.xsd">

    <!--数据源-->
    <bean id="database1" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
    </bean>
    <bean id="database2" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
    </bean>
    <bean id="database3" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
    </bean>

    <sharding:inline-strategy id="databaseStrategy" sharding-column="cloum1" algorithm-expression="table1_$->{(Math.abs(cloum1.hashCode()) % 512).intdiv(32) }" />
    <sharding:inline-strategy id="orderNoDatabaseStrategy" sharding-column="cloum2" algorithm-expression="table2_$->{(Math.abs(cloum2.hashCode()) % 512).intdiv(32) }" />
    <sharding:inline-strategy id="businessNoDatabaseStrategy" sharding-column="cloum3" algorithm-expression="table3_$->{(Math.abs(cloum3.hashCode()) % 512).intdiv(32) }" />
    <!-- 主键生成策略 -雪花算法-->
    <sharding:key-generator id="idKeyGenerator" type="SNOWFLAKE" column="id" props-ref="snowFlakeProperties"/>

    <sharding:data-source id="dataSource">
        <sharding:sharding-rule data-source-names="database1,database2,database3">
            <sharding:table-rules>

                <sharding:table-rule logic-table="table1"
                                     actual-data-nodes="database1_$->{0..15}.table1_$->{0..31}"
                                     database-strategy-ref="orderNoDatabaseStrategy"
                                     table-strategy-ref="order_waybill_tableStrategy"
                                     key-generator-ref="idKeyGenerator"/>

                <sharding:table-rule logic-table="table2"
                                     actual-data-nodes="database2_$->{0..15}.table2_$->{0..31}"
                                     database-strategy-ref="databaseStrategy"
                                     table-strategy-ref="waybill_contacts_tableStrategy"
                                     key-generator-ref="idKeyGenerator"/>

                <sharding:table-rule logic-table="table3"
                                     actual-data-nodes="database3_$->{0..15}.table3->{0..31}"
                                     database-strategy-ref="databaseStrategy"
                                     table-strategy-ref="waybill_tableStrategy"
                                     key-generator-ref="idKeyGenerator"/>
            </sharding:table-rules>
        </sharding:sharding-rule>
    </sharding:data-source>


    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:spring/mybatis-env-setting.xml"/>
        <property name="mapperLocations" value="classpath*:/mapper/*.xml"/>
    </bean>

</beans>





从上面的配置可以看出配置的是"SNOWFLAKE" 主键使用的是雪花算法,雪花算法产生的ID的组成总计64位,第一位为符号位不用,后41位为时间戳用于区别不同的时间点,在后面10位为workId用于区别不同的机器,最后12位为sequence用于同一时刻同一机器的并发数量。

那接下来就看看咱们自己的系统是怎么配置的吧,其中的属性snowFlakeProperties配置如下,其中的max.vibration.offset配置表示sequence的范围为1024。按照正常的理解任何单个机器的配置都很难达到这个并发量,难道是这个值没有生效?

    <sharding:key-generator id="idKeyGenerator" type="SNOWFLAKE" column="id" props-ref="snowFlakeProperties"/>





shardingsphere中实现获取主键的实现源码如下简述,具体的实现类org.apache.shardingsphere.core.strategy.keygen.SnowflakeShardingKeyGenerator,从源码看源码竟然一个日志都没有,那让咱们怎么去排查呢?怎么证明咱们的猜想是否正确的呢?囧……

真是败也萧何成也萧何,shardingsphere是通过java的SPI的方式进行的主键生成策略的扩展。自定义实现方式如下:实现org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator接口,如果自己想要实现使用SPI方式进行加载即可,那就让咱们自己加日志吧,走你……

既然都自己写实现了,那日志就该加的都加吧,咱这绝不吝啬这几行日志

修改主键选择生成策略为自己实现的类 type="MYSNOWFLAKE"

    <sharding:key-generator id="idKeyGenerator" type="MYSNOWFLAKE" column="id" props-ref="snowFlakeProperties"/>





启动看日志,属性中有max.vibration.offset=1024这个属性,竟然依旧拿到的还是默认的值1,惊讶中,细细一瞅,终究发现了问题,在getProperty(key)时如果返回的不是String类型那么为null,进而取值默认值1。从咱们的系统配置中可以看到系统配置的int类型的的1024,所以取值默认值1就说通了。

INFO 2023-08-17 14:07:51.062 2174320.63604.16922524693996408 176557 com.jd.las.waybill.center.config.MySnowflakeShardingKeyGenerator.getMaxVibrationOffset(MySnowflakeShardingKeyGenerator.java:107) -- 选择自定义的雪花算法获取到的properties={"max.vibration.offset":1024,"worker.id":"217","max.tolerate.time.difference.milliseconds":"3000"}
INFO 2023-08-17 14:07:51.063 2174320.63604.16922524693996408 176558 com.jd.las.waybill.center.config.MySnowflakeShardingKeyGenerator.getMaxVibrationOffset(MySnowflakeShardingKeyGenerator.java:110) -- 选择自定义的雪花算法获取到的getMaxVibrationOffset=1





截止到目前主键重复的问题终于可以解释的通了,因为并发支持的是0~1总共2个并发,所以在生产系统中尤其出现生产波次的时候出现重复值的可能性是存在的,然后把1024变成字符串修改上线,相信系统后面应该不会产生此类问题了。

越岭第二步

如果生活总是喜欢跟你开玩笑,逗你玩,那你就配合它笑一笑吧。

当上完线后,过了一段时间发现重复主键的问题竟然依旧存在只是频率少了些,不科学呀……

重新梳理思路,进行更详细的日志输出,下单进行验证,将接单落库这坨代码一并都加上日志以及触发雪花算法的生成的ID也加上日志

通过日志分析,又又又发现"灵异事件",10条插入SQL,只有两条触发了shardingsphere的雪花算法,诧异的很呀~

查看其他8张表落库的ID数据如下图,ID(1692562397556875266) 都为1692开头且长度20位,而shardingsphere产生的ID(899413419526356993)都为899开头且长度19位,很明显这8张表的主键不是shardingsphere生成的,那是这20位的数据哪来的呢???从ID上看明显也不是自增产生的主键,又不科学了……

又是一个深夜……

梳理思路重新在锊,主键相关的除了数据自增长、shardingsphere配置的雪花还有唯一的一个相关的组件那就是mybatis,由于项目是很早之前的应用了,使用的是baomidou的mybaits插件,如图是插件的入口,MybatisSqlSessionFactoryBean实现FactoryBean, InitializingBean, ApplicationListener几个Spring的接口

baomidou涉及该块问题的源码如下:

如果GlobalConfig没有配置workId和DataCenterId会使用无参构造,默认的workId

baomidou的雪花算法和shardingphere思路一致有一点点区别在于第12位和22位有datacenter<<17|workId<<12获取,且datacenter和workId需要在0~31之间

不同机器的Name:

所以又解释了为什么不同机器会出现相同的主键问题,但是如果有细心的同学就会问为啥10张表中有8张表走的是baomidou的雪花算法呢,那是因为baomidou会判断保存的入参实体bean上是否有id字段,是否能匹配上该字段,如果存在则在baomidou这层就给赋值了baomidou雪花算法生产的ID,后续就不会再次触发shardingsphere中ID生成,进而导致该问题。

截止到目前终于又解释通了为什么跨机器会产生相同的主键问题。

问题的解决方式:

baomidou配置的过程中指定workId和centerDataId,但是需要确保centerDataId<<17|workId<<12确保唯一。类比shardingphere,借用shardingphere中的12~22位唯一数,前5高位给(centerDataId<<17),后5低位给workId<<12;

夜已沉默……

生产环境已上线验证通过

作者:京东物流 王义杰

来源:京东云开发者社区 自猿其说Tech 转载请注明来源文章来源地址https://www.toymoban.com/news/detail-681555.html

到了这里,关于破局主键重复问题的坎坷路的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 蓝桥杯必备——动态规划“路径问题”以及这种题的小结

    int[][]m=new int[2][3] 表达的含义是,两行,三列。 不同路径 首先这个题我们分五步走 1.状态表示(按照经验+题目要求) 一般都是以···为结尾或者以···为起始 这道题我们就以dp[i][j]为他要求的到达结尾有多少条路径 此时你要思考一个东西,有多少条路径,他是怎么来的来考

    2024年02月08日
    浏览(35)
  • 【pytorch】随着epoch增加,显存逐渐增加?解决pytorch显存释放问题

    在训练时,检测 “out of memory” 的error并通过torch.cuda.empty_cache()处理 如: 在测试时,避免忘记设置 torch.no_grad() 如: Pytroch - 显存释放问题

    2024年02月11日
    浏览(61)
  • mysql表主键自增过大问题

    问题及项目环境   问题 最近在做项目时,发现我创建的每一个表的主键设置自增,在插入数据数据时会出现 自增值过大的问题。 问题展示: 在后端执行Basemapper中的insert()方法时,数据库中的主键id字段为下: 且我在对应的实体类的设置为下:  我们的期望时看到Id = 1,而不是

    2024年02月16日
    浏览(46)
  • mysql高并发下主键自增打来的问题

    在一般情况下,在新增领域对象后,都需要获取对应的主键值。使用应用层来维护主键,在一定程度上有利于程序性能的优化和应用移植性的提高。在采用数据库自增主键的方案里,如果JDBC驱动不能绑定新增记录对应的主键,就需要手工执行查询语句以获取对应的主键值,对

    2024年02月13日
    浏览(38)
  • mysql如何自定义自增主键值,以及所遇到的不生效问题

    最近有一个需求,要求对某些数据的数据库主键id格式化,要求id为(202311080000)的形式去自增,可以在get请求获取到这些数据时在请求路径上看到格式化的id,但是之前数据id有大于202311080000数值的,导致自增的主键值并没有生效(即使删除相关数据) 如何自定义自增主键值 第一种通过命

    2024年04月15日
    浏览(39)
  • 记一次docker-compose的坎坷安装经历

            最近公司在做一个kafka项目,所以想用docker来安装kafka集群,所以安装完docker后就准备安装docker-compose,但在安装过程中确碰到了各种问题,搞了两个半天再通过翻墙工具才终于搞定。         首先看了篇文章显示安装前要对应docker版本。 compose文件格式版本

    2024年02月11日
    浏览(40)
  • Kafka重复消费、Dubbo重复调用问题排查

            本业务为车机流量充值业务,大致流程为:收到微信、支付宝端用户支付成功回调后,将用户订单信息发送至kafka中;消费者接收到kafka中信息后进行解析,处理用户订单信息,为用户订购相关流量包(调用电信相关接口),订购成功/失败后会通过MQTT发送订购成功

    2024年03月24日
    浏览(49)
  • 安装SQL Server 2014的坎坷之路(安装中遇到的错误以及解决方案)

    目录 目录 一、SQL Server 2014下载、安装与使用 二、SQL Server 2014安装的坎坷之路 1、解决SQL安装规则需要 Microsoft .NET Framework 3.5 Service Pack 1失败 (1)检查电脑中是否有net 3.5  (2)安装net 3.5 2.解决“未能加载文件或程序集\\\"Microsoft.SqlServer.Sqm, Version=12.0.0.0,Culture=neutral”问题 3、解

    2024年02月04日
    浏览(80)
  • 从服务员到高级测试工程师,我的坎坷之路谁又能懂

      首先要感谢那些嘲讽我代码写的烂的人,五年开发经验嘲笑刚出校门踏入社会的我,让我放弃了开发工作,走向测试的康庄大道。此外,曾经的开发经验对我测试工作的帮助是无与伦比的。数据库,编程语言,liunx,环境部署维护,程序打包,参与代码走查,专项测试,性

    2023年04月27日
    浏览(45)
  • AI帮写会重复吗?揭秘AI写作工具的重复率问题

    大家好,小发猫降重今天来聊聊AI帮写会重复吗?揭秘AI写作工具的重复率问题,希望能给大家提供一点参考。 以下是针对论文重复率高的情况,提供一些修改建议和技巧,可以借助此类工具: AI帮写会重复吗?揭秘AI写作工具的重复率问题 在自媒体时代,AI写作工具越来越受

    2024年04月10日
    浏览(83)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包