zookeeper全局唯一id生成

一背景

傳統生成id方式可以靠數據庫的自增來實現,但是在分佈式環境下不太適應。依賴數據庫容易造成單點。

爲什麼不用UUID的,網上看別人介紹的時候,從兩個方面去分析:

1 大併發的情況下,UUID會出現重複。

2.UUID是隨即的,含義不明。從業務角度去考慮,如果用作訂單,用戶查詢訂單在數據分片的情況下很可能分散在多個庫,查詢困難。

全局唯一id的要求比較高:

不能有單點故障。

性能好,毫秒級返回。

能順序便於DB存儲及劃分。

二 使用zookeeper生成全局唯一id.

2.1 利用Zookeeper的znode數據版本生成序列號


客戶端採用:zkClient (https://github.com/adyliu/zkclient


    <dependency>  
        <groupId>com.github.adyliu</groupId>  
        <artifactId>zkclient</artifactId>  
        <version>2.1.1</version>  
    </dependency>

 java 代碼實現

public class ZKSeqTest {  
  
    //提前創建好存儲Seq的"/createSeq"結點 CreateMode.PERSISTENT  
    public static final String SEQ_ZNODE = "/seq";  
       
        //通過znode數據版本實現分佈式seq生成  
        public static class Task1 implements Runnable {   
            private final String taskName;         
            public Task1(String taskName) {  
                this.taskName = taskName;  
            }          
            @Override  
            public void run() {  
                ZkClient zkClient = new ZkClient("192.168.190.36:2181", 3000, 50000);  
                Stat  stat =zkClient.writeData(SEQ_ZNODE, new byte[0], -1);  
                int versionAsSeq = stat.getVersion();  
                System.out.println(taskName + " obtain seq=" +versionAsSeq );  
                zkClient.close();  
            }  
        }  
           
    public static void main(String[] args) {  
        // TODO Auto-generated method stub  
        //main  
        final ExecutorService service = Executors.newFixedThreadPool(20);  
           
        for (int i = 0; i < 10; i++) {  
            service.execute(new Task1("[Concurrent-" + i + "]"));  
        }  
    }  
}


public class ZKLock {  
      
      
    //提前創建好鎖對象的結點"/lock" CreateMode.PERSISTENT  
    public static final String LOCK_ZNODE = "/lock";  
    //分佈式鎖實現分佈式seq生成  
    public static class Task2 implements Runnable, IZkChildListener {  
       
        private final String taskName;  
       
        private final ZkClient zkClient;  
       
        private final String lockPrefix = "/loc";  
       
        private final String selfZnode;  
       
        public Task2(String taskName) {  
            this.taskName = taskName;  
            zkClient = new ZkClient("192.168.190.36:2181", 30000, 50000);  
            selfZnode = zkClient.createEphemeralSequential(LOCK_ZNODE + lockPrefix, new byte[0]);  
        }  
       
        @Override  
        public void run() {  
  
              createSeq();  
        }      
       
        private void createSeq() {  
            Stat stat = new Stat();  
            byte[] oldData = zkClient.readData(LOCK_ZNODE, stat);  
            byte[] newData = update(oldData);  
            zkClient.writeData(LOCK_ZNODE, newData);  
            System.out.println(taskName + selfZnode + " obtain seq=" + new String(newData));  
        }  
       
        private byte[] update(byte[] currentData) {  
            String s = new String(currentData);  
            int d = Integer.parseInt(s);  
            d = d + 1;  
            s = String.valueOf(d);  
            return s.getBytes();  
        }  
  
        @Override  
        public void handleChildChange(String parentPath,  
                List<String> currentChildren) throws Exception {  
            // TODO Auto-generated method stub  
              
        }      
         
    }  
      
  
    public static void main(String[] args) {  
      
                final ExecutorService service = Executors.newFixedThreadPool(20);  
                   
                for (int i = 0; i < 10; i++) {  
                    service.execute(new Task2("[Concurrent-" + i + "]"));  
                }  
                service.shutdown();  
    }  
  
}



利用帶序列號的znode實現

[java] view plain copy



後端看日誌就是運行期間有臨時節點,會話結束後自動刪除。

三 開源方案

網上還有開源的更好的開源實現方案值得借鑑。


3.1Flikr

基於int/bigint的自增

優:開發成本低

劣:如果需要高性能,需要專門一套MySQL集羣只用於生成自增ID。可用性也不強


3.2 Snowflake

twitter利用zookeeper實現了一個全局ID生成的服務snowflake,https://github.com/twitter/snowflake,可以生成全局唯一的64bit ID。

生成的ID的構成:

時間--用前面41 bit來表示時間,精確到毫秒,可以表示69年的數據  
機器ID--用10 bit來表示,也就是說可以部署1024臺機器  
序列數--用12 bit來表示,意味着每臺機器,每毫秒最多可以生成4096個ID


優:可用性強,速度快,id保存信息多。

劣:需要引入zookeeper 和獨立的snowflake專用服務器


3.3instagram

instagram參考了flickr的方案,再結合twitter的經驗,利用Postgres數據庫的特性,實現了一個更簡單可靠的ID生成服務。

 copy

  1. 使用41 bit來存放時間,精確到毫秒,可以使用41年。  

  2. 使用13 bit來存放邏輯分片ID。  

  3. 使用10 bit來存放自增長ID,意味着每臺機器,每毫秒最多可以生成1024個ID  


優: 開發成本低

劣: 基於postgreSQL的存儲過程,通用性差

還有基於redis的全局id生成方案:http://blog.csdn.net/hengyunabc/article/details/44244951


參考:

http://blog.csdn.net/bohu83/article/details/51457961


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章