如何产生一个全局唯一的流水号(附demo)

article/2025/11/8 1:45:09

本文介绍如何使用最简单的方法产生一个全局唯一的流水号,支持集群,性能可靠,并且经过实际的应用

 

唯一流水号的格式为当前系统时间+当前服务器编号+并发序列号,长度最短可为17位,每毫秒支持生成多个并且支持集群部署

 

废话不多说,直接上demo,以下demo只需要把连接数据库的工具类Dbutil换成你自己的就可以直接使用了,demo运行成功后需要注意下文中的注意事项

package com.helianxiaowu.utils;import com.newcapec.dao.SysClusterNodeDao;import org.apache.commons.lang.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.net.InetAddress;import java.net.UnknownHostException;import java.util.Date;import java.util.Map;import java.util.Random;/** * @title 唯一流水号生成器 * @desc * @author helianxiaowu * @date 2020/1/18 下午 11:48 */public class PrimaryGenerator {    private static final Logger logger = LoggerFactory.getLogger(PrimaryGenerator.class);    // 单例模式    private static PrimaryGenerator primaryGenerator = new PrimaryGenerator();    private PrimaryGenerator() {}    /**     * 对外提供单例对象     * @return     */    public static PrimaryGenerator getInstance() {        return primaryGenerator;    }    /**     * @title 生成一个唯一编号     * @desc     * @author W.jw     * @date 2018/5/9 14:23     */    public synchronized String make() {        // 获取当前系统的时间戳        Date now = new Date();        long timestamp = now.getTime();        /**         * 判断当前时间戳和上一次产生流水号的时间是否相等         *   如果不相等,可以使用当前时间戳         *   如果相等,判断并发数是否达到最大值         *     如果达到最大值,需要等待下一个毫秒作为新的时间戳         *     如果没有达到最大值,则可以使用当前序列值         */        if (this.lastTime == timestamp) {            serial = serial + 1 ;            if (serial == maxSerial) {                timestamp = this.tilNextMillis(this.lastTime);            }        } else {            serial = 0;        }        // 将当前系统时间赋值给lastTime,方便下一次做判断        this.lastTime = timestamp;        // 保证序列号是3位,产生的订单号长度一样。也可以不补位,订单号长度会短点        String.format("%03d", serial);        // 把当前系统时间格式化为yyMMddHHmmssSSS        String time = DateUtil.dateToString(now, "yyMMddHHmmssSSS");        // 唯一流水号 = yyMMddHHmmssSSS+当前服务器唯一编号+序列号        StringBuilder sb = new StringBuilder(time).append(getMachineId()).append(serial);        return sb.toString();    }    /**     * 等待下一个毫秒     * @param currentTime 当前系统时间     * @return     */    private long tilNextMillis(long currentTime) {        long timestamp = System.currentTimeMillis();        while (timestamp <= currentTime) {            timestamp = System.currentTimeMillis();        }        return timestamp;    }    /**     * 获取当前服务器的编号,固定长度为2位     *   从数据库中获取当前服务器的ip在服务器中对应的唯一id     *   如果id存在,则使用id作为唯一编号     *   如果id不存在,获取当前服务器的ip并插入数据库得到id     *     * @return     */    private String getMachineId() {        if (this.machineId != null) {            return this.machineId;        }        /**         * 获取当前服务器的ip,如果没有获取到ip则随机返回一个两位数         */        String ip = this.getIp();        if (ip == null) {            // 随机返回两位数并且加上100,保证随机返回的数值和数据库中的id值不冲突            return String.format("%02d", new Random().nextInt(100) + 100);        }        /**         * 从数据库中获取当前服务器的ip对应的id值         *   如果没有则插入一条记录并获取id值         *   如果有则直接使用         */        Map<String, Object> nodeMap = DbUtil.getByIp(ip);        if (nodeMap == null || nodeMap.size() == 0) {            try {                DbUtil.save(ip);                nodeMap = DbUtil.getByIp(ip);            } catch (Exception e) {                logger.error("插入集群节点信息失败:{}" , e);                return String.format("%02d", new Random().nextInt(100) + 100);            }        }        this.machineId = String.format("%02d", nodeMap.get("id"));        return this.machineId;    }    /**     * 获取当前服务器ip     * @return     */    private static String getIp() {        InetAddress address = null;        try {            address = InetAddress.getLocalHost();        } catch (UnknownHostException e) {            e.printStackTrace();            return null;        }        return address.getHostAddress();    }    /**     * 最后一次生成订单号的时间,格式yyMMddHHmmssSSS     */    private long lastTime = 0;    /**     * 当前服务器在集群内的编号     */    private String machineId;    /**     * 并发序列号     */    private long serial = 0L;    /**     * 允许并发的最大值     */    private final int maxSerial = 999;}

 

注意事项

1.本demo需要保证单例运行,否则会产生重复的流水号。保证单例模式的方法有很多种,如:使用单例模式或把demo交由spring管理,demo中使用的是单例模式

2.序列号的最大值不可以无限大,需要考虑服务器的配置。建议使用999,每毫秒产生1000个流水号,一秒就可以产生60*1000个流水号,并发性已经很好了

3.本demo中机器编号限制为两位,最大支持99个服务器,如果你的集群中服务器数量超过99个,则需要把编号的位数扩大。当然,服务器的数量不足10个的话,也可以调小,流水号的长度也会短点


扫码关注我的公众号

每天进步一点点,不负光阴,度几扶人

 


http://chatgpt.dhexx.cn/article/f4DuEheE.shtml

相关文章

记小辉人生中的第一刀

本篇是小辉趴在床上写完的博客&#xff0c;讲的是半个月前“肛裂肛瘘混合痔”手术到术后两周的康复过程以及一些注意事项和提示&#xff0c;给读者们的健康提个醒&#xff0c;欢迎转发给身边正在经历痛苦或者犹豫的朋友。 希望大家都身体健康&#xff0c;永远用不上我下面提到的…

砍一刀免费拿营销玩法解说

大家应该都收到拼多多的砍价链接&#xff0c;小来对拼多多这种扰民是推广方式非常反感&#xff0c;但不得不承认&#xff0c;这个营销方式却是拼多多最有效的营销方式之一。 拼多多最新的财报显示&#xff0c;拼多多2020年活跃买家数为7.884亿&#xff0c;首度超越阿里位居全国…

【leetCode:剑指 Offer】06. 从尾到头打印链表

1.题目描述 输入一个链表的头节点&#xff0c;从尾到头反过来返回每个节点的值&#xff08;用数组返回&#xff09;。 2.算法分析 倒序输出&#xff0c;使用栈Stack的数据结构。 先将链表中的元素入栈&#xff0c;然后遍历栈内元素&#xff0c;将元素加入到数组中。 3.代码实现…

流水

这几天怎么过去的 在纸上画一笔 就这么轻松 没和任何人联系 似乎很忙 总是在上课上课上课 从这个教室到那个教室 从这个实验室到那个实验室 嗜睡得厉害 心里很空 很久没有S的消息 有点冷

R语言--readr包读写数据

文章目录 前言一、发现问题二、分析问题三、解决问题四、读写速度总结吐槽 前言 当你使出了浑身解数&#xff0c;read.csv和read.table还是无法读入数据时&#xff0c;或许可以尝试一下readr包中的read_table&#xff0c;read_csv等函数。尝试一下&#xff01; 我是一个很专一…

R语言入门:读取csv文件及获取统计数值(1)

本次编程尝试使用R语言读取csv文件“filesize.csv”&#xff0c;并根据内容绘制统计图和得出平均数&#xff0c;中位数和方差等简单数据。 csv文件内容1001个数值至少大于1000的小数组成&#xff0c;每行一个&#xff0c;共计1001行 在打开文件之前&#xff0c;我们可能会遇到…

R语言 读取文件

1. R读取txt文件 使用R读取txt文件直接使用read.table()方法进行读取即可&#xff0c;不需要加载额外的包。 read.table("/home/slave/test.txt",headerT,na.strings c("NA"))注意&#xff0c;此处的na.strings c("NA") 的意思是文件中的缺失数…

R从文件中读取数据,输出文件

看了几天的书&#xff0c;终于到这一步了&#xff0c;说实话&#xff0c;用R来做统计&#xff0c;很少有人手动的去输入那些数字&#xff0c;肯定是从别的地方导入的&#xff0c;我们用来处理就可以了&#xff0c;所以到这里才算是真正的入门&#xff0c;前面都是做基础的练手。…

R语言中文件或数据的读写

文章目录 系统数据的读写xlsx文件的读写举例说明具体代码结果如下 csv文件的读写读取csv文件写入csv文件使用readr包读取csv文件使用data.table包读取csv文件 scan函数scan()函数的使用语法参数含义描述实例&#xff1a; 系统数据的读写 save(a,b,file"test.RData")…

掌握R语言文件读取方法

目标 掌握 R语言文件读取方法 学习笔记 utils包内Date Input用法base包内readLines用法stringi包内stri_read_linesxlsx包内Date Input用法readr包内 Read a delimited file 用法 1.utils包内Date Input用法 以read.table为例。 read.table参数详细说明见http://www.360doc…

R语言入门(15)_读取文件(read)

目录 一、read.table() 读取工作路径下的纯文本文件&#xff08;.txt&#xff09;(.csv) 1、工作路径的设置 2、head函数——只显示数据前几行 3、read.table()的其他一些参数 二、与read.table相类似的函数 三、read.table()读取网络上的文本文件 三、读取非文本文件 …

使用R读取并查看数据

本篇文章介绍如何使用R读取并查看数据&#xff0c;包含一些最基础的函数使用方法和说明。后面还会陆续介绍数据清洗&#xff0c;匹配和提取等相关的操作。 查看函数帮助 对于新手来说&#xff0c;在使用R时最重要的是了解不同函数的使用方法。很多时候我们都是边用边学的状态&…

R语言如何从外部读取数据到R中

R语言可以从键盘&#xff0c;文本&#xff0c;excel&#xff0c;access&#xff0c;数据库&#xff0c;专业处理软件sas 一、使用键盘的输入 mydata<-data.frame(agenumeric(0),gendercharacter(0),weightnumeric(0)) mydata<-edit(mydata) 二、读入带有分隔符文本格式…

R语言入门——数据快速读取与查看(含实例代码和参数讲解)

R语言数据读取 介绍引言结构安排 数据读取函数文本数据readLines函数 键盘键入数据scan函数讲解 表格数据.xlsx文件介绍表格数据函数参数介绍 快速读入参数介绍竞赛数据练习2020美赛C题2019华为杯E题 数据查看函数表 介绍 引言 毕业季来临&#xff0c;很多小伙伴都在积极准备…

R语言读CSV、txt文件方式以及read.table read.csv 和readr(大数据读取包)

首先准备测试数据*(mtcars)分别为CSV. TXT **2018博客之星评选&#xff0c;如果喜欢我的文章&#xff0c;请投我一票&#xff0c;编号&#xff1a;No.009** [支持连接](https://blog.csdn.net/HHTNAN/article/details/85330758) ,万分感谢&#xff01;&#xff01;&#x…

R语言之读取文件夹的数据

读取文件路径&#xff1a;一层目录&#xff08;“示例”&#xff09;、二层目录&#xff08;“数据1”、“数据2”&#xff09;下的表格数据。 “示例”文件下&#xff1a; “数据1”文件下&#xff1a; “数据2”文件下&#xff1a; 读取文件夹 rm(listls()) #清除变量 …

R入门(一)----读取数据、查看数据

感谢大鹏dapengde 创建数据 #在相应位置新建文件夹 dir.create(E:/R/R lab/学R/r4r) #将数据文件存进文件夹 write.csv(as.data.frame(t(matrix(co2,12,dimnames list(month.abb,unique(floor(time(co2))) ) ))),file E:/R/R lab/学R/r4r/co2.csv)读取数据 R数据的导入与…

手把手教你用R语言读取CSV文件

导读&#xff1a;R语言有许多种方法去获取数据&#xff0c;最常用的是读取CSV文件。 作者&#xff1a;Jared P. Lander 来源&#xff1a;大数据DT&#xff08;ID&#xff1a;hzdashuju&#xff09; 读取CSV文件最好的方法是使用read.table函数&#xff0c;许多人喜欢使用read.c…

R语言中 数据读取与写入

R中其他读取: source #读取R代码 dget #读取R文件 load #读取工作空间 一。R语言读取文本文件(.txt) 在R语言中,读取文本文件的函数可以是:read.table()、scan()、read.fwf(),也可以将.txt转换为.csv或.xlsx文件用其他函数读取。 1. read.table() 参数解释: …

R语言——数据格式和数据读取

R语言——数据读取之详解 福尔摩斯曾说过:“数据,数据,没有数据的推理是罪恶!” 不过比起有意思的统计分析,数据的导入与导出显得十分的无趣,但是不得不说统计分析的数据导入与导出是个让人沮丧的任务,而且耗时巨大。 今天分享的是R数据的储存数据格式,及其R中数据的输…