Distributed ID Series - Is Redis Cluster Implemented Distributed ID Suitable for Distributed ID

Keywords: Database Redis Jedis Java github

First is the project address:

https://github.com/maqiankun/distributed-id-redis-generator

For Redis Cluster to generate distributed ID s, here's the first thing to learn about the EVAL, EVALSHA command when redis uses the lua script:

https://www.runoob.com/redis/scripting-eval.html
https://www.runoob.com/redis/scripting-evalsha.html

Explain how Redis implements distributed ID s. Here, use the java language to explain:

Here we divide the distributed id into three parts: millisecond time, the number of nodes in the redis cluster, and the self-increasing sequence value of each redis node in each millisecond.

Then because the window is 64-bit, and then the first bit must be 0 when integer, the maximum value is 11111111111111111111111111111111111111111111111 11111111 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11Self-increasing sequence value in one millisecond

The milliseconds in which 41-bit binary 11111111111111111111111111111111 is converted to 10 are 21990232551, and then we converted 2199023255551 to 2039-09-07, which means it can be used for 20 years
Then 12 bits act as redis nodes, so the maximum is 11111111 11 with 12 bits, which means a maximum of 4095 redis nodes can be supported.
Then each 10-bit redis node increases its sequence value by itself, which is 111111111 111 at most 10-bit, meaning that each redis node can generate up to 1023 non-repeating id values per millisecond

Then we use java code to explain this principle. The following 1565165536640L is a millisecond value, then our redis node is set to 53, and then we set two different self-increasing sequence values, 1023 and 1023. The result below shows the redis node 53 in 1565165536640L.Two different distributed id values were generated

package io.github.hengyunabc.redis;

import java.text.SimpleDateFormat;
import java.util.Date;


public class Test {

    public static void main(String[] args) {
        long buildId = buildId(1565165536640L, 53, 1);
        System.out.println("Distributed id Yes:"+buildId);
        long buildIdLast = buildId(1565165536640L, 53, 1023);
        System.out.println("Distributed id Yes:"+buildIdLast);
    }
    
    public static long buildId(long miliSecond, long shardId, long seq) {
        return (miliSecond << (12 + 10)) + (shardId << 10) + seq;
    }


}
public class Test {

    public static void main(String[] args) {
        long buildId = buildId(1565165536640L, 53, 1);
        System.out.println("Distributed id Yes:"+buildId);
        long buildIdLast = buildId(1565165536640L, 53, 1023);
        System.out.println("Distributed id Yes:"+buildIdLast);
    }
    
    public static long buildId(long miliSecond, long shardId, long seq) {
        return (miliSecond << (12 + 10)) + (shardId << 10) + seq;
    }


}

The result is as follows

The distributed id is: 6564780070991352833
 The distributed id is: 6564780070991353855

So someone has to say, you are not in line with the distributed id settings. There is no readability at all. Here we can get the generation milliseconds time value of this distributed id by using the following methods.

package io.github.hengyunabc.redis;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Test {

    public static void main(String[] args) {
        long buildId = buildId(1565165536640L, 53, 1);
        parseId(buildId);
        long buildIdLast = buildId(1565165536640L, 53, 1023);
        parseId(buildIdLast);
    }
    
    public static long buildId(long miliSecond, long shardId, long seq) {
        return (miliSecond << (12 + 10)) + (shardId << 10) + seq;
    }

    public static void parseId(long id) {
        long miliSecond = id >>> 22;
        long shardId = (id & (0xFFF << 10)) >> 10;
        System.err.println("Distributed id-"+id+"The time generated is:"+new SimpleDateFormat("yyyy-MM-dd").format(new Date(miliSecond)));
        System.err.println("Distributed id-"+id+"In"+shardId+"Number redis Node Generation");
    }

}

That's ok ay. Ha-ha.

Distributed id-6564780070991352833 generated at 2019-08-07
 Distributed id-6564780070991352833 generated at redis node 53
 Distributed id-6564780070991353855 generated at 2019-08-07
 Distributed id-6564780070991353855 generated at redis node 53

Distributed id creation for redis in cluster Edition

At this point, the ports of my distributed redis cluster are 6380, 6381, respectively
First, the Evalsha command security sha1 check code is generated as follows.
The first step is to generate the security sha1 check code corresponding to port 6380, first enter the bin directory of redis, then execute the following command to download the lua script

wget https://github.com/maqiankun/distributed-id-redis-generator/blob/master/redis-script-node1.lua


Then execute the following command to generate the security sha1 check code corresponding to port 6380, and you see that it is be6d4e21e9113bf8af47ce72f3da18e00580d402

./redis-cli -p 6380 script load "$(cat redis-script-node1.lua)"

The first step is to generate the security sha1 check code corresponding to port 6381, first enter the bin directory of redis, then execute the following command to download the lua script

wget https://github.com/maqiankun/distributed-id-redis-generator/blob/master/redis-script-node2.lua

Then execute the following command to generate the corresponding security sha1 check code for port 6381, and see that it is 97f65601d0aaf1a0574da69b1ff3092969c4310e

./redis-cli -p 6381 script load "$(cat redis-script-node2.lua)"

Then we use the sha1 check above and the code below to generate the distributed id

The project picture is as follows

The code for the IdGenerator class is as follows


package io.github.hengyunabc.redis;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.tuple.Pair;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.exceptions.JedisConnectionException;

public class IdGenerator {
    /**
     * JedisPool, luaSha
     */
    List<Pair<JedisPool, String>> jedisPoolList;
    int retryTimes;

    int index = 0;

    private IdGenerator(List<Pair<JedisPool, String>> jedisPoolList,
            int retryTimes) {
        this.jedisPoolList = jedisPoolList;
        this.retryTimes = retryTimes;
    }

    static public IdGeneratorBuilder builder() {
        return new IdGeneratorBuilder();
    }

    static class IdGeneratorBuilder {
        List<Pair<JedisPool, String>> jedisPoolList = new ArrayList();
        int retryTimes = 5;

        public IdGeneratorBuilder addHost(String host, int port, String luaSha) {
            jedisPoolList.add(Pair.of(new JedisPool(host, port), luaSha));
            return this;
        }

        public IdGenerator build() {
            return new IdGenerator(jedisPoolList, retryTimes);
        }
    }

    public long next(String tab) {
        for (int i = 0; i < retryTimes; ++i) {
            Long id = innerNext(tab);
            if (id != null) {
                return id;
            }
        }
        throw new RuntimeException("Can not generate id!");
    }

    Long innerNext(String tab) {
        index++;
        int i = index % jedisPoolList.size();
        Pair<JedisPool, String> pair = jedisPoolList.get(i);
        JedisPool jedisPool = pair.getLeft();

        String luaSha = pair.getRight();
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            List<Long> result = (List<Long>) jedis.evalsha(luaSha, 2, tab, ""
                    + i);
            long id = buildId(result.get(0), result.get(1), result.get(2),
                    result.get(3));
            return id;
        } catch (JedisConnectionException e) {
            if (jedis != null) {
                jedisPool.returnBrokenResource(jedis);
            }
        } finally {
            if (jedis != null) {
                jedisPool.returnResource(jedis);
            }
        }
        return null;
    }

    public static long buildId(long second, long microSecond, long shardId,
            long seq) {
        long miliSecond = (second * 1000 + microSecond / 1000);
        return (miliSecond << (12 + 10)) + (shardId << 10) + seq;
    }

    public static List<Long> parseId(long id) {
        long miliSecond = id >>> 22;
        long shardId = (id & (0xFFF << 10)) >> 10;

        List<Long> re = new ArrayList<Long>(4);
        re.add(miliSecond);
        re.add(shardId);
        return re;
    }
}

Example's code is shown below. The purpose of the while loop below is to print multiple distributed ids. The tab variable below is the parameter inside the evalsha command and can be defined to suit your needs

package io.github.hengyunabc.redis;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

public class Example {

    public static void main(String[] args) {
        String tab = "This is evalsha Parameters inside commands, defined arbitrarily";

        IdGenerator idGenerator = IdGenerator.builder()
                .addHost("47.91.248.236", 6380, "be6d4e21e9113bf8af47ce72f3da18e00580d402")
                .addHost("47.91.248.236", 6381, "97f65601d0aaf1a0574da69b1ff3092969c4310e")
                .build();
        int hello = 0;
        while (hello<3){
            long id = idGenerator.next(tab);

            System.out.println("Distributed id value:" + id);
            List<Long> result = IdGenerator.parseId(id);

            System.out.println("Distributed id Generated at:" + new SimpleDateFormat("yyyy-MM-dd").format(new Date(result.get(0))) );
            System.out.println("redis node:" + result.get(1));
            hello++;
        }

    }
}

At this point, the print results are as follows

Distributed id value: 6564819854640022531
 Distributed id generation time is: 2019-08-07
 redis node: 1
 Distributed id value: 6564819855189475330
 Distributed id generation time is: 2019-08-07
 redis node: 0
 Distributed id value: 6564819855361442819
 Distributed id generation time is: 2019-08-07
 redis node: 1

Here the redis cluster version of the distributed id is complete, perfect

Is the distributed ID implemented by the Redis cluster suitable for use as a distributed id?

I think the Redis cluster implementation of distributed ID can be used basically in our development, but I still think it has the following two problems:

1: Here we can compare the mechanism of database self-incrementing ID in the previous article. Actually, Redis cluster can solve the performance problem of creating distributed ID in database cluster, but it is still difficult to expand Redis cluster system horizontally. If you want to add Redis node to Redis cluster in the future, it will also meet with database set.Node expansion of a group can be equally cumbersome.
2: Also, if Redis is not used in your project, then you have to introduce new components, which is a more troublesome problem.

Text Link

Other distributed ID family shortcuts:
Distributed ID Series (1) - Why Distributed IDs and Business Requirements for Distributed IDs are Needed
Distributed ID Series (2) - Is UUID Suitable for Distributed ID
Distributed ID Series (3) - Is Database Self-Increasing ID Mechanism Suitable for Distributed ID
Distributed ID Series (4) - Is Distributed ID implemented by Redis Cluster suitable for Distributed ID
Distributed ID Series (5) - Is Snowflake the Snowflake algorithm for Twitter suitable for distributed IDs

Lark web address
https://www.itqiankun.com/article/1565227901
https://blog.csdn.net/hengyunabc/article/details/44244951
https://tech.meituan.com/2017/04/21/mt-leaf.html
https://segmentfault.com/a/1190000011282426
https://www.jianshu.com/p/9d7ebe37215e

Posted by dawsba on Wed, 14 Aug 2019 20:37:06 -0700