分布式系统中,有一些需要使用全局唯一ID的场景,有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成,这时候通常我们会选择使用雪花算法
| 1 2 | snowflake的结构如下(每部分用-分开): 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 |
第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年) 然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) 最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)。
一般创建雪花算法的时候,依赖:
| 1 2 3 | Params: workerId – 终端ID datacenterId – 数据中心ID |
两个参数,这两个值的取值都为 0~31 之间的整型。
提供了一个基于Redis的实现方式:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | import java.io.Serializable; import java.util.linkedHashMap; import java.util.Map; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.support.atomic.RedisAtomicLong; public class SnowflakeInitiator { @Data @Builder @NoArgsConstructor @AllArgsConstructor public static class SnowIdDto implements Serializable, Comparable private Long timestamp; private Integer dataCenterId; private Integer workerId; @Override public int compareTo(SnowIdDto o) { long ex = this.timestamp - o.getTimestamp(); return ex > 0 ? 1 : -1; } } private static final Integer DATA_SIZE = 32; private static final String[] RADIX_STR = new String[]{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v"}; private static Map private static final String SNOW = "RedisPrefix"; static { for (int i = 0; i < DATA_SIZE; i++) { RADIX_MAP.put(RADIX_STR[i], i); } } private SnowIdDto calculateDataIdAndWorkId(RedisTemplate redisTemplate, String appName) { String key = SNOW + appName; RedisAtomicLong redisAtomicLong = new RedisAtomicLong(key, redisTemplate.getConnectionFactory()); long andIncrement = redisAtomicLong.getAndIncrement(); long result = andIncrement % (DATA_SIZE * DATA_SIZE); String str = StringUtils.leftPad(Integer.toString(Math.toIntExact(result), DATA_SIZE), 2, "0"); Integer dataId = RADIX_MAP.get(str.substring(0, 1)); Integer workId = RADIX_MAP.get(str.substring(1, 2)); return new SnowIdDto(System.currentTimeMillis(), dataId, workId); } } |



