laravel框架中使用mysql排他锁代码示例及场景介绍

2023-06-01 00:00:00 示例 框架 排他

mysql中的排他锁(EXclusive Lock),又称X锁、独占锁、写锁,针对行锁。

当有事务对数据加写锁后,其他事务不能再对锁定的数据加任何锁,又因为InnoDB对select语句默认不加锁,所以其他事务除了不能写操作外,照样是允许读的(尽管不允许加读锁)。


使用需求描述:

当mysql数据库的某条记录被多个php进程同时修改时,希望数据修改正确。


示例表 - user表

用来测试排他锁,实现通过加锁与解锁


创建用户表

CREATE TABLE users (
  id int(11) NOT NULL AUTO_INCREMENT,
  user_name varchar(255) NOT NULL DEFAULT '' comment '用户名称',
  level tinyint not null default 0 comment '用户类别,1注册用户,2铜牌用户,3高端用户',
  email varchar(255) NOT NULL DEFAULT '' comment 'email',
  created_at timestamp null default current_timestamp,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB ;

添加测试数据

insert into users(id,user_name)values (1,'管理员');
insert into users(id,user_name)values (2,'用户2');
insert into users(id,user_name)values (3,'用户3');


实现加锁

必须满足的条件,以示例表为例方便理解

1、表引擎必须innodb。
2、必须先开启事务。
3、必须使用 select XXX1 from XXX2 where XXX3 for update;
4、其中的 XXX3 必须有索引,建议使用主键最方便。
  尽量不要使用范围条件,可以使用 id=1, 或者 id in (1,3) 这样精确的定位记录的语句。
5、经实际测试,有普通索引的字段也可以。
  例如 select * from users where type=1 for update, 给这个 type 加索引的话也行。


加锁后对查改的影响

1、任何会话或连接,当查询到同一行记录时,
  如果使用 select XXX1 from XXX2 where XXX3 则没有影响,可以正常查询,无论是否在事务中。
 
2、任何会话或连接,当查询到同一行记录时,
  如果使用select XXX1 from XXX2 where XXX3 for update,则被强行阻塞等待,无论是否在事务
           中。
3、任何会话或连接,当修改同一行记录时,
  如果使用 update XXX2 set XXX1 where XXX3 ,则被强行阻塞等待,无论是否在事务中。
 
4、任何会话或连接,当修改包括那行的更多记录时,
  如果使用 update XXX2 set XXX1 where XXX4 ,这个 XXX4 包含了 XXX3 的记录和其他记录,则       整个 update 依然被强行阻塞等待,无论是否在事务中。


实现解锁

只要满足以下任意一个条件均可

1、在刚才加锁的 mysql 会话中,commit 事务
2、在刚才加锁的 mysql 会话中,rollback 事务
3、刚才加锁 的 mysql 连接断开。实际是 mysql 服务会自己 rollback 事务


laravel实现排他锁的示例代码:

//开启事务
DB::beginTransaction(); 

//排他锁
$user = User::lockForUpdate()->find(1);

//这里对用户做一些读写操作。
DB::commit();
//或 DB::rollBack();


mysql排他锁的使用总结及注意事项

1、如果修改例如用户账户余额之类的敏感数据,应该总是在使用update这个语句之前,
  最先开事务,再使用 select …. where id = 1 for update, 
  然后再使用 update 。。。where id =1 , 再提交事务。
 
2、这种总是先 select … for update的方式,实际是让所有sql修改语句串行执行,而不是并发执行,数据修改时当然肯定不会错,
  可是也阻塞了php语句的执行,并发访问高速度也慢。当然悲观锁本身就是这样的。
 
3、如果想保证数据修改正确,除了这种方法,还有一种方法也可以实现,就是不在数据库查询时加锁,
  而是让所有的修改sql的程序语言本身不能并发执行,也可以达到效果。
 
4、如果想保证数据修改正确,还有一种方法,使用乐观锁的实现。

5、如果想保证数据修改正确,还有一种方法,数据放redis,用redis锁。

相关文章