zoukankan      html  css  js  c++  java
  • 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万条整  

    数据库 :

     

  • 相关阅读:
    spring-boot集成1:起步
    策略模式实现多种支付方式
    自定义切面实现记录系统操作日志
    Spring Kafka
    使用Keepalived实现Nginx高可用
    Centos7桥接网络、DNS、时间同步配置
    jmeter随笔(1)-在csv中数据为json格式的数据不完整
    (续篇3):飞测独家のJmeter秘籍,限量发放
    紧张:飞测独家のJmeter秘籍,限量发放(续篇2)
    紧张:飞测独家のJmeter秘籍,限量发放
  • 原文地址:https://www.cnblogs.com/lifan12589/p/14695030.html
Copyright © 2011-2022 走看看