方案说明
- 锁的基本概念:
- 行级锁:在PostgreSQL中,
SELECT... FOR UPDATE
语句可以获取行级排他锁。例如,在更新商品库存时,我们可以使用这种锁来确保在更新库存时,其他事务不会同时修改同一行库存数据。
- 表级锁:通过
LOCK TABLE
语句获取表级锁。表级锁粒度大,会阻塞其他事务对该表的读写操作,所以应谨慎使用,但在某些极端高并发瞬间冲击场景下,可作为一种兜底策略。
- 正常并发操作策略:
- 商品库存更新:
- 首先使用
SELECT... FOR UPDATE
语句锁定要更新的商品库存行。例如:
BEGIN;
SELECT stock FROM products WHERE product_id = 1 FOR UPDATE;
-- 假设获取到的库存为stock变量,在此处进行库存更新逻辑,如stock = stock - 1
UPDATE products SET stock = stock - 1 WHERE product_id = 1;
COMMIT;
- 订单创建:
- 订单创建时,可能涉及到关联表操作,如插入订单主表和订单详情表。可以先获取订单主表的行级锁(如果订单表以订单ID为主键,可通过
SELECT... FOR UPDATE
锁定订单ID对应的行,但通常订单创建是插入新行,所以这里主要关注事务隔离性以避免并发插入问题)。对于订单详情表,也类似处理。例如:
BEGIN;
-- 插入订单主表
INSERT INTO orders (order_id, user_id, order_date) VALUES (1, 100, '2024 - 01 - 01') RETURNING order_id;
-- 获取插入的订单ID,假设为order_id变量
-- 插入订单详情表
INSERT INTO order_details (order_id, product_id, quantity) VALUES (order_id, 1, 2);
COMMIT;
- 用户积分计算:
- 用户积分计算可能依赖订单金额等信息。在计算积分前,先锁定用户表中对应的用户行。例如:
BEGIN;
SELECT points FROM users WHERE user_id = 100 FOR UPDATE;
-- 假设获取到的积分为points变量,根据订单金额等逻辑计算新积分,如points = points + 10
UPDATE users SET points = points + 10 WHERE user_id = 100;
COMMIT;
- 高并发瞬间冲击策略:
- 锁升级:在高并发瞬间,如果行级锁竞争过于激烈,可以考虑暂时升级为表级锁。例如,当检测到商品库存更新的行级锁等待队列长度超过一定阈值(如100个等待事务)时,对整个
products
表使用LOCK TABLE products IN EXCLUSIVE MODE
获取表级排他锁。这样所有对products
表的读写操作都将等待,直到持有锁的事务完成。
-- 伪代码示例,实际需要通过监控和程序逻辑触发
-- 假设检测到高并发,获取表级锁
BEGIN;
LOCK TABLE products IN EXCLUSIVE MODE;
-- 进行商品库存更新操作
SELECT stock FROM products WHERE product_id = 1 FOR UPDATE;
UPDATE products SET stock = stock - 1 WHERE product_id = 1;
COMMIT;
- 锁降级:在高并发冲击过去后,需要将表级锁降级为行级锁,以提高系统并发处理能力。可以通过监控锁的持有时间和等待队列情况,当等待队列长度小于一定阈值(如10个等待事务)且持有表级锁一段时间后(如5秒),释放表级锁,恢复使用行级锁进行操作。
- 数据一致性保证:
- 通过合理使用事务隔离级别(如
REPEATABLE READ
或SERIALIZABLE
),确保在事务执行过程中,数据的读取和修改是一致的。例如,将事务隔离级别设置为REPEATABLE READ
:
BEGIN ISOLATION LEVEL REPEATABLE READ;
-- 执行商品库存更新、订单创建、用户积分计算等操作
COMMIT;
- 在每个事务中,严格按照业务逻辑顺序进行操作,避免出现死锁。例如,在订单创建和商品库存更新的事务中,确保所有事务都以相同的顺序访问资源(如先获取商品库存锁,再进行订单创建操作)。
- 性能优化:
- 合理使用索引,例如在
products
表的product_id
字段、orders
表的order_id
字段、users
表的user_id
字段上创建索引,这样在获取锁和执行查询操作时能够提高效率。
- 尽量缩短事务的执行时间,避免长时间持有锁。例如,在获取锁后尽快完成相关的业务逻辑计算和数据更新操作,然后提交事务。
代码示例总结
- 商品库存更新:
BEGIN;
SELECT stock FROM products WHERE product_id = 1 FOR UPDATE;
UPDATE products SET stock = stock - 1 WHERE product_id = 1;
COMMIT;
- 订单创建:
BEGIN;
INSERT INTO orders (order_id, user_id, order_date) VALUES (1, 100, '2024 - 01 - 01') RETURNING order_id;
-- 获取插入的订单ID,假设为order_id变量
INSERT INTO order_details (order_id, product_id, quantity) VALUES (order_id, 1, 2);
COMMIT;
- 用户积分计算:
BEGIN;
SELECT points FROM users WHERE user_id = 100 FOR UPDATE;
UPDATE users SET points = points + 10 WHERE user_id = 100;
COMMIT;
- 锁升级(伪代码触发):
BEGIN;
LOCK TABLE products IN EXCLUSIVE MODE;
SELECT stock FROM products WHERE product_id = 1 FOR UPDATE;
UPDATE products SET stock = stock - 1 WHERE product_id = 1;
COMMIT;
- 设置事务隔离级别:
BEGIN ISOLATION LEVEL REPEATABLE READ;
-- 执行相关业务操作
COMMIT;