Spring Boot3 整合 Redis 实现库存扣减管理全解析

createh515小时前技术教程7

在当今竞争激烈的互联网大厂后端开发领域,高效且准确的库存管理系统对于电商、抽奖等各类业务场景至关重要。超卖现象一旦发生,不仅严重损害用户体验,还会对企业声誉造成负面影响。利用 Spring Boot3 整合 Redis 实现库存扣减管理,已成为众多开发者追求高性能、高并发系统的关键技术手段。接下来,我们将详细介绍其重要性、面临的挑战以及具体实现步骤。

库存扣减管理的重要性与挑战

在电商促销活动等场景中,大量用户同时抢购热门商品,如果库存扣减管理出现问题,就可能导致超卖现象。这不仅会让消费者对平台产生不满,还会给企业带来经济损失和声誉损害。传统基于数据库的库存扣减方式,例如使用一个字段存储库存,每次扣减时更新该字段,在高并发场景下,会出现大量请求阻塞等待锁,频繁访问数据库,导致系统性能急剧下降,甚至引发雪崩效应。即便采用将库存分层存储到多条记录并进行路由的优化方式,依然难以避免对数据库资源的大量占用。所以,寻求更高效的库存扣减解决方案迫在眉睫。

Spring Boot3 整合Redis实现库存扣减的具体步骤

引入依赖

首先,在 Spring Boot3 项目的 pom.xml 文件中,我们要添加必要的依赖。添加 spring-boot-starter-web 依赖,这将为项目提供 Web 服务支持,方便后续创建处理库存扣减的接口。代码如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

同时,引入
spring-boot-starter-data-redis 依赖,这是实现与 Redis 交互的关键。代码如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

此外,为了简化代码,我们还可以添加 lombok 依赖,它能自动生成常见的 Java 代码,如 Getter、Setter 等,减少冗余代码量。代码如下:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

Redis 分布式锁实现库存锁定

为确保在高并发环境下库存扣减的准确性,我们借助 Redis 分布式锁。创建一个 Redis 工具类,例如 redislockutil 。在这个类中,利用 Redis 的 setifabsent 命令来尝试设置锁,并设置一个合理的锁超时时间,比如 10 秒,以防止死锁的发生。在获取锁时,为每个请求生成唯一的标识,例如使用 UUID 作为 requestid,避免误删其他请求的锁。以下是工具类的部分示例代码:

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.UUID;

@Component
public class RedisLockUtil {
    private static final String LOCK_PREFIX = "lock:";
    private final StringRedisTemplate stringRedisTemplate;

    public RedisLockUtil(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    public boolean tryLock(String key, long expireTime) {
        String requestId = UUID.randomUUID().toString();
        Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(LOCK_PREFIX + key, requestId);
        if (result != null && result) {
            stringRedisTemplate.expire(LOCK_PREFIX + key, expireTime, TimeUnit.SECONDS);
            return true;
        }
        return false;
    }

    public void unlock(String key, String requestId) {
        String lockKey = LOCK_PREFIX + key;
        String currentValue = stringRedisTemplate.opsForValue().get(lockKey);
        if (requestId.equals(currentValue)) {
            stringRedisTemplate.delete(lockKey);
        }
    }
}

在库存服务实现中,当收到库存扣减请求时,首先尝试获取分布式锁。如果获取锁失败,说明当前有其他请求正在处理该商品的库存扣减,直接返回库存锁定失败信息。若成功获取锁,接着查询商品库存(在实际项目中,这里应查询数据库获取准确库存信息,为了示例简单,我们暂时模拟返回固定值)。如果库存不足,释放锁并返回库存不足提示。若库存充足,则进行库存扣减操作(同样,实际项目需操作数据库更新库存),最后释放锁。示例代码如下:

import org.springframework.stereotype.Service;
import javax.annotation.Resource;

@Service
public class StockService {
    @Resource
    private RedisLockUtil redisLockUtil;

    public String reduceStock(String productId, int quantity) {
        boolean locked = redisLockUtil.tryLock(productId, 10);
        if (!locked) {
            return "库存锁定失败";
        }
        try {
            // 模拟查询库存
            int stock = getStockFromDB(productId);
            if (stock < quantity) {
                return "库存不足";
            }
            // 模拟库存扣减
            boolean success = reduceStockInDB(productId, quantity);
            if (success) {
                return "库存扣减成功";
            } else {
                return "库存扣减失败";
            }
        } finally {
            redisLockUtil.unlock(productId, "requestId");
        }
    }

    private int getStockFromDB(String productId) {
        // 实际从数据库查询库存
        return 100;
    }

    private boolean reduceStockInDB(String productId, int quantity) {
        // 实际操作数据库更新库存
        return true;
    }
}

使用 Redis Lua 脚本实现无锁库存扣减

除了使用分布式锁,Redis 还提供了 Lua 脚本功能,让我们能够实现无锁库存扣减,进一步提升并发效率。在 Spring Boot 项目中配置 Lua 脚本,首先创建一个 Lua 脚本配置类,如 LuaConfig 。在这个类中,定义一个方法来加载库存扣减的 Lua 脚本文件。示例代码如下:

@Configuration
public class LuaConfig {
    @Bean
    public DefaultRedisScript<Long> stockReduceScript() {
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("stock-reduce.lua")));
        redisScript.setResultType(Long.class);
        return redisScript;
    }
}

然后编写名为 stock -reduce.lua 的脚本文件。在脚本中,首先判断库存键是否存在。若存在,获取当前库存值和需要扣减的数量。如果库存不足,直接返回失败标识;若库存充足,则使用 INCRBYFLOAT 命令进行库存扣减,并返回扣减后的库存值。示例脚本如下:

local key = KEYS[1]
local quantity = tonumber(ARGV[1])
if redis.call('EXISTS', key) == 0 then
    return -1
end
local stock = tonumber(redis.call('GET', key))
if stock < quantity then
    return -2
end
local newStock = stock - quantity
redis.call('SET', key, newStock)
return newStock

在实际使用时,将 Lua 脚本的 bean 和操作 Redis 服务器的 template 注入到服务类中,通过调用 Template 的 execute 方法,传入相应的键和参数,执行脚本实现库存扣减。示例代码如下:

@Service
public class StockService {
    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Resource
    private DefaultRedisScript<Long> stockReduceScript;

    public String reduceStockWithLua(String productId, int quantity) {
        List<String> keys = Collections.singletonList(productId);
        Long result = redisTemplate.execute(stockReduceScript, keys, String.valueOf(quantity));
        if (result == -1) {
            return "库存不存在";
        } else if (result == -2) {
            return "库存不足";
        } else {
            return "库存扣减成功,剩余库存:" + result;
        }
    }
}

注意事项

在实际生产环境中,使用 Redis 分布式锁时不能完全忽视死锁风险,务必设置合理的超时时间。建议使用功能更完善的 redisson 框架来替代自行实现的简单分布式锁。同时,当考虑 Redis 集群情况时,可能需要采用 redlock 算法来保证锁的可靠性。此外,库存操作最好与数据库事务配合使用,以确保数据一致性。即使 Redis 中的库存扣减成功,若数据库更新失败,也需要有相应的回滚机制。

总结

通过以上步骤,您可以在 Spring Boot3 项目中成功整合 Redis 实现库存扣减管理,通过合理运用分布式锁和 Lua 脚本等技术,我们能够构建出高效、稳定的库存管理系统,为业务的顺利开展提供坚实保障。

相关文章

推荐几个有深度的java项目

【文末获取】不同于世面上常见的商城,外卖系统,以下项目对netty,设计模式,系统架构设计等要求都比较高1.尼恩内部社群netty+zk千万级别im系统2.小傅哥知识星球netty网关抽奖系统3.鱼皮...

学Java真的没前途了吗?

学Java真的没前途了吗?Java真的凉了?转行还是硬刚?“培训班刚毕业没人要”、“投了3个月简历全挂”、“面到35岁HR直接劝退”...最近后台每天收到Java人的灵魂拷问:“现在学Java是不是4...

用考完试的旧书可以换盆栽?浙大学霸们的献爱心活动启动啦!

盆栽发烧友们注意!(敲黑板)几本不需要了的旧书,就可以换来心爱的小盆栽,各位真爱粉们能放过这么好的机会吗?NO WAY3月18日至19日,浙大第三届旧书换盆栽活动即将开展啦!第二届旧书换盆栽活动,让许...

回顾一下Redis吧!

1、Redis是什么?Redis(REmote DIctionary Server)是一个开源的高性能键值对存储数据库,也被称为数据结构服务器。它是一个内存中的数据存储系统,可以用作数据库、缓存和消息...

学习笔记:深入浅出redis

redisredis是当前最流行的非关系型数据库,很多场景都可以使用到redis,所以有了这篇文章的诞生为什么使用redis?在项目中,很多场景的并发量很大,如秒杀之类,若不使用redis缓存直接让其...