在开发中几乎所用的系统都会涉及到唯一单号的生成,通常分为两种:一种是有序的生成带有一定规则的单号,另一种是无序的随机生成唯一的单号。这里主要是介绍怎么才能在不同场景下生成有序带有一定规则的单号。

1.synchronized 同步获取单号

创建一个表来存储单号,使用唯一索引确保获得的每一个单号都是唯一的。

CREATE TABLE `generate_no` (`tid` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',`serial_no` varchar(50) NOT NULL COMMENT '流水号',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '最后修改时间',PRIMARY KEY (`tid`),UNIQUE KEY `uk_serial_no` (`serial_no`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8mb4

先通过SQL获取单号,再把单号存到表里保证表里的单号是最新,加synchronized是防止并发的时候生成相同的单号,相同的单号在插入数据库触发唯一索引报错。

 /*** 获取单号* @param prefix 前缀* @return*/public synchronized String getSerialNo(String prefix) {String dateStr = new SimpleDateFormat("yyMMdd").format(new Date());String param = prefix + dateStr;String serialNo = baseMapper.getSerialNo(param);GenerateNo generateNo = new GenerateNo();generateNo.setSerialNo(serialNo);this.save(generateNo);return serialNo;}

获取单号SQL

SELECT CONCAT(#{param},LPAD(SUBSTR(IFNULL(MAX(serial_no),1),-4)+1,4,0))
FROM generate_no
WHERE serial_no LIKE CONCAT(#{param}, '%')

2.利用redis生成单号

没啥说的直接上代码。

        @Autowiredprivate JedisCluster jedis;/*** 从redis获取最新单号* 生成规则 前缀 + 日期 + 流水码(五位)* @param prefix  前缀* @return*/public String getAutoNumber(String prefix) {String dateStr = new SimpleDateFormat("yyMMdd").format(new Date());//redis 存储的 keyString autoKey = prefix + dateStr;//校验key是否存在if (jedis.exists(autoKey)) {//key值不存在插入,如果存在即操作失败,不会覆盖value值jedis.setnx(autoKey, String.valueOf(0));}//获取流水号String autoNumber = String.valueOf(jedis.incr(autoKey));DecimalFormat df = new DecimalFormat("00000");//流水号不足5位补充成5位autoNumber = df.format(Integer.parseInt(autoNumber));//前缀 + 时间 + 流水码String number = autoKey + autoNumber;return number;}

3.乐观锁获取单号

3.1 创建单号表

将单号拆分成三个字段(前缀+日期+流水号),避免表数据太多,所以表里面只存最大的单号。

CREATE TABLE `generate_no` (`tid` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',`prefix` varchar(100) NOT NULL COMMENT '单号前缀',`serial_no` int(5) NOT NULL COMMENT '流水号',`create_date` varchar(8) NOT NULL DEFAULT '' COMMENT '创建日期',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '最后修改时间',PRIMARY KEY (`tid`),UNIQUE KEY `uk_prefix_serial_no` (`prefix`,`serial_no`,`create_date`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8mb4

3.2 获取单号的流程图

代码就不写了,画个流程图吧,按照流程图codeing就可以了,编码过程中注意异常的catch和版本控制。

4.mysql + redis + 分布式锁生 成单号

4.1 创建单号表

将单号拆分成三个字段(前缀+日期+流水号),避免表数据太多,所以表里面只存最大的单号。

CREATE TABLE `generate_no` (`tid` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',`prefix` varchar(100) NOT NULL COMMENT '单号前缀',`serial_no` int(5) NOT NULL COMMENT '流水号',`create_date` varchar(8) NOT NULL DEFAULT '' COMMENT '创建日期',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '最后修改时间',PRIMARY KEY (`tid`),UNIQUE KEY `uk_prefix_serial_no` (`prefix`,`serial_no`,`create_date`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8mb4

4.2 获取单号流程图

任性不写代码,看流程图吧。

redis存储的单号使用List结构,通过lpop命令来弹出头部元素并返回来实现取单号。

5.总结

上面介绍了4种单号的生成方法,它们有个字的优缺点和使用场景。

第一种使用同步方法代码简洁,实现方便,但是效率低,只适合访问量较小的单体应用系统。

第二种使用redis生成单号代码简洁、实现方便、满足高并发,但是在用于redis数据存储在内存中,有key值丢失的风险。

第三种使用乐观锁生成单号是第一种的改良版,能满足高并发环境,由于每次生成单号都需要访问最少两次数据库,同样存在性能问题,适合访问量小的系统。

由于单独使用mysql或者redis都存在一些问题,也就产生了mysql结合redis的方案,也就是第四种方案,每次访问mysql都生成1000个单号存放在redis里面,将最大单号存放在mysql中,解决了mysql性能瓶颈和redis特殊情况下key值丢失问题,适用于大部分高并发、分布式系统使用。

java流水单号生成相关推荐

  1. java充值卡号生成_Java工具集-通用卡号转换

    代码示例 /** * @program: simple_tools * @description: 用户卡生成规则类 卡号原则:将10进制卡号转成16进制卡号 * @author: Mr.chen * ...

  2. Java 订单号生成

    时间戳+随机数+用户唯一id public static String getOrderIdByTime() {SimpleDateFormat sdf = new SimpleDateFormat( ...

  3. java中生成yyyyMMdd001此类流水单号

    最近项目需求要生成yyyyMMdd001此类流水单号,一开始摸不着头脑,慢慢找到了解决疑惑的方法 我就直接贴代码: 1.截取工具类SubUtils.java public class SubUtils ...

  4. Java实现一个单号生成工具类

    在项目开发的过程中,如果一个系统存在多种不同类型的单据,单号生成就比较难以处理,为此,创造出一个单号生成的工具类就很有必要.直接上代码: 实体类:(数据库字段同下) public class Seri ...

  5. java唯一订单号_java web在高并发和分布式下实现订单号生成唯一的解决方案

    方案一: 如果没有并发,订单号只在一个线程内产生,那么由于程序是顺序执行的,不同订单的生成时间戳正常不同,因此用时间戳+随机数(或自增数)就可以区分各个订单.如果存在并发,且订单号是由一个进程中的多个 ...

  6. java 支付宝退款批次号生成

    /**      * 支付宝批次号生成      * 生成规则:当天日期[8位]+序列号[3至24位],如:201008010000001      * @return      */     pri ...

  7. eclipse编译java项目class文件_动态编译 Java 代码以及生成 Jar 文件

    导读: 最近在看 Flink 源码的时候发现到一段实用的代码,该代码实现了 java 动态编译以及生成 jar 文件.将其进行改进后可以应用到我们的平台上,实现在平台页面上编写 java 代码语句,提 ...

  8. 【java实现点卡生成】

    点卡主要有2部分:卡号和密码.卡号一般由数字组成,密码就不多说了. java中随机数很强大,大家可以自己查.卡号生成使用java中随机数,密码使用uuid,密码可以自己再加点东西之类的.下面是完整代码 ...

  9. 微信公众号生成临时二维码

    微信公众号生成临时二维码 微信公众平台生成带参数的二维码官方文档 分为三个部分: 获取access_token.通过ticket换取二维码.生成带参数的二维码 特别注意:需要有生成二维码的权限. 整个 ...

最新文章

  1. PHP里switch用法举例,PHP Switch语句的功能实例
  2. 张宏江:开源时代如何解决人的思维孤岛
  3. Android--Handler使用
  4. oracle设置缓存大小设置多少,【数据库类※从V$DB_CACHE_ADVICE中设置数据缓冲大小※】...
  5. matplotlib输出图形到网页_必学python库Matplotlib教程分享
  6. 基于VS Code创建Java command-line app
  7. 熟悉又陌生 彪悍徐茂栋的双面人生
  8. 【leetcode-74】搜索二维矩阵
  9. SequoiaDB数据水平分区简介
  10. 谷歌浏览器怎么查找和改变编码格式
  11. 【专家访谈】测试专家 - 陈林钧 访谈记录整理汇总
  12. Windows Phone中Map控件由浅及深
  13. 国际结算习题集及答案
  14. spring 学习书籍
  15. Oracle 12C 最新补丁下载与安装操作指北
  16. CORS跨域-Nginx使用方法(Access-Control-Allow-Origin错误提示)
  17. 今天您大数据营销了吗?
  18. 《回炉重造》——集合(容器)
  19. android能用svg格式,关于Android SVG图形:Android SVG图形 – 将当前PNG文件转换为svg格式的缺点...
  20. 京东区块链之科普篇:京东在区块链技术领域的应用与布局

热门文章

  1. 安装GNS3以及实现一个简单的网络拓扑图
  2. Android RadioButton修改圆圈大小
  3. 阿里技术高P访谈之“呆萌”程序员蒋晓伟为何从Facebook到阿里巴巴
  4. stm32USMART调试组件——HAL库
  5. vue组件间相互传值
  6. 大三计算机专业找不到方向怎么办?
  7. EFCore——IQueryable的延迟执行(14)
  8. 虚拟机安装教程(超详细,附软件)
  9. #今日论文推荐# 弥平仿真与现实的鸿沟:李飞飞、吴佳俊团队发布用于 Sim2Real 迁移的多感官物体数据集
  10. 2021年安全员-C证考试题及安全员-C证新版试题