当前位置:首页 > Java

java商品如何扣减

2026-03-21 14:40:15Java

商品库存扣减的基本逻辑

商品库存扣减是电商系统中常见的操作,核心目标是保证在高并发场景下数据的一致性。Java中实现库存扣减需要考虑原子性、并发控制和异常处理。

数据库乐观锁实现

通过版本号或时间戳实现乐观锁,适合并发量适中的场景:

// SQL示例
UPDATE product SET stock = stock - #{quantity}, version = version + 1 
WHERE id = #{productId} AND version = #{version} AND stock >= #{quantity}

需要在Service层进行重试逻辑:

@Transactional
public boolean deductStock(Long productId, int quantity) {
    int retryTimes = 3;
    while (retryTimes-- > 0) {
        Product product = productMapper.selectById(productId);
        if (product.getStock() < quantity) return false;

        int affected = productMapper.deductStock(productId, quantity, product.getVersion());
        if (affected > 0) return true;
    }
    return false;
}

Redis原子操作方案

利用Redis的原子特性实现高性能扣减:

// Lua脚本保证原子性
String luaScript = "if redis.call('exists', KEYS[1]) == 1 then " +
                   "local stock = tonumber(redis.call('get', KEYS[1])) " +
                   "if stock >= tonumber(ARGV[1]) then " +
                   "return redis.call('incrby', KEYS[1], -tonumber(ARGV[1])) " +
                   "end " +
                   "return -1 " +
                   "end " +
                   "return -2";

Long result = redisTemplate.execute(
    new DefaultRedisScript<>(luaScript, Long.class),
    Collections.singletonList("stock:" + productId),
    String.valueOf(quantity)
);

分布式锁解决方案

使用Redisson实现分布式锁:

public boolean deductStockWithLock(Long productId, int quantity) {
    RLock lock = redissonClient.getLock("product:" + productId);
    try {
        lock.lock();
        Product product = productMapper.selectById(productId);
        if (product.getStock() >= quantity) {
            product.setStock(product.getStock() - quantity);
            productMapper.updateById(product);
            return true;
        }
        return false;
    } finally {
        lock.unlock();
    }
}

消息队列异步处理

应对超高并发场景可采用异步扣减模式:

@KafkaListener(topics = "stock-deduction")
public void handleStockDeduction(StockMessage message) {
    try {
        deductStock(message.getProductId(), message.getQuantity());
    } catch (Exception e) {
        // 记录日志并加入重试队列
    }
}

异常处理与补偿机制

需要实现补偿逻辑处理失败场景:

@TransactionalEventListener(phase = AFTER_COMMIT)
public void onOrderCreated(OrderCreatedEvent event) {
    try {
        if(!stockService.deductStock(event.getProductId(), event.getQuantity())) {
            // 触发库存不足补偿流程
        }
    } catch (Exception e) {
        // 记录异常并触发补偿
    }
}

数据一致性验证

定期核对Redis与数据库库存:

java商品如何扣减

@Scheduled(cron = "0 0 3 * * ?")
public void stockSyncTask() {
    List<Product> products = productMapper.selectList(null);
    products.forEach(p -> {
        Integer redisStock = redisTemplate.opsForValue().get("stock:" + p.getId());
        if(redisStock != null && !redisStock.equals(p.getStock())) {
            // 触发库存校正
        }
    });
}

实际实现时应根据业务场景选择合适方案,超高并发建议采用Redis+Lua+异步消息的组合方案,普通场景可直接使用数据库乐观锁。所有方案都需配合完善的监控和报警机制。

标签: 商品java
分享给朋友:

相关文章

java如何使用

java如何使用

Java 基本使用方法 Java 是一种广泛使用的编程语言,适用于开发各种类型的应用程序。以下是 Java 的基本使用方法,包括环境配置、语法基础和常用操作。 安装 Java 开发环境 下载并安装…

java如何编程

java如何编程

Java编程基础 Java是一种面向对象的编程语言,广泛应用于企业级开发、移动应用(Android)等领域。以下是Java编程的核心步骤和示例。 环境搭建 安装JDK 从Oracle官网下载适合…

java如何读取文件

java如何读取文件

读取文件的常用方法 Java提供了多种读取文件的方式,适用于不同场景和需求。以下是几种常见的方法: 使用FileInputStream和BufferedInputStream读取二进制文件 try…

java如何学习

java如何学习

学习Java的有效方法 理解基础概念 Java是一门面向对象的编程语言,掌握基础概念如变量、数据类型、运算符、控制语句(if-else、循环)是必要的。面向对象的核心概念包括类、对象、继承、多态和封装…

java如何配置环境变量

java如何配置环境变量

配置Java环境变量的方法 下载并安装JDK 从Oracle官网或OpenJDK下载适合操作系统的JDK安装包。运行安装程序并按照提示完成安装,记下JDK的安装路径(例如C:\Program File…

如何成为java架构师

如何成为java架构师

掌握扎实的Java基础 深入理解Java核心知识,包括多线程、集合框架、JVM原理、设计模式等。熟悉Java生态中的常用框架如Spring、MyBatis、Hibernate等,并能分析其源码实现。…