分布式id生成方案
2019-03-16 18:22
169 查看
分布式 ID 生成器
一个唯一 ID 在一个分布式系统中是非常重要的一个业务属性,其中包括一些如订单 ID,消息 ID ,会话 ID,设备ID等,但是唯一的特点全局唯一。
全局唯一很好理解,目的就是唯一标识某个次请求,某个业务。
通常有以下几种方案:
基于数据库
可以利用
MySQL中的自增属性
auto_increment来生成全局唯一 ID,自增ID是连续的,它就依赖于数据库自身的锁,所以数据库就有瓶颈。当然了,多台数据库加某种间隔也是可用的,但是,运维维护会很复杂,因为它不是内聚的解决方案。而且Mysql数据迁移有可能导致id重复,并且Oracle没有递增如果数据库挂了那就非常容易出问题。
水平扩展改进
但也有改进空间,可以将数据库水平拆分,如果拆为了两个库 A 库和 B 库。
A 库的递增方式可以是
0 ,2 ,4 ,6。B 库则是
1 ,3 ,5 ,7。这样的方式可以提高系统可用性,并且 ID 也是趋势递增的。
但也有如下一下问题:
- 想要扩容增加性能变的困难,之前已经定义好了 A B 库递增的步数,新加的数据库不好加入进来,水平扩展困难。
- 也是强依赖与数据库,并且如果其中一台挂掉了那就不是绝对递增了。
本地 UUID 生成
还可以采用
UUID的方式生成唯一 ID,由于是在本地生成没有了网络之类的消耗,所有效率非常高。
但也有以下几个问题:
- 生成的 ID 是无序性的,不能做到趋势递增。
- 由于是字符串并且不是递增,所以不太适合用作主键。
采用本地时间
这种做法非常简单,可以利用本地的毫秒数加上一些业务 ID 来生成唯一ID,这样可以做到趋势递增,并且是在本地生成效率也很高。
但有一个致命的缺点:当并发量足够高的时候唯一性就不能保证了。
Twitter 雪花算法
它主要是一种划分命名空间的算法,将生成的 ID 按照机器、时间等来进行标志可以自己编写生成规则,比如:时间戳42位;机器编码5位;进程编码5位;序列号12位。
WorkerId.java
import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Enumeration; /** * 主键生成器 * 时间戳差值,占了42位;机器编码5位;进程编码5位;序列号12位。 * @author * @create 2019-03-16 */ public class WorkerId { private final static long twepoch = 12888349746579L; // 机器标识位数 private final static long workerIdBits = 5L; // 数据中心标识位数 private final static long datacenterIdBits = 5L; // 毫秒内自增位数 private final static long sequenceBits = 12L; // 机器ID偏左移12位 private final static long workerIdShift = sequenceBits; // 数据中心ID左移17位 private final static long datacenterIdShift = sequenceBits + workerIdBits; // 时间毫秒左移22位 private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; //sequence掩码,确保sequnce不会超出上限 private final static long sequenceMask = -1L ^ (-1L << sequenceBits); //上次时间戳 private static long lastTimestamp = -1L; //序列 private long sequence = 0L; //服务器ID private long workerId = 1L; private static long workerMask= -1L ^ (-1L << workerIdBits); //进程编码 private long processId = 1L; private static long processMask=-1L ^ (-1L << datacenterIdBits); private static WorkerId keyWorker = null; static{ keyWorker = new WorkerId(); } public static synchronized long nextId(){ return keyWorker.getNextId(); } private WorkerId() { //获取机器编码 this.workerId=this.getMachineNum(); //获取进程编码 RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); this.processId=Long.valueOf(runtimeMXBean.getName().split("@")[0]).longValue(); //避免编码超出最大值 this.workerId=workerId & workerMask; this.processId=processId & processMask; } public synchronized long getNextId() { //获取时间戳 long timestamp = timeGen(); //如果时间戳小于上次时间戳则报错 if (timestamp < lastTimestamp) { try { throw new Exception("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds"); } catch (Exception e) { e.printStackTrace(); } } //如果时间戳与上次时间戳相同 if (lastTimestamp == timestamp) { // 当前毫秒内,则+1,与sequenceMask确保sequence不会超出上限 sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { // 当前毫秒内计数满了,则等待下一秒 timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0; } lastTimestamp = timestamp; // ID偏移组合生成最终的ID,并返回ID long nextId = ((timestamp - twepoch) << timestampLeftShift) | (processId << datacenterIdShift) | (workerId << workerIdShift) | sequence; return nextId; } /** * 再次获取时间戳直到获取的时间戳与现有的不同 * @param lastTimestamp * @return 下一个时间戳 */ private long tilNextMillis(final long lastTimestamp) { long timestamp = this.timeGen(); while (timestamp <= lastTimestamp) { timestamp = this.timeGen(); } return timestamp; } private long timeGen() { return System.currentTimeMillis(); } /** * 获取机器编码 * @return */ private long getMachineNum(){ long machinePiece; StringBuilder sb = new StringBuilder(); Enumeration<NetworkInterface> e = null; try { e = NetworkInterface.getNetworkInterfaces(); } catch (SocketException e1) { e1.printStackTrace(); } while (e.hasMoreElements()) { NetworkInterface ni = e.nextElement(); sb.append(ni.toString()); } machinePiece = sb.toString().hashCode(); return machinePiece; } public static void main(String[] args) { System.out.println(nextId()); } }
相关文章推荐
- 分布式系统唯一ID生成方案汇总
- 分布式系统唯一ID生成方案汇总
- 分布式系统唯一ID生成方案汇总
- 浅谈CAS在分布式ID生成方案上的应用
- 分布式系统唯一ID生成方案汇总
- 分布式系统唯一ID生成方案汇总
- 浅谈CAS在分布式ID生成方案上的应用
- [转]分布式系统唯一ID生成方案汇总
- 分布式系统唯一ID生成方案
- 分布式环境下全局唯一ID的生成方案
- 分布式系统中生成全局唯一ID方案
- 分布式系统唯一ID生成方案汇总
- 分布式ID的生成方案
- 分布式系统唯一ID生成方案
- 分布式系统唯一ID生成方案汇总【转】
- 每秒生成一千万个【可视有序】分布式ID的简单方案
- 一种基于Orleans的分布式Id生成方案
- 分布式系统唯一ID生成方案汇总
- 分布式ID生成方案
- 分布式系统唯一ID生成方案汇总