如何按日期生成订单号

如何按日期生成订单号

需求描述

按日期生成订单号,格式为 2024010100001 ,其中 20240101 是当天日期, 00001 是连续递增的序号,第二天从 00001 重新开始递增,生成的订单号不能重复。

实现方案

考虑到应用可能因为升级等原因重启,应用重启后生成的订单号不能和之前的重复,就不考虑直接在内存中生成订单号。

方案一:通过 Redis 实现

将当天日期字符串作为 Redis String 的 key,使用 Redis String 类型的 incr 命令可以对值做自增操作。

使用 Redis 实现有以下好处:

  • Redis 的命令执行是单线程,不需要考虑并发可能造成订单号重复的问题。
  • Redis 可以对过期的 key 可以自动清理。
  • 相比于数据库,Redis 生成订单号的性能更好。

Java 实现代码如下:

@Service
public class OrderNumServiceImpl implements OrderNumService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @Override
    public String createOrderNum() {
        String key = new SimpleDateFormat("yyyyMMdd").format(new Date());
        Long seqNum = stringRedisTemplate.opsForValue().increment(key);
        if (seqNum == 1) {
            stringRedisTemplate.expire(key, calcTodayRemainSeconds(), TimeUnit.SECONDS);
        }
        return key + String.format("%05", seqNum);
    }
    
    private long calcTodayRemainSeconds() {
        Date date = new Date();
        LocalDateTime current = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
        LocalDateTime target = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())
            .plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
        return ChronoUnit.SECONDS.between(current, target);
    }
}

方案二: 通过数据库实现

如果应用程序只部署了数据库,没有部署 Redis,认为只为生成订单号部署 Redis 没有必要,也可以使用数据库实现。

创建数据表:

CREATE TABLE `order_num` (
  `id` int NOT NULL AUTO_INCREMENT,
   `key` varchar(64),
   `seq_num` int,
    PRIMARY KEY (`id`),
    UNIQUE INDEX `unique_key_idx` (`key`)
)

使用数据库生成订单号就要考虑并发请求的场景,避免生成的订单号重复。

如果应用是单实例部署的,可以在应用程序中对生成订单号的代码加锁(例如 Java 中的 synchronized )。

如果应用是多实例部署的,就需要在数据库加锁。当天记录不存在时需要先在数据库中插入当天的记录,要考虑并发场景可能创建多条记录,可以在数据表中将key字段设置为唯一索引。当天记录更新时对数据库加锁有两种方式:

  • 乐观锁

    对数据表增加 version 字段,更新记录时同时加上 where version={查询出来的 version 字段值},如果更新失败说明有其它线程同时更新了此条记录,需要增加重试机制,例如重试3次。

  • 悲观锁

    查询当天记录时使用如下 SQL 将记录锁定

    SELECT * FROM `order_num` WHERE `key` = ? FOR UPDATE
    

    然后对 seq_num 字段加1后更新

    UPDATE `order_num` SET `seq_num` = `seq_num` + 1 WHERE `key` = ?
    

总结

可以看出,使用数据库方案要考虑和处理的情况比 Redis 复杂,因此尽量考虑使用 Redis 方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值