天天看點

分布式唯一ID生成算法-雪花算法

  在我們的工作中,資料庫某些表的字段會用到唯一的,趨勢遞增的訂單編号,我們将介紹兩種方法,一種是傳統的采用随機數生成的方式,另外一種是采用目前比較流行的“分布式唯一ID生成算法-雪花算法”來實作。

  一、時間戳随機數生成唯一ID

  我們寫一個for循環,用

  RandomUtil.generateOrderCode()生成1000個唯一ID,執行結果我們會發現出現重複的ID。

  /**

  * 随機數生成util

  **/

  public class RandomUtil {

  private static final SimpleDateFormat dateFormatOne=new SimpleDateFormat("yyyyMMddHHmmssSS");

  private static final ThreadLocalRandom random=ThreadLocalRandom.current();

  //生成訂單編号-方式一

  public static String generateOrderCode(){

  //TODO:時間戳+N為随機數流水号

  return dateFormatOne.format(DateTime.now().toDate()) + generateNumber(4);

  }

  //N為随機數流水号

  public static String generateNumber(final int num){

  StringBuffer sb=new StringBuffer();

  for (int i=1;i<=num;i++){

  sb.append(random.nextInt(9));

  return sb.toString();

  鑒于此種“基于随機數生成”的方式在高并發的場景下并不符合我們的要求,接下來,我們将介紹另外一種比較流行的、典型的方式,即“分布式唯一ID生成算法-雪花算法”來實作。

  對于“雪花算法”的介紹,各位小夥伴可以參考Github上的這一連結,我覺得講得還是挺清晰的:

  github/souyunku/SnowFlake ,詳細的Debug在這裡就不贅述了,下面截取了部分概述:

  二、分布式唯一ID生成算法-雪花算法

  我們寫一個for循環,用SNOW_FLAKE.nextId() 生成1000個唯一ID,發現不會出現重複的。

  /* 雪花算法

  */

  public class SnowFlake {

  //起始的時間戳

  private final static long START_STAMP=1480166465631L;

  //每一部分占用的位數

  private final static long SEQUENCE_BIT=12; //序列号占用的位數

  private final static long MACHINE_BIT=5; //機器辨別占用的位數

  private final static long DATA_CENTER_BIT=5;//

二手手遊賣号

資料中心占用的位數

  //每一部分的最大值

  private final static long MAX_DATA_CENTER_NUM=-1L ^ (-1L << DATA_CENTER_BIT);

  private final static long MAX_MACHINE_NUM=-1L ^ (-1L << MACHINE_BIT);

  private final static long MAX_SEQUENCE=-1L ^ (-1L << SEQUENCE_BIT);

  //每一部分向左的位移

  private final static long MACHINE_LEFT=SEQUENCE_BIT;

  private final static long DATA_CENTER_LEFT=SEQUENCE_BIT + MACHINE_BIT;

  private final static long TIMESTAMP_LEFT=DATA_CENTER_LEFT + DATA_CENTER_BIT;

  private long dataCenterId; //資料中心

  private long machineId; //機器辨別

  private long sequence=0L; //序列号

  private long lastStamp=-1L;//上一次時間戳

  public SnowFlake(long dataCenterId, long machineId) {

  if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0) {

  throw new IllegalArgumentException("dataCenterId can't be greater than MAX_DATA_CENTER_NUM or less than 0");

  if (machineId > MAX_MACHINE_NUM || machineId < 0) {

  throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");

  this.dataCenterId=dataCenterId;

  this.machineId=machineId;

  //産生下一個ID

  public synchronized long nextId() {

  long currStamp=getNewStamp();

  if (currStamp < lastStamp) {

  throw new RuntimeException("Clock moved backwards. Refusing to generate id");

  if (currStamp==lastStamp) {

  //相同毫秒内,序列号自增

  sequence=(sequence + 1) & MAX_SEQUENCE;

  //同一毫秒的序列數已經達到最大

  if (sequence==0L) {

  currStamp=getNextMill();

  } else {

  //不同毫秒内,序列号置為0

  sequence=0L;

  lastStamp=currStamp;

  return (currStamp - START_STAMP) << TIMESTAMP_LEFT //時間戳部分

  | dataCenterId << DATA_CENTER_LEFT //資料中心部分

  | machineId << MACHINE_LEFT //機器辨別部分

  | sequence; //序列号部分

  private long getNextMill() {

  long mill=getNewStamp();

  while (mill <=lastStamp) {

  mill=getNewStamp();

  return mill;

  private long getNewStamp() {

  return System.currentTimeMillis();

  綜上,我們在高并發大量生成唯一ID時,避免生成重複ID,需要用第二種雪花算法生成。