MySQL中的select...for update,也称为MySQL悲观锁,其作用是:
为了在查询数据时,避免其他用户对所查询的数据进行修改或删除等操作,造成表中数据的不一致性
例如:我们做抢购系统、限量兑换系统等,有限的数据被多人同时操作的情况下,容易造成相同数据被重复使用,严重影响结果的准确性,此时对数据上锁,即显得非常有必要
下面通过举例,简单介绍MySQL悲观锁的使用方法和效果
首先创建一张测试表:
CREATE TABLE test(
id int primary key auto_increment,
cid int NOT NULL,
name varchar(50) NOT NULL
);
添加测试数据:
INSERT INTO test(cid,name) VALUES(3,'北京'),(6,'上海'),(9,'深圳');
结果如下图所示:
下面再打开一个MySQL客户端,对锁进行测试
正常情况下, 我们使用select语句可以在不同客户端中,以相同条件随意查询,得到相同结果
比如上表中,查询id为2,cid为6的值,sql语句为:
SELECT * FROM test WHERE id=2 AND cid=6;
假设这是在一个抢票系统中,这时有多人同时查到这条数据,就可能重复生成多个订单,导致数据错误。在这里使用悲观锁,可以很好的避免出现这种问题
使用悲观锁,首先要开启MySQL事务,禁止MySQL自动提交:
SET autocommit = 0;
然后开始事务:
begin;
查询对应数据:
select * from test where id = 2 and cid = 6 for update;
这时若两个乃至n个客户端同时操作,MySQL也会分先后。在得到结果的客户端,没有结束此次事务之前,后面的都需要排队等待,如下所示:
直到先查询到的执行“commit”或“rollback”结束本次事务,或者关闭本次连接结束事务,后面排队的再依次执行,如下图所示,右边的客户端查询等待了8秒:
若只用MySQL悲观锁查询数据,和普通查询没什么区别,反而影响其它客户端的查询速度。而在查询到结果后,对数据进行修改、删除等操作,后面排队的再以相同条件进行查询时,即可避免查到重复结果,如图:
如上图所示,使用select...for update锁定一条数据后,对其进行修改,另一个排队的客户端,再以相同条件查询,返回空结果。明确展示了悲观锁的效果
注意事项:
1、MySQL事务在MyISAM存储引擎下无效,故而悲观锁也不能使用。当发现select...for update无效时,请检查当前表的存储引擎,若为MyISAM,建议更换为InnoDB
2、事务的“commit”和“rollback”都可以对锁进行释放。在实际应用中要谨慎操作,考虑好各个因素后再上锁,并在合理位置对锁进行释放,避免陷入死锁