全局唯一ID算法:LeafSegmentMist

612 阅读5分钟

顾名思义,结合了美团的LeafSegment和Mist薄雾算法,目的是弥补LeafSegment算法的ID连续性、可预测性,同时弥补Mist算法再分布式架构和机器重启时,ID有概率重复的缺点。

UniqueId

/**
 * 全局id生成器
 */
public interface UniqueId {
   /**
    * 下个全局id
    *
    * @return
    */
   long next();

   /**
    * id含义
    *
    * @param id
    * @return
    */
   String mean(long id);
}

LeafSegment

/**
 * 美团Leaf
 * 原方案每次获取ID都得读写一次数据库,造成数据库压力大。改为利用proxy server批量获取,每次获取一个segment(step决定大小)号段的值。用完之后再去数据库获取新的号段,可以大大的减轻数据库的压力。
 */
public class LeafSegment implements UniqueId {
   /**
    * 业务名(唯一)
    */
   private String bizTag;

   private Segment[] segments = new Segment[]{new Segment(), new Segment()};

   private volatile int currentPos;

   private volatile boolean nextReady;
   private volatile boolean initOk;
   private final AtomicBoolean threadRunning = new AtomicBoolean(false);
   private final ReadWriteLock lock = new ReentrantReadWriteLock();

   private volatile int step;

   public LeafSegment(String bizTag, int step) {
      this.bizTag = bizTag;
      this.step = step;
   }

   public class Segment {
      private volatile AtomicLong value = new AtomicLong();
      private long maxValue;

      public long getIdle() {
         return maxValue - value.get();
      }
   }

   @Override
   public long next() {
      if (!initOk) {
         synchronized (this) {
            if (!initOk) {
               update(segments[currentPos]);
               initOk = true;
            }
         }
      }
      long value = current();
      if (value < 0) {
         switchSegment();
         return next();
      } else {
         return value;
      }
   }

   private long current() {
      lock.readLock().lock();
      try {
         Segment segment = segments[currentPos];
         if (!nextReady && segment.getIdle() < 0.9 * step && threadRunning.compareAndSet(false, true)) {
            prepareNext();
         }
         long value = segment.value.getAndIncrement();
         if (value < segment.maxValue) {
            return value;
         }
         return -1;
      } finally {
         lock.readLock().unlock();
      }
   }

   /**
    * 准备下个区的数据
    */
   private void prepareNext() {
      Invokable runnable = () -> {
         Segment next = segments[nextPos()];
         if (update(next)) {
            lock.writeLock().lock();
            nextReady = true;
            threadRunning.set(false);
            lock.writeLock().unlock();
         } else {
            threadRunning.set(false);
         }
      };
      RetryableRequest.notRetryableRequest("LeafSegment", runnable);
   }

   /**
    * 切换区
    */
   private void switchSegment() {
      lock.writeLock().lock();
      try {
         if (nextReady) {
            switchPos();
            nextReady = false;
         } else {
            throw new RuntimeException("Both segments are not ready");
         }
      } finally {
         lock.writeLock().unlock();
      }
   }

   private int nextPos() {
      return (currentPos + 1) % segments.length;
   }

   private void switchPos() {
      currentPos = nextPos();
   }

   long nextMaxValue = 100000;

   /**
    * 需要引入redis或DB,根据步长获取最大值
    *
    * @return
    */
   protected synchronized long nextMaxvalue() {
      nextMaxValue = nextMaxValue + step;
      return nextMaxValue;
   }

   /**
    * 更新区数据
    *
    * @param segment
    * @return
    */
   protected boolean update(Segment segment) {
      segment.maxValue = nextMaxvalue();
      segment.value.set(segment.maxValue - step);

      return true;
   }

   @Override
   public String mean(long id) {
      return String.format("segmentIncreaseId:", id);
   }

Mist

/**
 * 薄雾
 * 薄雾算法不受时间戳影响,受到数值大小影响。薄雾算法高位数值上限计算方式为int64(1<<47 - 1),上限数值140737488355327 百万亿级,假设每天消耗 10 亿,薄雾算法能使用 385+ 年。
 * <p>
 * 1位 位始终为0, 不可用 java中为符号位
 * 47位 自增数 自增数在高位能保证结果值呈递增态势,遂低位可以为所欲为;
 * 8位 随机因子1 上限数值 255,使结果值不可预测;
 * 8位 随机因子2 上限数值 255,使结果值不可预测;
 */
public class Mist implements UniqueId {

   private final AtomicLong sequence = new AtomicLong();

   private final long saltBit = 8L;

   private final long saltShift = saltBit;

   private final int saltMax = ~(-1 << saltBit);
   private final long increaseShit = saltBit + saltShift;

   @Override
   public long next() {
      return mist(sequence.getAndIncrement());
   }

   public long mist(long sequence) {
      long salt1 = JediFuncs.random(0, saltMax);
      long salt2 = JediFuncs.random(0, saltMax);
      return (sequence << increaseShit) | (salt1 << saltShift) | salt2;
   }

   @Override
   public String mean(long id) {
      long salt2 = id & saltMax;
      long salt1 = (id >> saltShift) & saltMax;
      long sequence = (id >> increaseShit);
      return String.format("sequence:%d salt1:%d salt1:%d", sequence, salt1, salt2);
   }
}

LeafSegmentMist

/**
 * 分段薄雾
 * 结合LeafSegment分段递增算法和薄雾算法,使ID呈递增状态且不可预测
 */
public class LeafSegmentMist implements UniqueId {
   private LeafSegment leafSegment;
   private Mist mist;

   /**
    * 分段薄雾
    *
    * @param bizTag 业务ID
    * @param step   步长
    */
   public LeafSegmentMist(String bizTag, int step) {
      leafSegment = new LeafSegment(bizTag, step);
      mist = new Mist();
   }

   @Override
   public long next() {
      return mist.mist(leafSegment.next());
   }

   @Override
   public String mean(long id) {
      return mist.mean(id);
   }
}

Test

@Test
public void leafSegmentMist() {
   LeafSegmentMist mist = new LeafSegmentMist("test", 1000);
   int times = 1;
   while (times++ <= 10000) {
      long v = mist.next();
      JLogger.debug("LeafSegmentMist:"+v);
      JLogger.debug(mist.mean(v));
   }
}

2023-06-30 13:46:08,971 [main] DEBUG debugLogger  - LeafSegmentMist:6797296358
2023-06-30 13:46:08,971 [main] DEBUG debugLogger  - sequence:103718 salt1:130 salt1:230
2023-06-30 13:46:08,971 [main] DEBUG debugLogger  - LeafSegmentMist:6797364332
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - sequence:103719 salt1:140 salt1:108
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - LeafSegmentMist:6797453582
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - sequence:103720 salt1:233 salt1:14
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - LeafSegmentMist:6797510576
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - sequence:103721 salt1:199 salt1:176
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - LeafSegmentMist:6797573170
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - sequence:103722 salt1:188 salt1:50
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - LeafSegmentMist:6797598314
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - sequence:103723 salt1:30 salt1:106
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - LeafSegmentMist:6797686431
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - sequence:103724 salt1:118 salt1:159
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - LeafSegmentMist:6797722486
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - sequence:103725 salt1:3 salt1:118
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - LeafSegmentMist:6797826327
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - sequence:103726 salt1:153 salt1:23
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - LeafSegmentMist:6797896782
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - sequence:103727 salt1:172 salt1:78
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - LeafSegmentMist:6797924957
2023-06-30 13:46:08,972 [main] DEBUG debugLogger  - sequence:103728 salt1:26 salt1:93