抢课_电商商品预约等等类似通用业务设计---基于 bitset 实现

✅基于 bitset 实现高效的 商品_选修课... 预约

业务场景

  • 在电商类行业:

针对一些热点商品,我们提供了预约功能,就像天猫超市卖茅台一样,需要提前预约才能购买,不约不能买

  • 在校园抢课系统:

针对一些热点选修课,我们提供了预约抢课功能,就像天猫超市卖茅台一样,需要提前预约才能购抢,不约不能抢

同一个商品,我们想要记录哪些用户预约过,同时需要能够快速查询,并且可以尽可能的减少存储空间,避免浪费,我们选择了使用 BitSet。

为了快速的存取,我们使用 Redis,正好Redis 也支持 bitSet 数据结构,同时,我们为了避免Redis 挂了,我们也要存储一张预约表, 在数据库中做持久化。

Redis 的 BitSet 是一种特殊的字符串类型,它允许你对字符串中的每一位(bit)进行操作。每个位可以是 0 或 1,因此你可以将 BitSet 视为一个非常高效的布尔数组。

使用 Redis BitSet 的好处

  1. 节省内存
  2. 每个位只占用 1 位(bit),相比于布尔数组或整数数组,BitSet 可以显著减少内存占用。这对于存储大量布尔值(如用户是否预约了某个商品)非常有用。
  3. 高效的操作
  4. Redis 提供了丰富的命令来操作 BitSet,例如 SETBIT、GETBIT、BITCOUNT 等。这些命令可以高效地设置、获取和统计位的状态。
  5. 原子性操作
  6. Redis 的 BitSet 操作是原子性的,这意味着多个客户端可以同时对同一个 BitSet 进行操作而不会产生竞争条件。这在高并发场景下非常重要。

因为我们的用户 ID 是一定不重复的,并且可以转换成integer,所以没一个用户可以映射到一个唯一的 bit 上面,这样预约过的用户对应的 bit 设置为1 ,没预约过的默认为0,就能实现存储预约信息了。

那么我们就可以把商品 id 作为 key,存储一个 bitset,bitset 中存储的是已经预约过的用户的id 列表。

具体实现

java 复制代码
/**
 * 商品预约
 * 先更新缓存,再更新数据库。优先保证缓存,如果出现不一致,以缓存为主
 *
 * @param request
 * @return
 */
@Transactional(rollbackFor = Exception.class)
public GoodsBookResponse book(GoodsBookRequest request) {
    // 因为用户id都是不重复的,并且可以转换成integer,所以这里可以使用BitSet来存储预约信息,减少存储量
    RBitSet bookedUsers = redissonClient.getBitSet(BOOK_KEY + request.getGoodsType() + CacheConstant.CACHE_KEY_SEPARATOR + request.getGoodsId());
    // 不报错则成功
    bookedUsers.set(Integer.parseInt(request.getBuyerId()));

    GoodsBook existBook = goodsBookMapper.selectByGoodsIdAndBuyerId(request.getGoodsId(), request.getGoodsType().name(), request.getBuyerId());
    if (existBook != null) {
        return new GoodsBookResponse.GoodsBookResponseBuilder().bookId(existBook.getId()).buildSuccess();
    }
    GoodsBook goodsBook = GoodsBook.createBook(request);
    boolean result = save(goodsBook);
    Assert.isTrue(result, () -> new BizException(RepoErrorCode.INSERT_FAILED));

    //异步为热门商品添加缓存,失败不影响业务
    Thread.ofVirtual().start(() -> {
        // 检查是否为热门商品
        long bookedCount = bookedUsers.cardinality();
        if (bookedCount > HOT_GOODS_BOOK_COUNT) {
            hotGoodsService.addHotGoods(request.getGoodsId(), request.getGoodsType().name());
        }
    });

    return new GoodsBookResponse.GoodsBookResponseBuilder().bookId(goodsBook.getId()).buildSuccess();
}

就这样,就可以把一个预约的信息保存下来了。

当29这个用户 ID 预约过之后:

想要查询某个用户是否预约过的时候,可以:

java 复制代码
public boolean isBooked(String goodsId, GoodsType goodsType, String buyerId) {
    RBitSet bookedUsers = redissonClient.getBitSet(BOOK_KEY + goodsType + CacheConstant.CACHE_KEY_SEPARATOR + goodsId);
    return bookedUsers.get(Integer.parseInt(buyerId));
}

这样就能查看某个用户是否在 bitset 中,即是否预约过。

以上操作其实就相当于执行了命令:

java 复制代码
GETBIT "goods:book:COLLECTION:10085" "39"
相关推荐
卷毛的技术笔记15 分钟前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
_codemonster22 分钟前
30分钟快速搭建 Spring Cloud Alibaba 微服务实战(一)
微服务·架构·毕业设计·课程设计
会编程的土豆29 分钟前
Go 语言反射(Reflection)详解
开发语言·后端·golang
Cosolar31 分钟前
从零写一个 Attention Is All You Need
人工智能·面试·架构
喵个咪1 小时前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
basketball6161 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
qq_2518364571 小时前
SpringBoot+Vue 共享电池柜管理系统 完整实现 前后端分离项目实战 完整代码
vue.js·spring boot·后端
zhangxingchao2 小时前
AI 大模型核心六:量化、Workflow 与 Agent、多轮 RAG
前端·人工智能·后端
qcx233 小时前
【系统学AI】09 Multi-Agent架构(2026版):从学术理论到工业级实践
java·人工智能·架构·multi-agent·claude agent
IT_陈寒3 小时前
Vite打包时遇到的坑,原来问题出在这里
前端·人工智能·后端