mysql乐观锁、悲观锁、共享锁与排它锁

乐观锁(乐观并发控制)和悲观锁(悲观并发控制)是指数据库在对待并发上控制的两种思想,共享锁和排它锁是具体的锁的实现,且都属于悲观锁。乐观锁没有加锁

乐观锁

获取数据的时候不用获取锁,直到需要更新数据的时候才去检查获取的记录是否已被其他事务更新,如果更新了则返回错误抛异常。注意的是,乐观锁中没有锁机制,通常的实现方式是用版本号或时间戳实现,比如,事务get了一条记录,同时获取到了该条记录的version,如果需要做更新操作,则对version进行+1操作,插入数据库时就会检查数据库当前的version+1是否与插入的version一致,如果一致则插入,否则抛异常,另只有更新操作才对version做+1操作,获取数据不会做

悲观锁

获取数据之前先获取锁,再进行操作

表锁

LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
UNLOCK TABLES  【释放表】

表共享锁|读锁

所有的客户端只能读这个表不能写这个表

a客户端对test表设置共享锁,b客户端可以读test表但无法写操作,直到a客户端解锁,b客户端才能完成写操作

表排他锁|写锁

所有当前锁定客户端可以操作这个表,其他客户端只能阻塞

a客户端对test表设置排它锁,除a客户端外的所有客户端都无法进行读写操作直至a客户端解锁

注意:在锁表的过程中锁表客户端只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!即某一客户端对test表设置锁,在未解锁前不能操作其他未设置锁的表。其他客户端可以操作未锁定的表

行锁(注:mysiam不支持行锁)

共享锁
set autocommit = 0;
select * from test where id = 1 lock in share mode;
update test set username = 'jack' where id = 1;
commit;

排它锁
set autocommit = 0;
select * from test where id = 1 for update;
update test set username = 'lxj' where id = 2;
commit;

行共享锁

a客户端对行加共享锁后,客户端b仍可查询该行记录并对其加共享锁,若多个客户端对同一行加共享锁会出现死锁现象。

行排它锁

a客户端对某行记录加了排他锁,其他客户端仍可以查询该记录,但是不能对该记录加锁,会等待获得锁。当a客户端对锁定的记录进行修改操作后,释放排它锁,其他客户端才能获的锁。

InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

注意共享锁与排它锁都是悲观锁,加锁后其他修改数据的请求都会进入等待过程

悲观锁使用场景

使用场景举例:以MySQL InnoDB为例

商品goods表中有一个字段status,status为1代表商品未被下单,status为2代表商品已经被下单,那么我们对某个商品下单时必须确保该商品status为1。假设商品的id为1。

1. 如果不采用锁,那么操作方法如下:
//1.查询出商品信息

select status from t_goods where id=1;

//2.根据商品信息生成订单

insert into t_orders (id,goods_id) values (null,1);

//3.修改商品status为2

update t_goods set status=2;

上面这种场景在高并发访问的情况下很可能会出现问题。

前面已经提到,只有当goods status为1时才能对该商品下单,上面第一步操作中,查询出来的商品status为1。但是当我们执行第三步Update操作的时候,有可能出现其他人先一步对商品下单把goods status修改为2了,但是我们并不知道数据已经被修改了,这样就可能造成同一个商品被下单2次,使得数据不一致。所以说这种方式是不安全的。

2. 使用悲观锁来实现:

在上面的场景中,商品信息从查询出来到修改,中间有一个处理订单的过程,使用悲观锁的原理就是,当我们在查询出goods信息后就把当前的数据锁定,直到我们修改完毕后再解锁。那么在这个过程中,因为goods被锁定了,就不会出现有第三者来对其进行修改了。

注:要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。

我们可以使用命令设置MySQL为非autocommit模式:

set autocommit=0;

设置完autocommit后,我们就可以执行我们的正常业务了。具体如下:

//0.开始事务

begin;/begin work;/start transaction; (三者选一就可以)

//1.查询出商品信息

select status from t_goods where id=1 for update;

//2.根据商品信息生成订单

insert into t_orders (id,goods_id) values (null,1);

//3.修改商品status为2

update t_goods set status=2;

//4.提交事务

commit;/commit work;

猜你喜欢

转载自blog.csdn.net/qq_39647045/article/details/81385072