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的记录加上锁,那么这个锁就是行锁。