久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      mysql實(shí)現(xiàn)的雪花算法

      mysql實(shí)現(xiàn)的雪花算法

      【相關(guān)學(xué)習(xí)推薦:mysql視頻教程】

      一、為何要用雪花算法

      1、問題產(chǎn)生的背景

      現(xiàn)如今越來越多的公司都在用分布式、微服務(wù),那么對應(yīng)的就會針對不同的服務(wù)進(jìn)行數(shù)據(jù)庫拆分,然后當(dāng)數(shù)據(jù)量上來的時候也會進(jìn)行分表,那么隨之而來的就是分表以后id的問題。

      例如之前單體項(xiàng)目中一個表中的數(shù)據(jù)主鍵id都是自增的,mysql是利用autoincrement來實(shí)現(xiàn)自增,而oracle是利用序列來實(shí)現(xiàn)的,但是當(dāng)單表數(shù)據(jù)量上來以后就要進(jìn)行水平分表,阿里java開發(fā)建議是單表大于500w的時候就要分表,但是具體還是得看業(yè)務(wù),如果索引用的號的話,單表千萬的數(shù)據(jù)也是可以的。水平分表就是將一張表的數(shù)據(jù)分成多張表,那么問題就來了如果還是按照以前的自增來做主鍵id,那么就會出現(xiàn)id重復(fù),這個時候就得考慮用什么方案來解決分布式id的問題了。

      2、解決方案

      2.1、數(shù)據(jù)庫表

      可以在某個庫中專門維護(hù)一張表,然后每次無論哪個表需要自增id的時候都去查這個表的記錄,然后用for update鎖表,然后取到的值加一,然后返回以后把再把值記錄到表中,但是這個方法適合并發(fā)量比較小的項(xiàng)目,因此每次都得鎖表。

      2.2、redis

      因?yàn)閞edis是單線程的,可以在redis中維護(hù)一個鍵值對,然后哪個表需要直接去redis中取值然后加一,但是這個跟上面一樣由于單線程都是對高并發(fā)的支持不高,只適合并發(fā)量小的項(xiàng)目。

      2.3、uuid

      可以使用uuid作為不重復(fù)主鍵id,但是uuid有個問題就是其是無序的字符串,如果使用uuid當(dāng)做主鍵,那么主鍵索引就會失效。

      2.4、雪花算法

      雪花算法是解決分布式id的一個高效的方案,大部分互聯(lián)網(wǎng)公司都在使用雪花算法,當(dāng)然還有公司自己實(shí)現(xiàn)其他的方案。

      二、雪花算法

      1、原理

      mysql實(shí)現(xiàn)的雪花算法

      雪花算法就是使用64位long類型的數(shù)據(jù)存儲id,最高位一位存儲0或者1,0代表整數(shù),1代表負(fù)數(shù),一般都是0,所以最高位不變,41位存儲毫秒級時間戳,10位存儲機(jī)器碼(包括5位datacenterId和5位workerId),12存儲序列號。這樣最大2的10次方的機(jī)器,也就是1024臺機(jī)器,最多每毫秒每臺機(jī)器產(chǎn)生2的12次方也就是4096個id。(下面有代碼實(shí)現(xiàn))

      但是一般我們沒有那么多臺機(jī)器,所以我們也可以使用53位來存儲id。為什么要用53位?

      因?yàn)槲覀儙缀醵际歉鷚eb頁面打交道,就需要跟js打交道,js支持最大的整型范圍為53位,超過這個范圍就會丟失精度,53之內(nèi)可以直接由js讀取,超過53位就需要轉(zhuǎn)換成字符串才能保證js處理正確。53存儲的話,32位存儲秒級時間戳,5位存儲機(jī)器碼,16位存儲序列化,這樣每臺機(jī)器每秒可以生產(chǎn)65536個不重復(fù)的id。

      2、缺點(diǎn)

      由于雪花算法嚴(yán)重依賴時間,所以當(dāng)發(fā)生服務(wù)器時鐘回?fù)艿膯栴}是會導(dǎo)致可能產(chǎn)生重復(fù)的id。當(dāng)然幾乎沒有公司會修改服務(wù)器時間,修改以后會導(dǎo)致各種問題,公司寧愿新加一臺服務(wù)器也不愿意修改服務(wù)器時間,但是不排除特殊情況。

      如何解決時鐘回?fù)艿膯栴}?可以對序列化的初始值設(shè)置步長,每次觸發(fā)時鐘回?fù)苁录?,則其初始步長就加1w,可以在下面代碼的第85行來實(shí)現(xiàn),將sequence的初始值設(shè)置為10000。

      三、代碼實(shí)現(xiàn)

      64位的代碼實(shí)現(xiàn):

      package com.yl.common; /**  * Twitter_Snowflake<br>  * SnowFlake的結(jié)構(gòu)如下(每部分用-分開):<br>  * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>  * 1位標(biāo)識,由于long基本類型在Java中是帶符號的,最高位是符號位,正數(shù)是0,負(fù)數(shù)是1,所以id一般是正數(shù),最高位是0<br>  * 41位時間截(毫秒級),注意,41位時間截不是存儲當(dāng)前時間的時間截,而是存儲時間截的差值(當(dāng)前時間截 - 開始時間截)  * 得到的值),這里的的開始時間截,一般是我們的id生成器開始使用的時間,由我們程序來指定的(如下下面程序IdWorker類的startTime屬性)。41位的時間截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>  * 10位的數(shù)據(jù)機(jī)器位,可以部署在1024個節(jié)點(diǎn),包括5位datacenterId和5位workerId<br>  * 12位序列,毫秒內(nèi)的計(jì)數(shù),12位的計(jì)數(shù)順序號支持每個節(jié)點(diǎn)每毫秒(同一機(jī)器,同一時間截)產(chǎn)生4096個ID序號<br>  * 加起來剛好64位,為一個Long型。<br>  * SnowFlake的優(yōu)點(diǎn)是,整體上按照時間自增排序,并且整個分布式系統(tǒng)內(nèi)不會產(chǎn)生ID碰撞(由數(shù)據(jù)中心ID和機(jī)器ID作區(qū)分),并且效率較高,經(jīng)測試,SnowFlake每秒能夠產(chǎn)生26萬ID左右。  */ public class SnowflakeIdWorker {   // ==============================Fields===========================================  /** 開始時間截 (2020-01-01) */  private final long twepoch = 1577808000000L;   /** 機(jī)器id所占的位數(shù) */  private final long workerIdBits = 5L;   /** 數(shù)據(jù)標(biāo)識id所占的位數(shù) */  private final long datacenterIdBits = 5L;   /** 支持的最大機(jī)器id,結(jié)果是31 (這個移位算法可以很快的計(jì)算出幾位二進(jìn)制數(shù)所能表示的最大十進(jìn)制數(shù)) */  private final long maxWorkerId = -1L ^ (-1L << workerIdBits);   /** 支持的最大數(shù)據(jù)標(biāo)識id,結(jié)果是31 */  private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);   /** 序列在id中占的位數(shù) */  private final long sequenceBits = 12L;   /** 機(jī)器ID向左移12位 */  private final long workerIdShift = sequenceBits;   /** 數(shù)據(jù)標(biāo)識id向左移17位(12+5) */  private final long datacenterIdShift = sequenceBits + workerIdBits;   /** 時間截向左移22位(5+5+12) */  private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;   /** 生成序列的掩碼,這里為4095 (0b111111111111=0xfff=4095) */  private final long sequenceMask = -1L ^ (-1L << sequenceBits);   /** 工作機(jī)器ID(0~31) */  private long workerId;   /** 數(shù)據(jù)中心ID(0~31) */  private long datacenterId;   /** 毫秒內(nèi)序列(0~4095) */  private long sequence = 0L;   /** 上次生成ID的時間截 */  private long lastTimestamp = -1L;   //==============================Constructors=====================================  /**  * 構(gòu)造函數(shù)  * @param workerId 工作ID (0~31)  * @param datacenterId 數(shù)據(jù)中心ID (0~31)  */  public SnowflakeIdWorker(long workerId, long datacenterId) {  if (workerId > maxWorkerId || workerId < 0) {  throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));  }  if (datacenterId > maxDatacenterId || datacenterId < 0) {  throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));  }  this.workerId = workerId;  this.datacenterId = datacenterId;  }   // ==============================Methods==========================================  /**  * 獲得下一個ID (該方法是線程安全的)  * @return SnowflakeId  */  public synchronized long nextId() {  long timestamp = timeGen();   //如果當(dāng)前時間小于上一次ID生成的時間戳,說明系統(tǒng)時鐘回退過這個時候應(yīng)當(dāng)拋出異常  if (timestamp < lastTimestamp) {  throw new RuntimeException(   String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));  }   //如果是同一時間生成的,則進(jìn)行毫秒內(nèi)序列  if (lastTimestamp == timestamp) {  sequence = (sequence + 1) & sequenceMask;  //毫秒內(nèi)序列溢出  if (sequence == 0) {  //阻塞到下一個毫秒,獲得新的時間戳  timestamp = tilNextMillis(lastTimestamp);  }  }  //時間戳改變,毫秒內(nèi)序列重置  else {  sequence = 0L;  }   //上次生成ID的時間截  lastTimestamp = timestamp;   //移位并通過或運(yùn)算拼到一起組成64位的ID  return ((timestamp - twepoch) << timestampLeftShift) //  | (datacenterId << datacenterIdShift) //  | (workerId << workerIdShift) //  | sequence;  }   /**  * 阻塞到下一個毫秒,直到獲得新的時間戳  * @param lastTimestamp 上次生成ID的時間截  * @return 當(dāng)前時間戳  */  protected long tilNextMillis(long lastTimestamp) {  long timestamp = timeGen();  while (timestamp <= lastTimestamp) {  timestamp = timeGen();  }  return timestamp;  }   /**  * 返回以毫秒為單位的當(dāng)前時間  * @return 當(dāng)前時間(毫秒)  */  protected long timeGen() {  return System.currentTimeMillis();  }   //==============================Test=============================================  /** 測試 */  public static void main(String[] args) {  SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);    for (int i = 0; i < 100; i++) {  long id = idWorker.nextId();  System.out.println(id);  }  } }

      補(bǔ)充知識:雪花算法實(shí)現(xiàn)分布式自增長ID

      我就廢話不多說了,大家還是直接看代碼吧~

      /**  * <p>名稱:IdWorker.java</p>  * <p>描述:分布式自增長ID</p>  * <pre>  * Twitter的 Snowflake JAVA實(shí)現(xiàn)方案  * </pre>  * 核心代碼為其IdWorker這個類實(shí)現(xiàn),其原理結(jié)構(gòu)如下,我分別用一個0表示一位,用—分割開部分的作用:  * 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000  * 在上面的字符串中,第一位為未使用(實(shí)際上也可作為long的符號位),接下來的41位為毫秒級時間,  * 然后5位datacenter標(biāo)識位,5位機(jī)器ID(并不算標(biāo)識符,實(shí)際是為線程標(biāo)識),  * 然后12位該毫秒內(nèi)的當(dāng)前毫秒內(nèi)的計(jì)數(shù),加起來剛好64位,為一個Long型。  * 這樣的好處是,整體上按照時間自增排序,并且整個分布式系統(tǒng)內(nèi)不會產(chǎn)生ID碰撞(由datacenter和機(jī)器ID作區(qū)分),  * 并且效率較高,經(jīng)測試,snowflake每秒能夠產(chǎn)生26萬ID左右,完全滿足需要。  * <p>  * 64位ID (42(毫秒)+5(機(jī)器ID)+5(業(yè)務(wù)編碼)+12(重復(fù)累加))  *  * @author Polim  */ public class IdWorker {  // 時間起始標(biāo)記點(diǎn),作為基準(zhǔn),一般取系統(tǒng)的最近時間(一旦確定不能變動)  private final static long twepoch = 1288834974657L;  // 機(jī)器標(biāo)識位數(shù)  private final static long workerIdBits = 5L;  // 數(shù)據(jù)中心標(biāo)識位數(shù)  private final static long datacenterIdBits = 5L;  // 機(jī)器ID最大值  private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);  // 數(shù)據(jù)中心ID最大值  private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);  // 毫秒內(nèi)自增位  private final static long sequenceBits = 12L;  // 機(jī)器ID偏左移12位  private final static long workerIdShift = sequenceBits;  // 數(shù)據(jù)中心ID左移17位  private final static long datacenterIdShift = sequenceBits + workerIdBits;  // 時間毫秒左移22位  private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;   private final static long sequenceMask = -1L ^ (-1L << sequenceBits);  /* 上次生產(chǎn)id時間戳 */  private static long lastTimestamp = -1L;  // 0,并發(fā)控制  private long sequence = 0L;   private final long workerId;  // 數(shù)據(jù)標(biāo)識id部分  private final long datacenterId;   public IdWorker(){  this.datacenterId = getDatacenterId(maxDatacenterId);  this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);  }  /**  * @param workerId  *  工作機(jī)器ID  * @param datacenterId  *  序列號  */  public IdWorker(long workerId, long datacenterId) {  if (workerId > maxWorkerId || workerId < 0) {   throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));  }  if (datacenterId > maxDatacenterId || datacenterId < 0) {   throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));  }  this.workerId = workerId;  this.datacenterId = datacenterId;  }  /**  * 獲取下一個ID  *  * @return  */  public synchronized long nextId() {  long timestamp = timeGen();  if (timestamp < lastTimestamp) {   throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));  }   if (lastTimestamp == timestamp) {   // 當(dāng)前毫秒內(nèi),則+1   sequence = (sequence + 1) & sequenceMask;   if (sequence == 0) {   // 當(dāng)前毫秒內(nèi)計(jì)數(shù)滿了,則等待下一秒   timestamp = tilNextMillis(lastTimestamp);   }  } else {   sequence = 0L;  }  lastTimestamp = timestamp;  // ID偏移組合生成最終的ID,并返回ID  long nextId = ((timestamp - twepoch) << timestampLeftShift)   | (datacenterId << datacenterIdShift)   | (workerId << workerIdShift) | sequence;   return nextId;  }   private long tilNextMillis(final long lastTimestamp) {  long timestamp = this.timeGen();  while (timestamp <= lastTimestamp) {   timestamp = this.timeGen();  }  return timestamp;  }   private long timeGen() {  return System.currentTimeMillis();  }   /**  * <p>  * 獲取 maxWorkerId  * </p>  */  protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {  StringBuffer mpid = new StringBuffer();  mpid.append(datacenterId);  String name = ManagementFactory.getRuntimeMXBean().getName();  if (!name.isEmpty()) {   /*   * GET jvmPid   */   mpid.append(name.split("@")[0]);  }  /*  * MAC + PID 的 hashcode 獲取16個低位  */  return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);  }   /**  * <p>  * 數(shù)據(jù)標(biāo)識id部分  * </p>  */  protected static long getDatacenterId(long maxDatacenterId) {  long id = 0L;  try {   InetAddress ip = InetAddress.getLocalHost();   NetworkInterface network = NetworkInterface.getByInetAddress(ip);   if (network == null) {   id = 1L;   } else {   byte[] mac = network.getHardwareAddress();   id = ((0x000000FF & (long) mac[mac.length - 1])    | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;   id = id % (maxDatacenterId + 1);   }  } catch (Exception e) {   System.out.println(" getDatacenterId: " + e.getMessage());  }  return id;  }   }

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號