postgresql库存扣减如何保证不超卖

来源:程序开发作者:北京SEO公司头衔:草根站长
导读:本期聚焦于小伙伴创作的《postgresql库存扣减如何保证不超卖》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《postgresql库存扣减如何保证不超卖》有用,将其分享出去将是对创作者最好的鼓励。

在电商、零售等业务系统中,库存扣减是高频且核心的操作,当多个用户同时下单购买同一商品时,如果处理不当就可能出现库存被重复扣减的超卖问题。postgresql作为成熟的关系型数据库,提供了多种事务和并发控制机制,可以有效避免这类问题的发生。

postgresql库存扣减如何保证不超卖

超卖问题的产生原因

超卖问题的本质是并发场景下的数据竞争问题。假设当前商品库存为1,两个用户同时发起下单请求,两个请求同时查询到库存为1,都认为可以扣减,随后都执行扣减操作,最终库存会变成-1,出现超卖。这个问题的核心是没有保证库存查询和扣减操作的原子性。

基于事务和行级锁的解决方案

postgresql的行级锁可以在事务中锁定目标行,防止其他事务同时修改该行数据,结合事务的原子性可以保证库存扣减的原子性。

实现步骤

  • 开启事务,设置合适的事务隔离级别,一般使用读已提交即可
  • 对目标库存行加行级排他锁,查询当前库存数量
  • 判断库存是否充足,充足则执行扣减操作,不足则回滚事务
  • 提交事务,释放锁

代码示例

以下是使用postgresql行级锁实现库存扣减的示例代码,假设库存表结构为:

-- 库存表结构
CREATE TABLE product_stock (
    product_id INT PRIMARY KEY,
    stock_num INT NOT NULL CHECK (stock_num >= 0)
);

扣减库存的存储过程如下:

CREATE OR REPLACE PROCEDURE deduct_stock(
    p_product_id INT,
    p_deduct_num INT,
    OUT p_result INT
)
LANGUAGE plpgsql
AS $$
DECLARE
    v_current_stock INT;
BEGIN
    -- 开启事务,在存储过程中自动包含在事务内
    -- 对目标行加行级排他锁,FOR UPDATE会锁定查询到的行
    SELECT stock_num INTO v_current_stock FROM product_stock WHERE product_id = p_product_id FOR UPDATE;
    
    -- 判断库存是否充足
    IF v_current_stock >= p_deduct_num THEN
        UPDATE product_stock SET stock_num = stock_num - p_deduct_num WHERE product_id = p_product_id;
        p_result := 1; -- 扣减成功
    ELSE
        p_result := 0; -- 库存不足
    END IF;
    
    -- 正常执行结束会自动提交事务,若出现异常会自动回滚
EXCEPTION
    WHEN OTHERS THEN
        p_result := -1; -- 执行异常
        ROLLBACK;
END;
$$;

调用存储过程扣减库存的代码如下:

-- 调用存储过程,扣减商品ID为1的库存2件
CALL deduct_stock(1, 2, NULL);
-- 查看返回结果
DO $$
DECLARE
    res INT;
BEGIN
    CALL deduct_stock(1, 2, res);
    RAISE NOTICE '扣减结果:%', res;
END;
$$;

基于乐观锁的解决方案

乐观锁假设并发冲突概率较低,不会主动加锁,而是通过版本号或者库存数值比对的方式来判断是否有其他事务修改过数据。

实现原理

在库存表中增加一个版本号字段,每次更新库存时同时更新版本号,并且更新条件中带上之前的版本号,如果更新影响的行数为0,说明版本号已经被其他事务修改,当前扣减失败。

代码示例

首先修改库存表结构,增加版本号字段:

ALTER TABLE product_stock ADD COLUMN version INT NOT NULL DEFAULT 0;

乐观锁扣减库存的SQL如下:

-- 先查询当前库存和版本号
SELECT stock_num, version INTO @current_stock, @current_version FROM product_stock WHERE product_id = 1;

-- 判断库存是否充足
IF @current_stock >= 2 THEN
    -- 执行更新,条件中带上版本号,同时版本号加1
    UPDATE product_stock 
    SET stock_num = stock_num - 2, version = version + 1 
    WHERE product_id = 1 AND version = @current_version;
    
    -- 判断更新是否成功
    IF ROW_COUNT() > 0 THEN
        RAISE NOTICE '扣减成功';
    ELSE
        RAISE NOTICE '扣减失败,并发冲突';
    END IF;
ELSE
    RAISE NOTICE '库存不足';
END IF;

两种方案的对比

方案类型适用场景优点缺点
行级锁方案并发量较高,冲突概率大的场景实现简单,可靠性高,不会出现超卖高并发下锁竞争会导致性能下降,长事务会长时间持有锁
乐观锁方案并发量较低,冲突概率小的场景不需要加锁,性能较好,不会阻塞其他事务冲突概率高时会导致大量更新失败,需要重试机制

注意事项

  • 无论使用哪种方案,都需要在库存表的stock_num字段上添加非负约束,防止扣减后出现负数
  • 行级锁方案要注意控制事务的执行时间,避免长事务占用锁资源
  • 乐观锁方案如果更新失败,需要设计合理的重试机制,避免用户下单失败
  • 如果业务中存在分库分表的情况,需要结合分布式锁来保证跨节点的库存扣减一致性
库存扣减的核心是保证查询和扣减操作的原子性,postgresql的事务和锁机制已经提供了完善的底层支持,开发者只需要根据业务场景选择合适的方案即可有效避免超卖问题。

postgresql事务并发控制库存扣减修改时间:2026-07-03 23:54:35

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。