xiaozhuaia 发表于 2019-1-7 14:19:07

zookeeper全局唯一id生成

一背景
传统生成id方式可以靠数据库的自增来实现,但是在分布式环境下不太适应。依赖数据库容易造成单点。
为什么不用UUID的,网上看别人介绍的时候,从两个方面去分析:
1 大并发的情况下,UUID会出现重复。
2.UUID是随即的,含义不明。从业务角度去考虑,如果用作订单,用户查询订单在数据分片的情况下很可能分散在多个库,查询困难。
全局唯一id的要求比较高:
不能有单点故障。
性能好,毫秒级返回。
能顺序便于DB存储及划分。
二 使用zookeeper生成全局唯一id.
2.1 利用Zookeeper的znode数据版本生成序列号
http://img.blog.csdn.net/20160520155122773?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

客户端采用:zkClient (https://github.com/adyliu/zkclient)


  
      
      com.github.adyliu
      zkclient
      2.1.1
      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);
                Statstat =zkClient.writeData(SEQ_ZNODE, new byte, -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(""));
      }
    }
}  

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);
      }
      @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 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(""));
                }
                service.shutdown();
    }
}  

  
  利用带序列号的znode实现

   view plain copy

[*]http://img.blog.csdn.net/20160520161921534?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

  http://img.blog.csdn.net/20160520162010194?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

  后端看日志就是运行期间有临时节点,会话结束后自动删除。
三 开源方案
网上还有开源的更好的开源实现方案值得借鉴。


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


[*]  使用41 bit来存放时间,精确到毫秒,可以使用41年。
[*]  使用13 bit来存放逻辑分片ID。
[*]  使用10 bit来存放自增长ID,意味着每台机器,每毫秒最多可以生成1024个ID


优: 开发成本低
劣: 基于postgreSQL的存储过程,通用性差
  还有基于redis的全局id生成方案:http://blog.csdn.net/hengyunabc/article/details/44244951


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




页: [1]
查看完整版本: zookeeper全局唯一id生成