当前位置: 首页 > 知识库问答 >
问题:

java - 服务器集群下根据年月日生成唯一编号重复?

应涵容
2023-08-22
开发环境 springboot + mybatis-plus
实现类伪代码如下
@Transactional(rollbackFor = Exception.class) public String getSeq(int id) {        SequenceMapper sequenceMapper = SpringContextUtis.getBean("sequenceMapper", SequenceMapper.class);         // 获取当前时间并格式化 yyyyMMddHHmm        String formatDate = dateFormatter.format(now);        // 取出数据库中的序列化对象        // 获取上一次的序列对象        Sequence sequence = sequenceMapper.selectOne(Wrappers.<Sequence>lambdaQuery().eq(Sequence::getId, 1));        // 如果数据库中存在         if (sequence != null) {            // 时间相同则将排序号加1            if (sequence中日期字符串 == formatDate) {               int last = sequence.last += 1;               sequence.last = last;             } else {                // 时间不同 则直接创建一个 当前时间 + 排序号的 序列                sequence.formatDate = formatDate;                sequence.last = last;            }        } else {            // 如果这种类型的序列从未创建过 则创建一个 当前时间 + 排序号的序列            sequence = new Sequence();            sequence.id = 1;            sequence.formatDate = formatDate;            sequence.last = formatDate + "01";            // 插入到数据库               if(sequenceMapper.insert(sequence) > 0)                return sequence.format + last;            else               throw new RuntimeException();        }        // 保存到数据库中 并记录上一次的 时间 和 排序号       if(sequenceMapper.update(sequence, Wrappers.<Sequence>lambdaUpdate().eq(Sequence::getId(), sequence.id)) > 0)          return sequence.format + last;       else          throw new RuntimeException();
调用类伪代码如下
   public String getUserSeq(){        // 这里使用 redisson 作为分布式锁        RedissonClient redissonClient = SpringContextUtis.getBean("redissonClient", RedissonClient.class);        RLock lock = redissonClient.getLock("sequence");        String value = null;        // 只有真正获取到值才停止        while(value == null) {            lock.lock();            try {               value = sequenceGenerate.getSeq(1);            }catch(Exception exception){                log.error("获取失败,继续获取");            }finally {                lock.unlock();            }        }        return value;    }
代码基本就上面那些,在本地单机环境下没出现过 序列字符串 重复的问题;在集群环境下时而出现,按照我的理解来说,在创建时已经使用redisson锁住了,应该是不会出现数据脏读的问题,这样应该是不会重复的。求各位大佬帮忙解答一下,十分感谢。

共有1个答案

奚翰海
2023-08-22

你这样还是会有脏数据的。

因为在 unlock 的时候,事务是没有提交的,此时数据还没写进去,其它进程又进来读数据了。

换种说法,目前你的做法,事务应该是整个线程结束后才被提交,而不是执行完 getSeq 就提交。

你这种情况我之前遇到,单纯的使用 @Transactional(rollbackFor = Exception.class),不论嵌套再多方法,使用的实际上都是同一个事务,当你打开一个事物的时候。

试想一下,当你打开同一个事务时,你如何让程序知道去主动提交事务呢?spring 框架并没有给你获取当前线程事务,手动提交的接口。

因此唯一的可能就是当线程结束后,spring 才知道此时要提交事务了。

方法一:

@Transactional(rollbackFor = Exception.class)
改成:

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)

让方法结束时,强行提交到数据库。

Propagation.REQUIRES_NEW:含义是,参数表示当前方法需要在新的事务中运行,加上之后,才能让方法结束后就立即提交事务。

方法二:

更加彻底的方法是使用 TransactionTemplate 新开一个事务,这样掌控起来更加容易和便于理解。

    @Autowired    TransactionTemplate template;    public void test() {        template.execute(status -> {            // 数据库更新            return null;        });    }
 类似资料:
  • 问题内容: Oracle是否具有内置功能来根据其各个组成部分(年,月和日)创建仅对缺失数据返回null的日期? 我知道,但是我需要首先编写一个字符串,并且运算符和函数都无法轻松处理丢失的数据: 只要是我们最终和: 问题答案: 例如,您可以使用: 不幸的是,Oracle没有一种方法(如果可能)进行转换,否则将返回NULL。SQL Server最近为此目的而引入。 一种选择是为失败的转换编写带有异常处

  • 我的数组包含从0到整数的随机唯一数。最大值。 如何生成唯一的id/签名(int)来唯一地标识每个数组,而不是搜索每个数组并检查每个数字。 例如 每个数组可以有不同的长度,但数字在数组中不重复,可以在其他数组中重复。每个数组的唯一id的目的是通过id来识别它,以便快速进行搜索。数组包含组件的id,数组的唯一签名/id将标识其中包含的组件。 此外,无论数组中的值的顺序如何,生成的id应该是相同的。像{

  • 尝试将我的 Android 应用构建到 APK 时,构建服务器出错且无法构建。但是,在模拟器中,应用程序工作正常,并且我认为错误的类是由编译器创建的(它不是应用程序内部的类之一) 下面是错误日志: -

  • 本文向大家介绍php 根据自增id创建唯一编号类,包括了php 根据自增id创建唯一编号类的使用技巧和注意事项,需要的朋友参考一下 在开发过程中,我们数据表一般都使用自增数字作为id主键,而id是数字型,不容易理解。我们把id按一定格式转为编号后,很容易根据编号知道代表的是什么内容。 例如订单表id=20160111197681234,只看id我们并不知道这个id是订单表的id,而转为编号O-20

  • 本文向大家介绍PHP生成唯一订单号,包括了PHP生成唯一订单号的使用技巧和注意事项,需要的朋友参考一下 在网上找了一番,发现这位同学的想法挺不错的,redtamo,具体的请稳步过去看看,我作简要概述,该方法用上了英文字母、年月日、Unix 时间戳和微秒数、随机数,重复的可能性大大降低,还是很不错的。使用字母很有代表性,一个字母对应一个年份,总共16位,不多也不少. 1. 生成效果: 2. 输出结果

  • 问题内容: 我在sql中有一个用户表,他们每个都有生日。我想将他们的出生日期转换为他们的年龄(仅年),例如日期:年龄:14,并将更改为年龄: 在这里,我想显示用户的日期: 问题答案: 功能:, MySQL > = 5.0.0 功能:,