SELECT FOR UPDATE(转)

MySQL  使用SELECT ... FOR UPDATE 做事务写入前的确认

以MySQL 的InnoDB 为例,预设的Tansaction isolation level 为REPEATABLE READ,在SELECT 的读取锁定主要分为两种方式:

SELECT ... LOCK IN SHARE MODE SELECT ... FOR UPDATE

这两种方式在事务(Transaction) 进行当中SELECT 到同一个数据表时,都必须等待其它事务数据被提交(Commit)后才会执行。而主要的不同在于LOCK IN SHARE MODE 在有一方事务要Update 同一个表单时很容易造成死锁 。

简单的说,如果SELECT 后面若要UPDATE 同一个表单,最好使用SELECT ... UPDATE。

举个例子: 假设商品表单products 内有一个存放商品数量的quantity ,在订单成立之前必须先确定quantity 商品数量是否足够(quantity>0) ,然后才把数量更新为1。

不安全的做法:

SELECT quantity FROM products WHERE id=3; UPDATE products SET quantity = 1 WHERE id=3;

为什么不安全呢?

少量的状况下或许不会有问题,但是大量的数据存取「铁定」会出问题。

如果我们需要在quantity>0 的情况下才能扣库存,假设程序在第一行SELECT 读到的quantity 是2 ,看起来数字没有错,但是当MySQL 正准备要UPDATE 的时候,可能已经有人把库存扣成0 了,但是程序却浑然不知,将错就错的UPDATE 下去了。

因此必须透过的事务机制来确保读取及提交的数据都是正确的。

于是我们在MySQL 就可以这样测试: (注1)

SET AUTOCOMMIT=0; BEGIN WORK; SELECT quantity FROM products WHERE id=3 FOR UPDATE; ===========================================

此时products 数据中id=3 的数据被锁住(注3),其它事务必须等待此次事务 提交后才能执行

SELECT * FROM products WHERE id=3 FOR UPDATE (注2) 如此可以确保quantity 在别的事务读到的数字是正确的。 ===========================================

UPDATE products SET quantity = ‘1‘ WHERE id=3 ; COMMIT WORK;

===========================================

提交(Commit)写入数据库,products 解锁。

注1: BEGIN/COMMIT 为事务的起始及结束点,可使用二个以上的MySQL Command 视窗来交互观察锁定的状况。

注2: 在事务进行当中,只有SELECT ... FOR UPDATE 或LOCK IN SHARE MODE 同一笔数据时会等待其它事务结束后才执行,一般SELECT ... 则不受此影响。

注3: 由于InnoDB 预设为Row-level Lock,数据列的锁定可参考这篇。

注4: InnoDB 表单尽量不要使用LOCK TABLES 指令,若情非得已要使用,请先看官方对于InnoDB 使用LOCK TABLES 的说明,以免造成系统经常发生死锁。

MySQL SELECT ... FOR UPDATE 的Row Lock 与Table Lock

上面介绍过SELECT ... FOR UPDATE 的用法,不过锁定(Lock)的数据是判别就得要注意一下了。由于InnoDB 预设是Row-Level Lock,所以只有「明确」的指定主键,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)。

举个例子:

假设有个表单products ,里面有id 跟name 二个栏位,id 是主键。

例1: (明确指定主键,并且有此数据,row lock)

SELECT * FROM products WHERE id=‘3‘ FOR UPDATE;

例2: (明确指定主键,若查无此数据,无lock)

SELECT * FROM products WHERE id=‘-1‘ FOR UPDATE;

例2: (无主键,table lock)

SELECT * FROM products WHERE name=‘Mouse‘ FOR UPDATE;

例3: (主键不明确,table lock)

SELECT * FROM products WHERE id<>‘3‘ FOR UPDATE;

例4: (主键不明确,table lock)

SELECT * FROM products WHERE id LIKE ‘3‘ FOR UPDATE;

注1: FOR UPDATE 仅适用于InnoDB,且必须在事务区块(BEGIN/COMMIT)中才能生效。

注2: 要测试锁定的状况,可以利用MySQL 的Command Mode ,开二个视窗来做测试。

时间: 2024-12-31 05:13:00

SELECT FOR UPDATE(转)的相关文章

select from update row的实现

DTCC大会上,阿里江疑的演讲中提到一个:select from update hot row; 不明白如何在Oracle中实现的,他的意思是在一条SQL中实现update和select这条update的字段信息. 经dbsnake指点,了解到这是模仿了Oracle的returning into子句,可以将使用的DML语句影响的行记录的指定列的值select出来. 官方文档中有示例: http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/tu

Mysql查询语句使用select.. for update导致的数据库死锁分析

近期有一个业务需求,多台机器需要同时从Mysql一个表里查询数据并做后续业务逻辑,为了防止多台机器同时拿到一样的数据,每台机器需要在获取时锁住获取数据的数据段,保证多台机器不拿到相同的数据. 我们Mysql的存储引擎是innodb,支持行锁.解决同时拿数据的方法有很多,为了更加简单,不增加其他表和服务器的情况下,我们考虑采用select... for update的方式,这样X锁锁住查询的数据段,表里其他数据没有锁,其他业务逻辑还是可以操作. 这样一台服务器比如select .. for upd

MySQL中select * for update锁表的范围

MySQL中select * for update锁表的问题 由于InnoDB预设是Row-Level Lock,所以只有「明确」的指定主键,MySQL才会执行Row lock (只锁住被选取的资料例) ,否则MySQL将会执行Table Lock (将整个资料表单给锁住). 举个例子: 假设有个表单products ,里面有id跟name二个栏位,id是主键. 例1: (明确指定主键,并且有此笔资料,row lock) SELECT * FROM products WHERE id='3' F

sql server中高并发情况下 同时执行select和update语句死锁问题 (一)

 最近在项目上线使用过程中使用SqlServer的时候发现在高并发情况下,频繁更新和频繁查询引发死锁.通常我们知道如果两个事务同时对一个表进行插入或修改数据,会发生在请求对表的X锁时,已经被对方持有了.由于得不到锁,后面的Commit无法执行,这样双方开始死锁.但是select语句和update语句同时执行,怎么会发生死锁呢?看完下面的分析,你会明白的- 首先看到代码中使用的查询的方法Select <span style="font-size:18px;"> /// &

【转载】MySQL事务以及SELECT ... FOR UPDATE的使用

MySQL中的事务,默认是自动提交的,即autocommit = 1: 但是这样的话,在某些情形中就会出现问题:比如: 如果你想一次性插入了1000条数据,mysql会commit1000次的, 如果我们把autocommit关闭掉[autocommit = 0],通过程序来控制,只要一次commit就可以了,这样也才能更好的体现事务的特点! 对于需要操作数值,比如金额,个数等等! 记住一个原则:一锁二判三更新 如果SELECT 后面若要UPDATE 同一个表单,最好使用SELECT ... F

Oracle编辑数据时提示:这些查询结果不可更新,请使用ROWI或者SELECT……FOR UPDATE获得可更新结果

我们在对Oracle数据库进行操作时,有时会在查询完结果后想要对其中的某些数据进行操作,当我们点击编辑(一个锁标志)是,会提示我们上述问题中的错误:这些查询结果不可更新,请使用ROWI或者SELECT--FOR UPDATE获得可更新结果.按照错误提示的信息我们可以采用两种解决办法: 解决办法1:在查询语句后面写上for update,如:select * from 表名 for update: 解决办法2:在查询的列中使用rowid属性,如:select rowID, 表名.* from 表名

select for update行锁

select for update行锁 2008-05-26 15:15:37 分类: Oracle Select-For Update语句的语法与select语句相同,只是在select语句的后面加FOR UPDATE [NOWAIT]子句. 该语句用来锁定特定的行(如果有where子句,就是满足where条件的那些行).当这些行被锁定后,其他会话可以选择这些行,但不能更改或删除这些行,直到该语句的事务被commit语句或rollback语句结束为止. 如图20.51所示,左上角的会话用Sel

MySQL 使用SELECT ... FOR UPDATE 做事务写入前的确认(转)

Select…For Update语句的语法与select语句相同,只是在select语句的后面加FOR UPDATE [NOWAIT]子句. 该语句用来锁定特定的行(如果有where子句,就是满足where条件的那些行).当这些行被锁定后,其他会话可以选择这些行,但不能更改或删除这些行,直到该语句的事务被commit语句或rollback语句结束为止. MySQL  使用SELECT ... FOR UPDATE 做事务写入前的确认 以MySQL 的InnoDB 为例,预设的Tansactio

mysql SELECT FOR UPDATE语句使用示例

以MySQL 的InnoDB 为例,预设的Tansaction isolation level 为REPEATABLE READ,在SELECT 的读取锁定主要分为两种方式:SELECT ... LOCK IN SHARE MODE SELECT ... FOR UPDATE这两种方式在事务(Transaction) 进行当中SELECT 到同一个数据表时,都必须等待其它事务数据被提交(Commit)后才会执行.而主要的不同在于LOCK IN SHARE MODE 在有一方事务要Update 同