分布式环境ID生成策略
1,id生成策略之snowflake 算法
package com.test; /**** * id生成策略之snowflake 算法 * 此算法是 twitter 开源的分布式 id 生成算法,采用 Scala 语言实现,是把一个 64 位的 long 型的 id, * 用其中的 41 bits 作为毫秒数, * 用 10 bits 作为工作机器 id,12 bits 作为序列号。 * @author * */ public class IdWorker { private long workerId; private long datacenterId; private long sequence; public IdWorker(long workerId, long datacenterId, long sequence) { // sanity check for workerId // 这儿不就检查了一下,要求就是你传递进来的机房id和机器id不能超过32,不能小于0 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)); } System.out.printf( "worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d", timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId); this.workerId = workerId; this.datacenterId = datacenterId; this.sequence = sequence; } private long twepoch = 1288834974657L; private long workerIdBits = 5L; private long datacenterIdBits = 5L; // 这个是二进制运算,就是 5 bit最多只能有31个数字,也就是说机器id最多只能是32以内 private long maxWorkerId = -1L ^ (-1L << workerIdBits); // 这个是一个意思,就是 5 bit最多只能有31个数字,机房id最多只能是32以内 private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); private long sequenceBits = 12L; private long workerIdShift = sequenceBits; private long datacenterIdShift = sequenceBits + workerIdBits; private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; private long sequenceMask = -1L ^ (-1L << sequenceBits); private long lastTimestamp = -1L; public long getWorkerId() { return workerId; } public long getDatacenterId() { return datacenterId; } public long getTimestamp() { return System.currentTimeMillis(); } public synchronized long nextId() { // 这儿就是获取当前时间戳,单位是毫秒 long timestamp = timeGen(); if (timestamp < lastTimestamp) { System.err.printf("clock is moving backwards. Rejecting requests until %d.", lastTimestamp); throw new RuntimeException(String.format( "Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } if (lastTimestamp == timestamp) { // 这个意思是说一个毫秒内最多只能有4096个数字 // 无论你传递多少进来,这个位运算保证始终就是在4096这个范围内,避免你自己传递个sequence超过了4096这个范围 sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0; } // 这儿记录一下最近一次生成id的时间戳,单位是毫秒 lastTimestamp = timestamp; // 这儿就是将时间戳左移,放到 41 bit那儿; // 将机房 id左移放到 5 bit那儿; // 将机器id左移放到5 bit那儿;将序号放最后12 bit; // 最后拼接起来成一个 64 bit的二进制数字,转换成 10 进制就是个 long 型 return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; } private long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } private long timeGen() { return System.currentTimeMillis(); } // ---------------测试--------------- public static void main(String[] args) { IdWorker worker = new IdWorker(1, 1, 1); for (int i = 0; i < 30; i++) { System.out.println(worker.nextId()); } } }
Snowflake
1. Snowflake算法是基于二进制的,对于像我这样基础不扎实的理解起来还是比较困难。
2. Snowflake集群环境下需要保证时钟同步,对运维能力有一定要求;一旦时钟错乱,又刚好是高并发时,会导致大量异常序号。
3. 如果公司运维能力有限,不适合用Snowflake。
4. 百度开源的UidGenerator(仅支持单机部署)使用Snowflake算法,单机QPS可达600万。项目说明:https://github.com/baidu/uid-generator/blob/master/README.zh_cn.md 。
5. 美团Leaf(分布式ID生成系统),QPS近5万。项目地址:https://tech.meituan.com/2017/04/21/mt-leaf.html 。
微信序列号生成器
文档地址:https://www.infoq.cn/article/wechat-serial-number-generator-architecture
1. 递增但不连续的数字序列解决方案。
2. 设计目标QPS1000万以上。
3. 通过在递增过程中使用“步长”将每秒磁盘写入由1000万级降至1万。
4. 设计原理相对于Snowflake更通俗易懂。
5. 可以使用hash的负载均衡策略组建集群。
6. 缺点:需要自己实现集群中机器增减后更新负载均衡策略的逻辑。
使用redis做发号器要考虑的问题也很多:
1. redis宕机,数据还没持久化到硬盘;
2. redis宕机后,发生主从切换,怎么保证序列正常;
3. redis要通过应用程序封装一次,这样会多一次IO操作,如果生成100万个序列,也要访问100万次redis,怎么优化?
总结:
1. 高并发场景下可以借助redis生成序列,但不适合依赖redis的自增操作实现序列递增;
2. 需要快速实现发号器功能,能容忍序列重复,可以直接使用redis实现,因为简单。
浙公网安备 33010602011771号