高并发系统-分布式唯一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 详解

相关推荐
高频因子挖掘机18 分钟前
历史行情返回顺序变了,为什么按行号计算指标很危险?
后端·github·api
小天源1 小时前
从 Nginx 日志到访问分析:轻量化网站监测服务的架构实践
jvm·nginx·网络安全·架构·网络监控
紧跟先前的步伐1 小时前
Go AI Agent 实战 03|Token、上下文窗口与模型调用成本
java·人工智能·golang
数据工匠老o1 小时前
异构数据库DDL同步:260条链路的结构变更自动适配方案
数据库·架构
const_qiu2 小时前
第四章 00:开篇 —— 从「会聊天」到「记得住、会干活、能协作」
java·开发语言·人工智能·python
Wang's Blog2 小时前
Java 项目实战: 交友项目 - 个人资料管理的更新接口实现与坑
java·开发语言·交友
团子哒哒2 小时前
数组、切片和Map
后端
Promising_GEO2 小时前
GEE实战02期:从卫星波段开始:利用 Sentinel-2 自己计算 NDVI
java·sentinel·遥感·空间分析
专注仿真2 小时前
ThinkPHP6 API接口开发实例:从零搭建RESTful风格接口
后端·php·restful
大文说跨境2 小时前
亚马逊多账号管理浏览器怎么选?云登/紫鸟/AdsPower 对比
java·前端·自动化