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

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

2. 生成方案

2.5 Redis ID

Redis可以作为一个可靠的存储和生成分布式ID的方案之一。 Redis分布式ID实现主要是通过提供像 INCR 和 INCRBY 这样的自增原子命令,由于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 详解

相关推荐
程序猿DD13 分钟前
OctaFuse Gateway 2.15.0:MCP 聚合、向量 Embeddings 与官方目录同步
后端·agent
安易算力2 小时前
昇腾CANN算子开发与MindSpore部署调优实践:从Ascend C到模型推理全链路
网络·架构
蛋蛋的就会顺顺的2 小时前
hot100 p0——栈,贪心算法,动态规划
java·前端·javascript·算法·leetcode·力扣
余槐i3 小时前
PostgreSQL 函数索引报错 must be immutable:3种绕法与代价
数据库·后端·sql·postgresql·性能优化
需要8263 小时前
Spring Bean 生命周期:从实例化到销毁的完整钩子
java·后端·spring
小 黄 鸡3 小时前
HashMap 面试必会知识点详解(JDK 8)
java·面试·职场和发展
凤山老林4 小时前
Spring Boot 接入国密 SM2/SM3/SM4 的一些实践记录
spring boot·后端·python
伯恩bourne5 小时前
Spring Boot Bean 注册的两种方式:@Component 家族 vs @Configuration + @Bean
java·spring boot·后端
张彦峰ZYF5 小时前
停顿短不等于延迟低:G1、ZGC 与低延迟服务的目标分野、代价账本与选型标定
后端·回收暂停、应用停止总时长·到达安全点、分配停滞·屏障吃吞吐、并发线程吃核心·并发窗口吃堆余量·分区式靠挑回收集兑现目标·染色指针式不支持压缩引用
silvamux_5 小时前
curl 测试 OpenAI 兼容 API 的完整流程
java·人工智能·缓存·ai·oneapi