基于 Redis 来确保库存扣减的原子性


前言

下面是一个用 Java 实现的分布式锁示例,基于 Redis 来确保库存扣减的原子性,避免超卖问题。我们将使用 Jedis 作为 Redis 客户端,并模拟一个电商系统中的库存扣减场景。


1. 环境准备

在运行代码之前,请确保以下环境已准备好:

  1. 安装 Redis 并启动 Redis 服务。
  2. 在项目中引入 Jedis 依赖(Maven 配置如下):
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.2.3</version>
</dependency>

2. Java 实现

以下是完整的 Java 代码实现:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
import java.util.UUID;

public class DistributedLockExample {

    private static final String REDIS_HOST = "localhost";
    private static final int REDIS_PORT = 6379;
    private static final String LOCK_KEY = "lock:item_123"; // 锁的键
    private static final String STOCK_KEY = "stock:item_123"; // 库存的键
    private static final int STOCK_INITIAL = 10; // 初始库存

    public static void main(String[] args) {
        // 初始化 Redis 连接
        try (Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT)) {
            // 初始化库存
            jedis.set(STOCK_KEY, String.valueOf(STOCK_INITIAL));

            // 模拟多个用户并发购买
            for (int i = 1; i <= 15; i++) { // 15 个用户尝试购买
                String userId = "user_" + i;
                new Thread(() -> purchaseItem(jedis, userId)).start();
            }
        }
    }

    /**
     * 购买商品
     *
     * @param jedis  Redis 客户端
     * @param userId 用户 ID
     */
    private static void purchaseItem(Jedis jedis, String userId) {
        String lockValue = UUID.randomUUID().toString(); // 锁的值,确保唯一性
        int retryCount = 3; // 重试次数
        boolean lockAcquired = false;

        // 尝试获取锁
        while (retryCount > 0) {
            String result = jedis.set(LOCK_KEY, lockValue, SetParams.setParams().nx().ex(10)); // nx: 键不存在时才设置,ex: 锁的过期时间为 10 秒
            if ("OK".equals(result)) {
                lockAcquired = true;
                break;
            }
            retryCount--;
            try {
                Thread.sleep(1000); // 等待 1 秒后重试
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        if (!lockAcquired) {
            System.out.println(userId + " 获取锁失败,无法购买");
            return;
        }

        try {
            // 查询库存
            int stock = Integer.parseInt(jedis.get(STOCK_KEY));
            if (stock <= 0) {
                System.out.println(userId + " 购买失败,库存不足");
                return;
            }

            // 扣减库存
            jedis.decr(STOCK_KEY);
            System.out.println(userId + " 购买成功,库存剩余: " + (stock - 1));
        } finally {
            // 释放锁
            if (lockValue.equals(jedis.get(LOCK_KEY))) {
                jedis.del(LOCK_KEY);
            }
        }
    }
}

3. 代码说明

  1. Redis 连接

    • 使用 Jedis 客户端连接 Redis,并初始化库存。
  2. 分布式锁的实现

    • 使用 jedis.set 方法,结合 nx(键不存在时才设置)和 ex(设置过期时间)参数,实现分布式锁。
  3. 库存扣减

    • 在获取锁后,查询库存并扣减库存,确保操作的原子性。
  4. 锁的释放

    • 在 finally 块中释放锁,确保锁一定会被释放,避免死锁。
  5. 重试机制

    • 如果获取锁失败,用户会等待并重试,直到成功获取锁或重试次数用尽。

4. 运行结果

运行代码后,输出可能如下:

user_1 购买成功,库存剩余: 9
user_2 购买成功,库存剩余: 8
user_3 购买成功,库存剩余: 7
user_4 购买成功,库存剩余: 6
user_5 购买成功,库存剩余: 5
user_6 购买成功,库存剩余: 4
user_7 购买成功,库存剩余: 3
user_8 购买成功,库存剩余: 2
user_9 购买成功,库存剩余: 1
user_10 购买成功,库存剩余: 0
user_11 购买失败,库存不足
user_12 购买失败,库存不足
user_13 获取锁失败,无法购买
user_14 获取锁失败,无法购买
user_15 获取锁失败,无法购买

5. 总结

通过 Java 和 Redis 实现分布式锁,可以有效解决电商系统中的库存扣减问题,避免超卖现象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

和烨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值