zookeeper global unique id generation

Posted by hemlata on Tue, 21 May 2019 01:50:38 +0200

One background

The traditional method of generating id can be realized by the self-increment of database, but it is not suitable in distributed environment. Dependence on databases can easily lead to singletons.

Why not use UUID? When you read someone's introduction on the internet, you should analyze it from two aspects:

In the case of large concurrency, UUID will duplicate.

2.UUID is instantaneous and its meaning is unclear. From a business point of view, if used as an order, the user queries the order in the case of data fragmentation, which is likely to be scattered in multiple databases and difficult to query.

The requirement of global unique id is high:

No single point of failure.

Good performance, millisecond return.

It is easy to store and divide DB in sequence.

Second, zookeeper is used to generate global unique id.

2.1 Generation of serial numbers using Zookeeper's znode data version


Client adopts: zkClient( https://github.com/adyliu/zkclient)


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

 java code implementation

public class ZKSeqTest {  
  
    //Create the'/ createSeq'node to store Seq in advance CreateMode.PERSISTENT  
    public static final String SEQ_ZNODE = "/seq";  
       
        //Distributed seq generation through znode data version  
        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 {  
      
      
    //Create the node "/ lock" CreateMode.PERSISTENT of the lock object in advance  
    public static final String LOCK_ZNODE = "/lock";  
    //Distributed Lock for Distributed seq Generation  
    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();  
    }  
  
}



Using znode with serial number to realize

[java] view plain copy



Back-end logs are temporary nodes that are automatically deleted after the session ends.

Three Open Source Solutions

There are also better open source implementations on the Internet that are worth learning from.


3.1Flikr

Self-increment Based on int/bigint

Excellent: Low development cost

Bad: If you need high performance, you need a dedicated MySQL cluster for generating self-increasing ID s only. Usability is not strong


3.2 Snowflake

Twitter uses zookeeper to implement a global ID generation service snowflake, https://github.com/twitter/snowflake, which can generate a globally unique 64bit ID.

The composition of the generated ID:

Time -- The first 41 bit s represent time, accurate to milliseconds, and can represent 69 years of data.  
Machine ID -- expressed in 10 bit s, which means 1024 machines can be deployed  
Sequence Number - Represented in 12 bit s, which means that each machine can generate up to 4096 ID s per millisecond


Excellent: high availability, fast speed, id saves more information.

Bad: zookeeper and independent snowflake dedicated servers need to be introduced


3.3instagram

instagram refers to flickr's scheme, combines twitter's experience, and utilizes the characteristics of Postgres database to realize a simpler and more reliable ID generation service.

 copy

  1. Use 41 bit s for storage time, accurate to milliseconds, and can last 41 years.  

  2. Use 13 bit s to store the logical fragment ID.  

  3. Using 10 bit s to store self-growing IDs means that up to 1024 IDs can be generated per machine per millisecond.  


Excellent: Low development cost

Poor: Storage process based on postgreSQL, poor versatility

There is also a redis-based global id generation scheme: http://blog.csdn.net/hengyunabc/article/details/44244951


Reference resources:

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


Topics: Java Zookeeper github Database