java 生成分布式Id ---- 雪花算法
测试:
用 setnx 往 redis 里插 100万条 看是否有重复的 后面又往数据库插 100万,id为主键
package com...uitl; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Enumeration;/** * id自增器(雪花算法) * * @author renjie * @version 1.0.0 */ public class SnowFlake { 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 SnowFlake snowFlake = null; static{ snowFlake = new SnowFlake(); } public static synchronized long nextId(){ return snowFlake.getNextId(); } private SnowFlake() { //获取机器编码 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) { for(int i=0;i<1000000;i++){ Long userId = SnowFlake.nextId(); // System.out.println(userId); String id = userId.toString(); int n = i; if(id.length()!=19){ System.out.println(id); } if(n==999999){ System.out.println("999999: "+id); } try { //用 setnx 往 redis 里插 100万条 看是否有重复的 boolean serchRedis = RedisUtil.setnx(userId.toString(),userId.toString()); } catch (Exception e) { e.printStackTrace(); System.out.println(userId); } } } }
测试结果:
redis 100万条整
数据库 :