数据库锁的设计

近期开发与钱相关的项目,在高并发场景下对数据的准确行有很高的要求,用到了for update,故总结一波以便日后留恋。

for update的使用场景

如果遇到存在高并发并且对于数据的准确性很有要求的场景,例如支付、对账等,是需要了解和使用for update的。

比如涉及到金钱、库存等。一般这些操作都是很长一串并且是开启事务的。如果库存刚开始读的时候是1,而立马另一个进程进行了update将库存更新为0了,而事务还没有结束,会将错的数据一直执行下去,就会有问题。所以需要for upate 进行数据加锁防止高并发时候数据出错。

for update必须在事务块(BEGIN/COMMIT)中才能生效。在进行事务操作时,通过"for update"语句,MySQL与ORCALE 会对查询结果集中每行数据都添加排他锁,其他线程对该记录的更新与删除操作都会阻塞。排他锁包含行锁、表锁。

一、数据一致性

假设有A、B两个用户同时各购买一件 id=1 的商品,用户A获取到的库存量为 1000,用户B获取到的库存量也为 1000,用户A完成购买后修改该商品的库存量为 999,用户B完成购买后修改该商品的库存量为 999,此时库存量数据产生了不一致。

有两种解决方案:

悲观锁方案

:每次获取商品时,对该商品加排他锁。也就是在用户A获取获取 id=1 的商品信息时对该行记录加锁,期间其他用户阻塞等待访问该记录。悲观锁适合写入频繁的场景。

begin;

select * from goods where id = 1 for update;

update goods set stock = stock - 1 where id = 1;

commit;

乐观锁方案

:每次获取商品时,不对该商品加锁。在更新数据的时候需要比较程序中的库存量与数据库中的库存量是否相等,如果相等则进行更新,反之程序重新获取库存量,再次进行比较,直到两个库存量的数值相等才进行数据更新。乐观锁适合读取频繁的场景。

#不加锁获取 id=1 的商品对象

select * from goods where id = 1

begin;

#更新 stock 值,这里需要注意 where 条件 "stock = cur_stock",只有程序中获取到的库存量与数据库中的库存量相等才执行更新

update goods set stock = stock - 1 where id = 1 and stock = cur_stock;

commit;

如果我们需要设计一个商城系统,该选择以上的哪种方案呢?

查询商品的频率比下单支付的频次高,基于以上我可能会优先考虑第二种方案(当然还有其他的方案,这里只考虑以上两种方案)。

InnoDB默认是行级别的锁,当有明确指定的主键时候,是行级锁。否则是表级别。

#for update的注意点

for update 必须开启事务,在begin与commit之间才生效。

总结

一般在支付、商品交易的库存变更等业务场景采用悲观锁方案;

在广告、电商浏览页面、投票等场景采用乐观锁方案

相关推荐
hanyi_qwe4 分钟前
MySQL事务基础
数据库·mysql
l1t8 分钟前
三种用SQL解决Advent of Code 2022第8题 树顶木屋 的比较和分析
数据库·sql·oracle·duckdb·advent of code
如果未来,10 分钟前
Oracle的Redo log和Undo log的区别
数据库·oracle
koping_wu15 分钟前
【方案设计】Mysql相关场景
数据库·mysql
杨云龙UP19 分钟前
SQL Server小技巧:用 SSMS 重置登录密码,不影响正在运行的系统
运维·服务器·数据库·sql·sqlserver
洁洁!21 分钟前
openEuler多样性算力支持实践:容器化多架构应用部署与调度
服务器·数据库·科技·语言模型·数据分析
白露与泡影27 分钟前
MySQL整体设计与存储引擎深度剖析:从架构哲学到引擎选型(了解)
数据库·mysql·架构
ManageEngine卓豪34 分钟前
企业网站监控与性能优化指南
数据库·microsoft·性能优化
小小哭包43 分钟前
Spring+MyBatis实现数据库读写分离方案
数据库·spring·mybatis
Shingmc31 小时前
MySQL表的操作
数据库·mysql