抢课_电商商品预约等等类似通用业务设计---基于 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"
相关推荐
chxii2 分钟前
3.1go流程控制语句
开发语言·后端·golang
追逐时光者5 分钟前
在 ASP.NET Core 中创建中间件的 4 种方式
后端·.net
霍珵璁8 分钟前
Objective-C语言的物联网
开发语言·后端·golang
LeonNo1120 分钟前
golang编写UT:applyFunc和applyMethod区别
开发语言·后端·golang
m0_748248231 小时前
【JavaWeb06】Tomcat基础入门:架构理解与基本配置指南
java·架构·tomcat
uhakadotcom1 小时前
WebGPU:解锁浏览器中的高性能图形和计算
后端·面试·github
uhakadotcom1 小时前
【新手必看】 Mitsuba科研渲染器入门指南:从零玩转光影魔术
后端·面试·github
uhakadotcom1 小时前
深度学习超级采样(DLSS)技术解析
后端·面试·github
Apifox1 小时前
一分钟,让你的 API 文档支持 MCP 使用,Apifox 新功能上线!!!
前端·后端·mcp
鲁子狄2 小时前
[笔记] SpringBoot3 使用 EasyExcel 封装工具类实现复杂 Excel 数据处理:使用Java构建高效的数据导入解决方案
java·后端