Redis combat 9-globally unique ID

When issuing coupons, each store can issue coupons. When users rush to buy, if the ID in the coupon table uses the self-increasing ID of the database, there will be the following problems:

1: The law of id is too obvious, easy to be brushed

2: When the amount of data is large, it will be limited by the single table data

Disadvantage scenario analysis:

ID rule scenario: If our ID has too obvious rules, users or business opponents can easily guess some of our sensitive information, such as how many orders the mall sold in a day, which is obviously inappropriate.

Single table limitation: As the scale of our mall becomes larger and larger, the capacity of a single mysql table should not exceed 500W. After the amount of data is too large, we need to split the database and table, but after splitting the table, they logically They are the same table, so their ids cannot be the same, so we need to ensure the uniqueness of ids.

Global ID Generator

The global ID generator is a tool used to generate a globally unique ID in a distributed system. Generally, the following characteristics need to be concealed:

Uniqueness, high availability, incrementality, security, high performance

Globally unique ID generation strategy:

UUID, Redis auto-increment, snowflake algorithm, database auto-increment

Redis self-increment ID strategy:

1: One key per day, convenient to count orders;

2: IDs are all time stamps + counters

Actual combat: Realize globally unique ID by splicing other information based on Redis

The globally unique ID is of long type, and the timestamp is based on a certain point in time. For example, we can use it for 69 years starting from 2022.11.26 23:00:00.

Thinking 1: Get the second of the current time:

Thinking 2: How is the timestamp calculated?

Use seconds from current time - seconds from initial time

Thinking 3: How to set the serial number?

Use the setnx command of Redis, it is best to add the current year, month and day

Thinking 4: How to splice?

Because what we need to return is of type long, if you use string splicing, you need to convert it. Also know that using string splicing, when the amount of concurrency is large, there will also be performance problems. So how should we deal with it?

Note: Let's look at the format of the globally unique ID again. As shown in the figure above, we can see that there are 64 bits in total, of which the sign bit is 1, and the timestamp is 31 bits. The serial number is 32 digits, have you found anything? If we move the timestamp to the left by 32 bits (because the serial number is 32 bits. Move the position to the left to make room for the serial number), is it the sign bit + the timestamp?

1: We also know that the fastest left movement in the computer is x<< digits.

2: We also need to know that in the computer | or calculation: bitwise OR operation "|"

According to the above, we can know that after the serial number of the bit operation, it is the value of the serial number. Whatever the serial number is, that's it.

Therefore, we can know that the splicing code is as follows: timeStamp << 32 |no;

Final code:

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
 
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
 
/**
 * @author 凯哥Java
 * @description  基于Redis实现62位的全局唯一ID
 * @company
 *  */
@Component
public class RedisIdWorker {
 
    private static final long BEGIN_TIMESTAMP = 1669503600L;
 
 
    private final StringRedisTemplate stringRedisTemplate;
 
    /**
     * 序列号的位数
     */
    private static final int COUNT_BITS = 32;
 
    public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate; 
        //3: splicing return
    }
 
    /** 
     * Get id 
     * @param kyePrefix 
     * @return 
     */ 
    public long nextId(String kyePrefix){ 
        //1: Generation timestamp = current timestamp - start timestamp 
        LocalDateTime now = LocalDateTime.now(); 
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC); 
        long timeStamp = nowSecond - BEGIN_TIMESTAMP; 
 
        //2: Generate a serial number. Use sexNx. Add the current year, month, and day to it 
        //Get your current time, month, and day 
        String date = now.format (DateTimeFormatter.ofPattern("yyyy:MM:dd")); 
        //Start 32-bit serial number 
        long no = stringRedisTemplate.opsForValue().increment("icr:"+kyePrefix+":"+date); 
        return timeStamp << COUNT_BITS|no; 
 
    } 
    /** 
     * Get the milliseconds of the specified time 
     * @param args 
     */ 
    public static void main(String[] args) { 
        LocalDateTime time = LocalDateTime.of(2022,11,26,23,00,00 ); 
        long second = time.toEpochSecond(ZoneOffset.UTC); 
        System.out.println(second); 
 
    } 
} 
 
Test: use multithreading and countdownlatch 
private ExecutorService executorService = Executors.newFixedThreadPool(500); 
 
    @Resource 
    private RedisIdWorker redisIdWorker; 
 
    @ Test 
    public void RedisIdWorkerTest() throws InterruptedException { 
        CountDownLatch latch = new CountDownLatch(300); 
        Runnable task = ()->{ 
            for(int i = 0;i<100;i++){
                long id = redisIdWorker.nextId("myorder");
                System.out.println(id);
            }
            latch.countDown();
        };
 
        long begin = System.currentTimeMillis();
        for(int i = 0;i< 300;i++){
            executorService.submit(task);
        }
        latch.await();
        long endTime = System.currentTimeMillis();
        System.out.println("耗时:"+(endTime-begin));
    }

Guess you like

Origin blog.csdn.net/kaizi_1992/article/details/128784616