前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >For update介绍

For update介绍

原创
作者头像
HLee
修改2021-08-19 10:20:34
修改2021-08-19 10:20:34
1.6K00
代码可运行
举报
文章被收录于专栏:房东的猫房东的猫
运行总次数:0
代码可运行

简介

for update的作用是在查询的时候为行加上排它锁。

它的典型使用场景是高并发并且对于数据的准确性有很高要求,比如金钱、库存等,一般这种操作都是很长一串并且开启事务的,假如现在要对库存进行操作,在刚开始读的时候是1,然后马上另外一个进程将库存更新为0了,但事务还没结束,会一直用1进行后续的逻辑,就会有问题,所以需要用for upate加锁防止出错,记住一个原则:一锁二判三更新

for update仅适用于InnoDB,并且必须开启事务,在begin与commit之间才生效。InnoDB 默认是行级锁,当有明确指定的主键/索引时候,是行级锁,否则是表级锁。

代码语言:javascript
代码运行次数:0
运行
复制
select * from t for update 会等待行锁释放之后,返回查询结果。
select * from t for update nowait 不等待行锁释放,提示锁冲突,不返回结果
select * from t for update wait 5 等待5秒,若行锁仍未释放,则提示锁冲突,不返回结果
select * from t for update skip locked 查询返回查询结果,但忽略有行锁的记录

场景分析

假设有一张商品表 goods,它包含 id,商品名称,库存量三个字段,表结构如下:

代码语言:javascript
代码运行次数:0
运行
复制
CREATE TABLE `goods` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  `stock` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_name` (`name`) USING HASH
) ENGINE=InnoDB

INSERT INTO `goods` VALUES ('1', 'prod11', '1000');
INSERT INTO `goods` VALUES ('2', 'prod12', '1000');
INSERT INTO `goods` VALUES ('3', 'prod13', '1000');
INSERT INTO `goods` VALUES ('4', 'prod14', '1000');
INSERT INTO `goods` VALUES ('5', 'prod15', '1000');
INSERT INTO `goods` VALUES ('6', 'prod16', '1000');
INSERT INTO `goods` VALUES ('7', 'prod17', '1000');
INSERT INTO `goods` VALUES ('8', 'prod18', '1000');
INSERT INTO `goods` VALUES ('9', 'prod19', '1000');

数据一致性

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

有两种解决方案:

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

代码语言:javascript
代码运行次数:0
运行
复制
begin;
select * from goods where id = 1 for update;
update goods set stock = stock - 1 where id = 1;
commit;

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

代码语言:javascript
代码运行次数:0
运行
复制
#不加锁获取 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;

行锁与表锁

  • 只根据主键进行查询,并且查询到数据,主键字段产生行锁
代码语言:javascript
代码运行次数:0
运行
复制
begin;
select * from goods where id = 1 for update;
commit;
  • 只根据主键进行查询,没有查询到数据,不产生锁
代码语言:javascript
代码运行次数:0
运行
复制
begin;
select * from goods where id = 1 for update;
commit;
  • 根据主键、非主键含索引(name)进行查询,并且查询到数据,主键字段产生行锁,name字段产生行锁
代码语言:javascript
代码运行次数:0
运行
复制
begin;
select * from goods where id = 1 and name='prod11' for update;
commit;
  • 根据主键、非主键含索引(name)进行查询,没有查询到数据,不产生锁
代码语言:javascript
代码运行次数:0
运行
复制
begin;
select * from goods where id = 1 and name='prod12' for update;
commit;
  • 根据主键、非主键不含索引(name)进行查询,并且查询到数据,如果其他线程按主键字段进行再次查询,则主键字段产生行锁,如果其他线程按非主键不含索引字段进行查询,则非主键不含索引字段产生表锁,如果其他线程按非主键含索引字段进行查询,则非主键含索引字段产生行锁,如果索引值是枚举类型,mysql也会进行表锁,这段话有点拗口,大家仔细理解一下
代码语言:javascript
代码运行次数:0
运行
复制
begin;
select * from goods where id = 1 and name='prod11' for update;
commit;
  • 根据主键、非主键不含索引(name)进行查询,没有查询到数据,不产生锁
代码语言:javascript
代码运行次数:0
运行
复制
begin;
select * from goods where id = 1 and name='prod12' for update;
commit;
  • 根据非主键含索引(name)进行查询,并且查询到数据,name字段产生行锁
代码语言:javascript
代码运行次数:0
运行
复制
begin;
select * from goods where name='prod11' for update;
commit;
  • 根据非主键含索引(name)进行查询,没有查询到数据,不产生锁
代码语言:javascript
代码运行次数:0
运行
复制
begin;
select * from goods where name='prod11' for update;
commit;
  • 根据非主键不含索引(name)进行查询,并且查询到数据,name字段产生表锁
代码语言:javascript
代码运行次数:0
运行
复制
begin;
select * from goods where name='prod11' for update;
commit;
  • 根据非主键不含索引(name)进行查询,没有查询到数据,name字段产生表锁
代码语言:javascript
代码运行次数:0
运行
复制
begin;
select * from goods where name='prod11' for update;
commit;
  • 只根据主键进行查询,查询条件为不等于或者LIKE,并且查询到数据,主键字段产生表锁
代码语言:javascript
代码运行次数:0
运行
复制
begin;
select * from goods where id <> 1 for update;
commit;

begin;
select * from goods where id like '1' for update;
commit;

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 场景分析
    • 数据一致性
    • 行锁与表锁
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档