MySQL悲观锁和乐观锁(并发控制)

袁志蒙 次浏览

摘要:做商城开发时经常会遇到高并发的问题,除了使用Redis队列等技术外,也可以使用Mysql数据库的“锁”机制。悲观锁:一般使用 select ...for update 对所选择的数据进行加锁处理,例如select * from yzm_order...

做商城开发时经常会遇到高并发的问题,除了使用Redis队列等技术外,也可以使用Mysql数据库的“锁”机制。

一、悲观锁

1、当事务在操作数据时把这部分数据进行锁定,直到操作完毕后再解锁,其他事务操作才可操作该部分数据。这将防止其他进程读取或修改表中的数据。


2、实现

一般使用 select ...for update 对所选择的数据进行加锁处理,例如 SELECT * FROM `yzm_order` WHERE  `order_no` = 'S201909277894' LIMIT 1  FOR UPDATE ,这样就通过开启排他锁的方式实现了悲观锁。此时在 yzm_order 表中,order_no 为 S201909277894 的那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。

二、乐观锁

1、如果有人在你之前更新了,你的更新应当是被拒绝的,可以让用户重新操作。


2、实现:

大多数基于数据版本(Version)记录机制实现,

具体可通过给表加一个版本号或时间戳字段实现,当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断当前版本信息与第一次取出来的版本值大小,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据,拒绝更新,让用户重新操作。


在数据库中,悲观锁的流程如下:

在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。

如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。 具体响应方式由开发者根据实际需要决定。

如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。

其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。


测试一把:

会话1:

SET autocommit=0
SELECT * FROM yzm_order WHERE `order_no` = 'S201909277894' FOR UPDATE
COMMIT

新会话2:

在会话1未commit之前,执行以下SQL,会发生堵塞

UPDATE `yzm_order` SET `status` = 2 WHERE id = 1;

当会话1长时间未提交事务时,会发生以下报错信息

[Err] 1205 - Lock wait timeout exceeded; try restarting transaction


再次测试:

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

SELECT * FROM yzm_order WHERE id='3' FOR UPDATE;


例2: (明确指定主键,且无此记录,无lock) 

SELECT * FROM yzm_order WHERE id='-1' FOR UPDATE;


例3: (无主键,table lock) 

SELECT * FROM yzm_order WHERE status='1' FOR UPDATE;


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

SELECT * FROM yzm_order WHERE id<>'3' FOR UPDATE;


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

SELECT * FROM yzm_order WHERE id LIKE '3' FOR UPDATE;


总结:InnoDB默认行级锁。行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住。


随机新闻

表情

共3条评论