twitter snowflake: 零配置随机版
程序员文章站
2022-07-03 10:33:22
...
因为无法彻底解决系统时钟后调的问题, 而且嫌配置麻烦. 所以对snowflake做了一下修改:
1.消除了配置.
2.尽可能地消除时钟后调时的可用性问题.
3.生成对数据库索引友好的流水号.
具体实现:
/** * Snowflake java version with random string suffixed. * Translated by rickg@iteye.com on 2017/3/22 from scala version at https://github.com/twitter/snowflake/releases/tag/snowflake-2010. */ class SnowflakeGenerator{ private static final byte[] alphanumerics = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".getBytes(); /*2017-01-01 00:00:00*/ private static final long twepoch = 1483200000000L; private static final long workerIdBits = 5L; /*31*/ private static final long maxWorkerId = -1L ^ (-1L << workerIdBits); private static final long datacenterIdBits = 5L; /*31*/ private static final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); private static final long sequenceBits = 12L; /*4095*/ private static final long maxSequence = -1L ^ (-1L << sequenceBits); /*12*/ private static final long workerIdShift = sequenceBits; /*17*/ private static final long datacenterIdShift = sequenceBits + workerIdBits; /*22*/ private static final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; /*0-31*/ private long workerId; /*0-31*/ private long datacenterId; /*0-4095*/ private long sequence = 0L; private long lastTimestamp = -1L; public SnowflakeGenerator() { this(randomLong(maxWorkerId), randomLong(maxDatacenterId)); } public SnowflakeGenerator(long workerId, long datacenterId) { this(workerId, datacenterId, randomLong(maxSequence)); } public SnowflakeGenerator(long workerId, long datacenterId, long sequence) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); } if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); } this.workerId = workerId; this.datacenterId = datacenterId; this.sequence = sequence; } public String nextId() { return nextLongId() + createRandomAlphanumerics(7); } public synchronized long nextLongId() { long timestamp = System.currentTimeMillis(); if (timestamp < lastTimestamp) { //allow clock moving backward since the result will be suffixed with random string //throw new IllegalStateException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } if (lastTimestamp == timestamp) { sequence = (sequence + 1) & maxSequence; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0; } lastTimestamp = timestamp; return (((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence); } private long tilNextMillis(long lastTimestamp) { long timestamp = System.currentTimeMillis(); while (timestamp <= lastTimestamp) { timestamp = System.currentTimeMillis(); } return timestamp; } private static long randomLong(long maxValue) { return (long) (Math.random() * (maxValue + 1)); } private String createRandomAlphanumerics(int length) { byte[] array = new byte[length]; for (int i = 0; i < array.length; i++) { array[i] = (alphanumerics[(int) (Math.random() * alphanumerics.length)]); } return new String(array); } }
解决的方法无外乎引入随机. 在同一秒的时间内, 出现碰撞的概率为150万万万亿分之一, 就是同一个人连续三次中大乐透一等奖的概率的30分之一. 如果公司有100万台服务器, 每个服务器上都运行着snowflake, 则在同一秒钟内出现碰撞的概率为1万万万亿分之一的数量级.
相对于官方snowflake算法而言, 引入随机无疑是一种倒退, 性能下降25%. 那也是没有办法的事情, 反正够用了.
相关阅读:
twitter snowflake生成流水号的系统时间后调问题
twitter snowflake: kubernetes+docker+zookeeper实现workerId的自动分配
上一篇: 如何使网站用户信任度增加