在任何一个系统中,都有需要唯一的一个编号标识一条记录或一个消息,例如一个订单记录或者消息中间件发送的消息这样的需求。在微服务系统,每一个微服务生成的id不能重复,最好保证一定的自增,目前的解决方案有UUID,数据库id自增,数据库生成id,雪花算法,然后根据这些解决方案对应的工具有美团的leaf,百度的UidGenerator。
-
UUID:是java自带的一种id生成方式,uuid能够保证同一时空所有机器都能够保持唯一。
//java使用方式 UUID uuid = UUID.randomUUID(); String id = uuid.toString();
-
数据库id自增:当多个服务在自身系统中能够保持唯一性,但在全局不能保证唯一性的时候,例如使用各个服务中的当前系统时间毫秒数作为id,那么就会导致在某一个毫秒中,生成重复的id.如果所有服务都去通过数据库获取id编号,那么就可以保证id不能重复。
通过维护一张表,表中记录什么类型的自增,自增起始值、自增间隔、下一次自增起始值。例如对人员编号进行从1开始逐一自增。 --type---start---inteval---one_time_total-- --people---1--------1----------1000------ A.在程序中维护startNum,currentNum,endNum,inteval; 一次获取人员编号的过程: select start,inteval,one_time_total from t_number where type = 'people';//获取人员当前编号信息 update start = start + inteval * one_time_total where type = 'people';//更新人员编号信息 得到startNum = start、endNum = start + inteval * oneTimeTotal、inteval = inteval;当前编号从startNum开始,endNum结束,currentNum代表当前编号。 B.通过数据库触发器实现 --biz_type--max_id---step---update_time--- ---people----0-------1000-----currentTime- 创建触发器或者存储过程 Begin UPDATE table SET max_id=max_id+step WHERE biz_tag='people' SELECT tag, max_id, step FROM table WHERE biz_tag='people' Commit
-
雪花算法: 雪花算法为Twitter开源的一种生成id的算法。
其由64位bit组成,第1位为标识位,第2位到第42位为毫秒数,记录当前时间与起始时间的毫秒数差值,记录长度为69年,第43位到52位表示机器id,Twitter是将前5位分配为idc标识号,后5位分配为机器标识号,53位到64位表示为递增序列号。
java实现代码package com.xiayu.config; public class SnowFlake{ //下面两个每个5位,加起来就是10位的工作机器id private long workerId; //工作id,每一个服务对应唯一一个workerId,这个id当服务比较少的时候,可以写在配置文件中,如果比较多的话可以使用zk的持久顺序节点 private long datacenterId; //数据id //12位的序列号 private long sequence; public SnowFlake(long workerId, long datacenterId, long sequence){ // 校验wokerId值 if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0",maxWorkerId)); } //校验数据中心id值 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; //长度为5位 private long workerIdBits = 5L; private long datacenterIdBits = 5L; //最大值 private long maxWorkerId = -1L ^ (-1L << workerIdBits); private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); //序列号id长度 private long sequenceBits = 12L; //序列号最大值 private long sequenceMask = -1L ^ (-1L << sequenceBits); //工作id需要左移的位数,12位 private long workerIdShift = sequenceBits; //数据id需要左移位数 12+5=17位 private long datacenterIdShift = sequenceBits + workerIdBits; //时间戳需要左移位数 12+5+5=22位 private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; //上次时间戳,初始值为负数 private long lastTimestamp = -1L; public long getWorkerId(){ return workerId; } public long getDatacenterId(){ return datacenterId; } public long getTimestamp(){ return System.currentTimeMillis(); } //下一个ID生成算法,线程安全的 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)); } //获取当前时间戳如果等于上次时间戳(同一毫秒内),则在序列号加一;否则序列号赋值为0,从0开始。 if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; //序列号+1 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 = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } //获取系统时间戳 private long timeGen(){ return System.currentTimeMillis(); } public static void main(String[] args) { SnowFlake worker = new SnowFlake(1,1,1); for (int i = 0; i < 20; i++) { System.out.println(worker.nextId()); } } }
大厂id生成工具有美团的Leaf(树叶),和百度的UidGenerator,其原理同上。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。