作为程序员一定要保持良好的睡眠,才能好编程

MySQL中锁类型详细介绍 共享锁 排他锁 间隙锁

发布时间:2018-04-28

MySQL中锁详解(行锁、表锁、页锁、悲观锁、乐观锁等)


https://blog.csdn.net/tanga842428/article/details/52748531

为了剞劂数据库并发控制问题,如在同一时刻,客户端对于同一个表做更新或者查询操作,为保证数据的额一致性,需要对并发操作进行控制,因此就产生了锁。


锁的类型:


1、共享锁

共享锁代号是S,共享锁的锁粒度是行或多个行,一个事物货物共享锁之后,可以对锁定范围内的数据执行读操作。


2、排他锁

排它锁的粒度与共享锁相同,也是行或者多行,一个事物获取了排他锁之后,可以对锁定范围内的数据执行写操作。


3、意向锁

意向锁是一种表锁,锁定的粒度是整张表。


锁与锁之间的关系,要么相容,要么是互斥的。

意向锁又分为 意向排他锁 和 意向共享锁



为了尽可能提高数据库的并发量,需每次锁定的数据范围越小越好,越小的锁,其消耗的系统资源越多,系统性能越下降,这样的情况下就产生了锁粒度的概念


锁粒度主要分为 表锁和 行锁


MyIsam 使用表锁,当写入数据是,把整个表记录被锁,此时读取、写操作一律等待。同时 alter table 执行时使用的也是表锁。


InnoDb使用行锁,若搜索条件做的不好,也可能导致表锁 


Mysql一些语句执行所的情况:



select .... LOCK In SHARE MODE

此操作会加上一个共享锁,若会话师傅中查找的数据已经被其他会话事物加上排他锁的话,共享锁会等待其结束再加,若等待时间过长 就会 锁等待超时。


select ... for update

此操作会加上一个排它锁,其他会话事务,讲无法再加其他锁,必须等待其结束


锁定范围:


select * from user for update  锁定user全表

select * from user where id=1 for update 锁定user表中id为1 的行

select * from user,class where user.class_id=class.id for update 锁定user和class表全部

select * from user,class where user.class_id=class.id and user.id=9 for update 锁定两个表符合条件的行

select * from user,class where user.class_id=class.id and user.id=9 for update of user.id 只锁定user表中符合条件的行




select * from user where id =100 for update nowait



mysql> update a set name='aa' where id=5;

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction




Insert 、update、delete  会话会对DML语句操作的数据加上一个排他锁,其他会话的事务都会等待其结束。



Innodb引擎会自动给会话事务中的共享锁、更新锁、排他锁,加到一个区间值域是,这个锁就是间隙锁,对不存在的数据也锁住,防止出现幻写。


查看Mysql中InnoDB引擎的状态:

show engine innodb status \G


当超过事务等待锁运去的最大时间,此时会提示

"ERROR1205(HY000):Lock wait timeout exceeded;try restarting transaction"

 及当时事务执行失败,则自动执行回滚操作。


INNODB锁为行级锁,同时做小范围的锁定的范围,注意此时where条件指定的列有主键或索引。



由于InnoDB为行级锁为间隙所,加锁只锁定需要的记录,因此多个事务可以互不影响。






乐观锁

乐观锁不是数据库自带的,需要我们自己去实现。乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。

通常实现是这样的:在表中的数据进行操作时(更新),先给数据表加一个版本(version)字段,每操作一次,将那条记录的版本号加1。也就是先查询出那条记录,获取出version字段,如果要对那条记录进行操作(更新),则先判断此刻version的值是否与刚刚查询出来时的version的值相等,如果相等,则说明这段期间,没有其他程序对其进行操作,则可以执行更新,将version字段的值加1;如果更新时发现此刻的version值与刚刚获取出来的version的值不相等,则说明这段期间已经有其他程序对其进行操作了,则不进行更新操作。

举例:


下单操作包括3步骤:

1.查询出商品信息

select (status,status,version) from t_goods where id=#{id}

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

3.修改商品status为2

update t_goods 

set status=2,version=version+1

where id=#{id} and version=#{version};


除了自己手动实现乐观锁之外,现在网上许多框架已经封装好了乐观锁的实现,如hibernate,需要时,可能自行搜索"hiberate 乐观锁"试试看。




乐观锁 实现方式







共享锁 如何加锁?


在语句后面 加上  lock in share mode





排它锁 如何加锁?


在语句后面加上  for update;



行锁

行锁,由字面意思理解,就是给某一行加上锁,也就是一条记录加上锁。


比如之前演示的共享锁语句


SELECT * from city where id = "1"  lock in share mode; 


由于对于city表中,id字段为主键,就也相当于索引。执行加锁时,会将id这个索引为1的记录加上锁,那么这个锁就是行锁。