分布式ID生成方案
程序员文章站
2022-05-04 17:26:02
...
1、方案一:数据库自增长序列或字段
比如用户的ID值,就可以通过在数据库的用户表中新增一个字段,然后这个字段可以设置为每次增长n。比如库1每次增长2,库2每次增长1。(当前系统使用的方案)
alter table t_ott_account add column user_id bigint;
CREATE SEQUENCE user_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
alter sequence user_id_seq restart with 1000000;
alter sequence user_id_seq increment by 2 ;
alter table t_ott_account alter column user_id set default nextval('user_id_seq');
优点:简单方便
缺点:
不同数据库语法和实现不同,数据库迁移的时候或多数据库版本支持的时候需要处理。
在单个数据库或读写分离或一主多从的情况下,只有一个主库可以生成。有单点故障的风险。
在性能达不到要求的情况下,比较难于扩展。
如果遇见多个系统需要合并或者涉及到数据迁移会相当痛苦。
分表分库的时候会有麻烦。
2、方案二:UUID
全球唯一
优点:简单,代码方便。
缺点:不适合做用户ID这种
3、方案三:Redis生成ID
Redis生成ID的方案可以在数据库自增长序列性能不满足要求的时候来使用。
4、Twitter的snowflack算法
参考:https://github.com/twitter/snowflake
思路:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0
public class SnowFlake
{
/**
* 起始的时间戳
*/
private final static long twepoch = 1557825652094L;
/**
* 每一部分占用的位数
*/
private final static long workerIdBits = 5L;
private final static long datacenterIdBits = 5L;
private final static long sequenceBits = 12L;
/**
* 每一部分的最大值
*/
private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
private final static long maxSequence = -1L ^ (-1L << sequenceBits);
/**
* 每一部分向左的位移
*/
private final static long workerIdShift = sequenceBits;
private final static long datacenterIdShift = sequenceBits + workerIdBits;
private final static long timestampShift = sequenceBits + workerIdBits + datacenterIdBits;
private long datacenterId; // 数据中心ID
private long workerId; // 机器ID
private long sequence = 0L; // ***
private long lastTimestamp = -1L; // 上一次时间戳
public SnowFlake(long workerId, long datacenterId)
{
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;
}
public synchronized long nextId()
{
long timestamp = timeGen();
if (timestamp < lastTimestamp)
{
throw new RuntimeException(String.format(
"Clock moved backwards. Refusing to generate id for %d milliseconds",
lastTimestamp - timestamp));
}
if (timestamp == lastTimestamp)
{
sequence = (sequence + 1) & maxSequence;
if (sequence == 0L)
{
timestamp = tilNextMillis();
}
}
else
{
sequence = 0L;
}
lastTimestamp = timestamp;
return (timestamp - twepoch) << timestampShift // 时间戳部分
| datacenterId << datacenterIdShift // 数据中心部分
| workerId << workerIdShift // 机器标识部分
| sequence; // ***部分
}
private long tilNextMillis()
{
long timestamp = timeGen();
while (timestamp <= lastTimestamp)
{
timestamp = timeGen();
}
return timestamp;
}
private long timeGen()
{
return System.currentTimeMillis();
}
public static void main(String[] args)
{
SnowFlake snowFlake = new SnowFlake(1, 1);
long nextId = snowFlake.nextId();
System.out.println(nextId);
// 136931497562214400 136931566357188608
}
}
另外还有zookeeper生成唯一ID和MongoDB的objectID,可以作为参考
上一篇: 浅谈被jQuery抛弃的函数及替代函数_jquery
下一篇: Java内存模型之重排序