redis实现全局唯一id

image

Redis 中的INCREMENT操作。

简单来说,Redis 的 INCREMENT 操作指的是对一个存储在 Redis 中的整数值进行“增加”的命令。这些命令是原子性(Atomic) 的,这是它们最核心、最重要的特性。

在深入命令之前,必须先理解“原子性”。原子性意味着一个操作是不可分割的。即使有多个客户端同时连接到 Redis 服务器并对同一个键(Key)进行增减操作,Redis 也会确保这些命令一个接一

个地顺序执行,而不会出现竞争条件(Race Condition)。

例如,如果键 mycounter 的值是 10,同时有 1000 个客户端执行 INCR mycounter 命令,最终的结果一定是 1010,绝对不会出现丢失任何一次增加的情况。

这使得 Redis 的 INCREMENT 命令成为实现高并发计数器的完美选择。

具体实现:

RedisIdWorker

package com.hmdp.utils;

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;

@Component
public class RedisIdWorker {

    private static final long SEGIN_TIMESTAMP = 1735689600L;

    private static final int COUNT_BITS = 32;

    private StringRedisTemplate stringRedisTemplate;

    public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }


    public long nextId(String keyPrefix) {
        // 1.生成时间戳
        LocalDateTime now = LocalDateTime.now();
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timestamp = nowSecond - SEGIN_TIMESTAMP;

        // 2.生成序列号,获取当前日期,自增长
        String data = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + data);

        // 3.拼接并返回
        return timestamp << COUNT_BITS | count;
    }
}

测试:

package com.hmdp;

import com.hmdp.entity.Shop;
import com.hmdp.service.impl.ShopServiceImpl;
import com.hmdp.utils.CacheClient;
import com.hmdp.utils.RedisIdWorker;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

import java.util.concurrent.*;

import static com.hmdp.utils.RedisConstants.CACHE_SHOP_KEY;

@SpringBootTest
class HmDianPingApplicationTests {

    @Resource
    private CacheClient cacheClient;

    @Resource
    private ShopServiceImpl shopService;

    @Resource
    private RedisIdWorker redisIdWorker;
    
    private ExecutorService es = Executors.newFixedThreadPool(500);

    @Test
    void testIdWorker() throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(300);
        Runnable task = () ->{
            for(int i = 0;i < 100;i++){
                long id = redisIdWorker.nextId("order");
                System.out.println(id);
            }
            latch.countDown();
        };
        long start = System.currentTimeMillis();
        for(int i = 0;i < 300;i++){
            es.submit(task);
        }
        latch.await();
        long end = System.currentTimeMillis();
        System.out.println("time:" + (end - start));
    }

    @Test
    void testSaveShop() throws InterruptedException {
        Shop shop = shopService.getById(1L);

        cacheClient.setWithLogicalExpire(CACHE_SHOP_KEY + 1L,shop,10L, TimeUnit.SECONDS);
    }
}

posted @ 2025-09-16 19:47  雨花阁  阅读(14)  评论(0)    收藏  举报