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

时间: 2024-07-29 15:19:20

zookeeper全局唯一id生成的相关文章

分布式系统全局唯一ID生成

一 什么是分布式系统唯一ID 在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识. 如在金融.电商.支付.等产品的系统中,数据日渐增长,对数据分库分表后需要有一个唯一ID来标识一条数据或消息,数据库的自增ID显然不能满足需求,此时一个能够生成全局唯一ID的系统是非常必要的. 二.分布式系统唯一ID的特点 全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求. 趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数

常见分布式全局唯一ID生成策略

全局唯一的 ID 几乎是所有系统都会遇到的刚需.这个 id 在搜索, 存储数据, 加快检索速度 等等很多方面都有着重要的意义.工业上有多种策略来获取这个全局唯一的id,针对常见的几种场景,我在这里进行简单的总结和对比. 简单分析一下需求 [1] 所谓全局唯一的 id 其实往往对应是生成唯一记录标识的业务需求. 这个 id 常常是数据库的主键,数据库上会建立聚集索引(cluster index),即在物理存储上以这个字段排序.这个记录标识上的查询,往往又有分页或者排序的业务需求.所以往往要有一个t

mysql全局唯一ID生成方案(二)

MySQL数据表结构中,一般情况下,都会定义一个具有‘AUTO_INCREMENT’扩展属性的‘ID’字段,以确保数据表的每一条记录都可以用这个ID唯一确定: 随着数据的不断扩张,为了提高数据库查询性能,降低查询热点,一般都会把一张表按照一定的规则分成多张数据表,即常说的分表: 分表除了表名的索引不同之外,表结构都是一样的,如果各表的‘ID’字段仍采用‘AUTO_INCREMENT’的方式的话,ID就不能唯确定一条记录了. 这时就需要一种处于各个分表之外的机制来生成ID,我们一般采用一张单独的数

分布式全局唯一ID生成策略?

一.背景 分布式系统中我们会对一些数据量大的业务进行分拆,如:用户表,订单表.因为数据量巨大一张表无法承接,就会对其进行分库分表. 但一旦涉及到分库分表,就会引申出分布式系统中唯一主键ID的生成问题. 1.1 唯一ID的特性 整个系统ID唯一; ID是数字类型,而且是趋势递增; ID简短,查询效率快. 1.2 递增与趋势递增 递增 趋势递增 第一次生成的ID为12,下一次生成的ID是13,再下一次生成的ID是14. 什么是?如:在一段时间内,生成的ID是递增的趋势.如:再一段时间内生成的ID在[

Twitter全局唯一ID生成算法

测试:private static void TestIdWorker() { HashSet<long> set = new HashSet<long>(); IdWorker idWorker1 = new IdWorker(0, 0); IdWorker idWorker2 = new IdWorker(1, 0); //762884413578018816 //762884520121729024 Stopwatch sw = new Stopwatch(); sw.Sta

高并发分布式系统中生成全局唯一Id汇总

高并发分布式系统中生成全局唯一Id汇总 (转自:http://www.cnblogs.com/baiwa/p/5318432.html) 数据在分片时,典型的是分库分表,就有一个全局ID生成的问题.单纯的生成全局ID并不是什么难题,但是生成的ID通常要满足分片的一些要求:   1 不能有单点故障.   2 以时间为序,或者ID里包含时间.这样一是可以少一个索引,二是冷热数据容易分离.   3 可以控制ShardingId.比如某一个用户的文章要放在同一个分片内,这样查询效率高,修改也容易.   

分布式全局唯一ID的实现

分布式全局唯一ID的实现 前言 上周末考完试,这周正好把工作整理整理,然后也把之前的一些素材,整理一番,也当自己再学习一番. 一方面正好最近看到几篇这方面的文章,另一方面也是正好工作上有所涉及,所以决定写一篇这样的文章. 先是简单介绍概念和现有解决方案,然后是我对这些方案的总结,最后是我自己项目的解决思路. 概念 在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识. 如在金融.电商.支付.等产品的系统中,数据日渐增长,对数据分库分表后需要有一个唯一ID来标识一条数据或消息,数据库的自增I

游戏服务器生成全局唯一ID的几种方法

在服务器系统开发时,为了适应数据大并发的请求,我们往往需要对数据进行异步存储,特别是在做分布式系统时,这个时候就不能等待插入数据库返回了取自动id了,而是需要在插入数据库之前生成一个全局的唯一id,使用全局的唯一id,在游戏服务器中,全局唯一的id可以用于将来合服方便,不会出现键冲突.也可以将来在业务增长的情况下,实现分库分表,比如某一个用户的物品要放在同一个分片内,而这个分片段可能是根据用户id的范围值来确定的,比如用户id大于1000小于100000的用户在一个分片内.目前常用的有以下几种:

如何在高并发分布式系统中生成全局唯一Id

我了解的方案如下-------------------------- 1.  使用数据库自增Id 优势:编码简单,无需考虑记录唯一标识的问题. 缺陷: 1)         在大表做水平分表时,就不能使用自增Id,因为Insert的记录插入到哪个分表依分表规则判定决定,若是自增Id,各个分表中Id就会重复,在做查询.删除时就会有异常. 2)         在对表进行高并发单记录插入时需要加入事物机制,否则会出现Id重复的问题. 3)         在业务上操作父.子表(即关联表)插入时,需要