有时我们不依赖于数据库中自动递增的字段产生唯一ID,比如多表同一字段需要统一一个唯一ID,这时就需要用程序来生成一个唯一的全局ID。

UUID

从Java 5开始, UUID 类提供了一种生成唯一ID的简单方法。UUID是通用唯一识别码 (Universally Unique Identifier)的缩写,UUID来源于OSF(Open Software Foundation,开源软件基金会)的DCE(Distributed Computing Environment,分布式计算环境)规范。UUID 的目的,是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。如此一来,每个人都可以建立不与其它人冲突的 UUID。

UUID是一个128bit的数字,也可以表现为32个16进制的字符(每个字符0-F的字符代表4bit),中间用"-"分割。

  • 时间戳+UUID版本号: 分三段占16个字符(60bit+4bit),
  • Clock Sequence号与保留字段:占4个字符(13bit+3bit),
  • 节点标识:占12个字符(48bit),

UUID的唯一缺陷在于生成的结果串会比较长。

public class GenerateUUID {public static final void main(String... args) {// generate random UUIDsUUID idOne = UUID.randomUUID();UUID idTwo = UUID.randomUUID();log("UUID One: " + idOne);log("UUID Two: " + idTwo);}private static void log(Object object) {System.out.println(String.valueOf(object));}
}

结果为

UUID One: 6b193443-b95d-4462-9902-a6455ebc56d6
UUID Two: 4ef9b375-839b-4150-8f31-1ed85fab63fd

随机数的哈希值

此方法使用SecureRandom和MessageDigest:

  • 启动时,初始化SecureRandom (这可能是一个冗长的操作)
  • 使用 SecureRandom生成一个随机数
  • 创建一个MessageDigest,使用某种摘要算法
  • 将MessageDigest返回的byte[]编码为某种可接受的文本形式
  • 检查结果是否已经被使用;如果尚未使用,则适合作为唯一标识符

MessageDigest类是适合于产生任意数据的“单向散列”。

public class GenerateId {public static void main(String... arguments) {try {SecureRandom prng = SecureRandom.getInstance("SHA1PRNG");String randomNum = Integer.valueOf(prng.nextInt()).toString();MessageDigest sha = MessageDigest.getInstance("SHA-1");byte[] result = sha.digest(randomNum.getBytes());System.out.println("Random number: " + randomNum);System.out.println("Message digest: " + hexEncode(result));} catch (NoSuchAlgorithmException ex) {System.err.println(ex);}}static private String hexEncode(byte[] input) {StringBuilder result = new StringBuilder();char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a','b', 'c', 'd', 'e', 'f'};for (int idx = 0; idx < input.length; ++idx) {byte b = input[idx];result.append(digits[(b & 0xf0) >> 4]);result.append(digits[b & 0x0f]);}return result.toString();}
}

结果为

Random number: -2017013782
Message digest: 2c3bba8d4dbd3699648c5909685d21f9c64b6a8a

Twitter的snowflake

twitter的一个全局唯一id生成器,结果是一个long型的ID。

  • 正数位(1bit):一个符号位,永远是0。
  • 时间戳(41bit) :自从2012年以来的毫秒数,能撑139年。
  • 自增序列(12bit,最大值4096):毫秒之内的自增,过了一毫秒会重新置0。
  • DataCenter ID (5 bit, 最大值32):配置值,支持多机房。
  • Worker ID ( 5 bit, 最大值32),配置值,一个机房里最多32个机器。

Snowflake算法的变化

Snowflake算法生成的唯一ID为long型数值,但如果想在应用中使用int类型的自增ID的话可以做些调整。

时间戳改为分钟(25bit),自增序列(7bit)。自增序列最大值128,在一分钟内会不够使用。可以采用预支方式取下一分钟。

此方式只适用于一个单体应用,不适合分布式系统。

/*** @ClassName: SnowflakeIdWorker3rd* @Description:snowflake算法改进* @author: wanghao* @date: 2019年12月13日 下午12:50:47* @version V1.0* *          将产生的Id类型更改为Integer 32bit <br>*          把时间戳的单位改为分钟,使用25个比特的时间戳(分钟) <br>*          去掉机器ID和数据中心ID <br> *          7个比特作为自增值,即2的7次方等于128。*/
public class SnowflakeIdWorker3rd {/** 开始时间戳 (2019-01-01) */private final int twepoch = 25771200;// 1546272000000L/1000/60;/** 序列在id中占的位数 */private final long sequenceBits = 7L;/** 时间截向左移7位 */private final long timestampLeftShift = sequenceBits;/** 生成序列的掩码,这里为127 */private final int sequenceMask = -1 ^ (-1 << sequenceBits);/** 分钟内序列(0~127) */private int sequence = 0;private int laterSequence = 0;/** 上次生成ID的时间戳 */private int lastTimestamp = -1;private final MinuteCounter counter = new MinuteCounter();/** 预支时间标志位 */boolean isAdvance = false;// ==============================Constructors=====================================public SnowflakeIdWorker3rd() {}// ==============================Methods==========================================/*** 获得下一个ID (该方法是线程安全的)* * @return SnowflakeId*/public synchronized int nextId() {int timestamp = timeGen();// 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}if(timestamp > counter.get()) {counter.set(timestamp);isAdvance = false;}// 如果是同一时间生成的,则进行分钟内序列if (lastTimestamp == timestamp || isAdvance) {if(!isAdvance) {sequence = (sequence + 1) & sequenceMask;}// 分钟内自增列溢出if (sequence == 0) {// 预支下一个分钟,获得新的时间戳isAdvance = true;int laterTimestamp = counter.get();if (laterSequence == 0) {laterTimestamp = counter.incrementAndGet();}int nextId = ((laterTimestamp - twepoch) << timestampLeftShift) //| laterSequence;laterSequence = (laterSequence + 1) & sequenceMask;return nextId;}}// 时间戳改变,分钟内序列重置else {sequence = 0;laterSequence = 0;}// 上次生成ID的时间截lastTimestamp = timestamp;// 移位并通过或运算拼到一起组成32位的IDreturn ((timestamp - twepoch) << timestampLeftShift) //| sequence;}/*** 返回以分钟为单位的当前时间* * @return 当前时间(分钟)*/protected int timeGen() {String timestamp = String.valueOf(System.currentTimeMillis() / 1000 / 60);return Integer.valueOf(timestamp);}// ==============================Test=============================================/** 测试 */public static void main(String[] args) {SnowflakeIdWorker3rd idWorker = new SnowflakeIdWorker3rd();for (int i = 0; i < 1000; i++) {long id = idWorker.nextId();System.out.println(i + ": " + id);}}
}
public class MinuteCounter {private static final int MASK = 0x7FFFFFFF;private final AtomicInteger atom;public MinuteCounter() {atom = new AtomicInteger(0);}public final int incrementAndGet() {return atom.incrementAndGet() & MASK;}public int get() {return atom.get() & MASK;}public void set(int newValue) {atom.set(newValue & MASK);}
}

Java中生成唯一ID的方法相关推荐

  1. java creat uid_关于uniqueidentifier:如何在Java中创建唯一ID?

    本问题已经有最佳答案,请猛点这里访问. 我正在寻找在Java中创建唯一ID作为String的最佳方法. 任何指导表示赞赏,谢谢. 我应该提到我正在使用Java 5. 看看这个stackoverflow ...

  2. java redis 生成唯一id_Redis在集群环境中生成唯一ID

    概述 设计目标:每秒最大生成10万个ID,ID单调递增且唯一.Reidis可以不需要持久化ID. 要求:集群时钟不能倒退. 总体思路:集群中每个节点预生成生成ID:然后与redis的已经存在的ID做比 ...

  3. Postgresql 直接在查询结果中生成唯一ID

    不需要排序 select (row_number() over()) as id from generate_series(1,100) 1 根据指定的字段排序 select (row_number( ...

  4. java 字符串生成唯一id

    UUID.nameUUIDFromBytes((str1).getBytes()).toString();

  5. php给留言分配id_如何使用php生成唯一ID的4种方法

    php生成唯一ID的应用场景非常普遍,如临时缓存文件名称,临时变量,临时安全码等,uniqid()函数基于以微秒计的当前时间,生成一个唯一的 ID.由于生成唯一ID与微秒时间关联,因此ID的唯一性非常 ...

  6. JS生成唯一id方式介绍(UUID和NanoID)

    记录下JS生成唯一id的方法. 1.生成uuid的方法 方法一: function guid() {return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.repl ...

  7. 生成唯一id的几种方法

    生成唯一id的几种方法 生成唯一id的方式有很多,UUID,自动增长列,雪花算法,redis等等. 生成id的要求: 全局唯一 趋势递增 效率高(生成.使用.索引) 控制并发 1.雪花算法(twitt ...

  8. Java生成唯一id的几种方式(已验证)

    1.数据库自增序列方式 数据库方式比较简单,比如oracle可以用序列生成id,Mysql中的AUTO_INCREMENT等,这样可以生成唯一的ID,性能和稳定性依赖于数据库!如mysql主键递增: ...

  9. 关于android开发添加菜单XML文件之后无法在R.java中生成ID的问题

    因为和同学分开做的android软件,现在想整合他做的界面部分,于是拷贝了res和src文件夹的文件,其中包括一个res.menu文件夹中的XML.但是每次将该文件导入到工程总无法自动在R.java中 ...

  10. java唯一id_生成唯一ID的四种办法 程序员必备

    我们在实际编程过程中会经常遇到需要用唯一ID的场合,这些唯一ID还会存到数据库中以便于我们将来进行查询. 例如用户编号.订单编号.客户编号等等,几乎凡是需要用来严格划分用户数据归属性的地方就需要用到唯 ...

最新文章

  1. python学习费用-苏州Python培训学校费用
  2. 一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)
  3. MySQL DBA面试全揭秘
  4. 学习《css世界》笔记之多行文本实现垂直居中
  5. 传统的6d位姿估计fangfa1_李飞飞团队最新论文:基于anchor关键点的类别级物体6D位姿跟踪...
  6. Python函数的参数传递方式
  7. 论文《Learning Spatial Fusion for Single-Shot Object Detection》解读
  8. TensorFlow教程之API DOC 6.3.10. NN
  9. android自定义布局中的平滑移动
  10. 谈谈我对元宇宙的理解
  11. python 收银系统_实现全栈收银系统(Node+Vue)(一)
  12. 得出一个月有多少工作日
  13. 如何linux删除文件夹,linux删除文件夹,教您电脑的linux怎么样删除文件夹
  14. ZZULIOJ-1525-小P找座位
  15. 【C#】关于委托和事件
  16. uni.app小程序实现跳转获取数据
  17. React-blog 搭建属于自己的博客
  18. 围绕增材制造零件可靠性分析,概述增材制造方法的可重复性!
  19. Android逆向之路---让你的微信地区来自火星
  20. 计算机专业行政工作简历,行政管理与计算机专业的个人简历范文

热门文章

  1. PTA——循环移动(思路清奇版)
  2. SpringBoot毕业设计项目源代码SpringBoot+PPT+VUE.js实现博客系统[包运行成功]
  3. 解决jenkins shell执行sonar-scanner提示命令存在的问题
  4. Http协议网络读卡器Aspx网页Request获取刷卡数据Response回应驱动显示
  5. 怎么对视频进行加密,视频一机一码制,防止别人出售(软件 非试用版)
  6. python机器学习《回归 一》
  7. 【AI视野·今日Robot 机器人论文速览 第五十五期】Mon, 16 Oct 2023
  8. python中myf什么意思_python中的负函数
  9. 新版友价源码T5商城(整站6月16升级版)
  10. 前端面试题—css盒子模型