高并发系统-分布式唯一ID生成(三)-RedisID生成及应用

本文紧接着上文高并发系统-分布式唯一ID生成(二)-号段模式及应用

2. 生成方案

2.5 Redis ID

Redis可以作为一个可靠的存储和生成分布式ID的方案之一。 Redis分布式ID实现主要是通过提供像 INCRINCRBY 这样的自增原子命令,由于Redis单线程的特点,可以保证ID的唯一性和有序性。

1. 优点

性能不错、每秒10万并发量,性能是优于数据库。

生成的 ID 是有序递增的。

2. 缺点

redis 宕机后不可用,RDB重启数据丢失会重复ID。

自增,数据量易暴露。

引入新的组件,增加系统复杂度,并且需要占用网络资源,性能要比本地生成慢

3. 应用场景

适用于TPS要求不高场景或者可通过活动区分开,比如发放优惠券券码,可以通过批次作为前缀,每个批次使用不同REDIS自增序列

尤其比较较高并发生成唯一ID且ID长度序列一致场景,比如券码,京东E卡的卡号之类的

4. 实践

场景:生成一个10位长度券码,要求是唯一的,并且长度都为10位。

思路:

通过REDIS自增ID实现编码,序列值转换成35进制,在后面加一位Z,剩余位数用随机数填充保证固定长度唯一编码 比如序列为100,生成的最终编码为2TZB47AIPX

通过上述方式,保证生成值唯一,且能够达到35^10个

代码实现:

核心逻辑

java 复制代码
@Service
@Slf4j
public class RedisIdSeq {
    private final static char[] CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y'};
    private final int SCALE = 35;
    private final static char SPLIT = 'Z';

    @Autowired
    private RedisService redisService;

    public List<String> createCode(int maxNum, int size, String priFix, String key, int maxLength) {
        int curIndex = redisService.incrAndGetIntValue(key, size);
        if (curIndex > maxNum) {
            log.error("createCode exceed max num ={}", maxNum);
            return Collections.emptyList();
        }
        List<String> resultList = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            resultList.add(generateCode(priFix, curIndex--, maxLength));
        }
        return resultList;
    }

    private String generateCode(String preFix, int num, int maxLength) {
        int temp = num;
        int index = 0;
        StringBuilder stringBuilder = new StringBuilder(maxLength);
        stringBuilder.append(preFix);
        while (temp > 0) {
            index = num % SCALE;
            stringBuilder.append(CHARS[index - 1]);
            temp = temp / SCALE;
        }
        if (stringBuilder.length() >= maxLength) {
            return stringBuilder.toString();
        }
        stringBuilder.append(SPLIT);
        int randomSize = maxLength - stringBuilder.length();
        for (int i = 0; i < randomSize; i++) {
            stringBuilder.append(CHARS[SecureRandomUtil.getInstance().nextInt(SCALE)]);
        }
        return stringBuilder.toString();
    }
}

使用redission取值

java 复制代码
public int incrAndGetIntValue(String key, Integer addNum) {
    RAtomicLong rAtomicLong = redissonClient.getAtomicLong(key);
    return (int) rAtomicLong.addAndGet(addNum);
}

最终的test用例如下

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@Slf4j
public class RedisIdSeqTest {
    @Resource
    private RedisIdSeq redisIdSeq;

    @Test
    public void testCreateCode() {
        List<String> prefixList = redisIdSeq.createCode(10000, 1, "", "ABC", 10);
        log.info("prefixList={}", prefixList);
        List<String> codeList = redisIdSeq.createCode(10000, 10, prefixList.get(0), prefixList.get(0), 20);
        log.info("codeList={}", codeList);
    }
}

最终生成结果如下:

xml 复制代码
2023-12-24 21:47:28.978|INFO |test|1|127.0.0.1|8173c001efdc4303bab4aaf23464a2b8|prefixList=[1Z6PEOSSA3]|com.toby.dynamic.data.source.redis.id.RedisIdSeqTest
2023-12-24 21:47:31.552|INFO |test|1|127.0.0.1|8173c001efdc4303bab4aaf23464a2b8|codeList=[1Z6PEOSSA39Z1VC68J5P, 1Z6PEOSSA38Z468YSILR, 1Z6PEOSSA37ZAJQTERNX, 1Z6PEOSSA36Z64KRUV6A, 1Z6PEOSSA35Z9K7F06K2, 1Z6PEOSSA34ZULBTKOJ4, 1Z6PEOSSA33Z733FXM2R, 1Z6PEOSSA32ZD52WWEOC, 1Z6PEOSSA31ZAMBGCBW2, 1Z6PEOSSA30Z7BTE9SN9]|com.toby.dynamic.data.source.redis.id.RedisIdSeqTest

参考
分布式 ID 详解

相关推荐
llwszx1 小时前
深入理解Java锁原理(一):偏向锁的设计原理与性能优化
java·spring··偏向锁
小马爱打代码1 小时前
微服务外联Feign调用:第三方API调用的负载均衡与容灾实战
微服务·架构·负载均衡
云泽野2 小时前
【Java|集合类】list遍历的6种方式
java·python·list
二进制person2 小时前
Java SE--方法的使用
java·开发语言·算法
小阳拱白菜3 小时前
java异常学习
java
程序员岳焱4 小时前
Java 与 MySQL 性能优化:Java 实现百万数据分批次插入的最佳实践
后端·mysql·性能优化
FrankYoou4 小时前
Jenkins 与 GitLab CI/CD 的核心对比
java·docker
麦兜*5 小时前
Spring Boot启动优化7板斧(延迟初始化、组件扫描精准打击、JVM参数调优):砍掉70%启动时间的魔鬼实践
java·jvm·spring boot·后端·spring·spring cloud·系统架构
KK溜了溜了5 小时前
JAVA-springboot 整合Redis
java·spring boot·redis
大只鹅5 小时前
解决 Spring Boot 对 Elasticsearch 字段没有小驼峰映射的问题
spring boot·后端·elasticsearch