在mysql当中,关于innodb的锁类型总共能够分为四种,包含了行锁和表锁,分别是mysql
下面是各类锁之间的对应兼容状况(ps:在某一篇博客上看到,忘了是哪一篇,以为好就截下来了嘻嘻):算法
InnoDB三种行锁的算法:sql
共享锁shared locks(S锁)也称读锁,容许其余事物再加S锁,不容许其余事物再加X锁数据库
加锁方式:bash
select...lock in share mode复制代码
注意:数据结构
- 对于使用共享锁的事务,其余事务只能读,不可写
- 若是执行了更新操做则会一直等待,直到当前事务commit或者rollback
- 若是当前事务也执行了其余事务处于等待的那条sql语句,当前事务将会执行成功,而其余事务会报死锁
- 而且容许其余锁共存
排它锁Exclusive Locks(X锁)也称写锁,不容许其余事务再加S锁或者X锁并发
加锁方式:测试
select ... for update复制代码
→ for update:InnoDB默认是行级别的锁,当有明确指定的主键时,使用的是行锁;不然使用的是表锁。使用状况详细以下:ui
select name,age from tb_user where id = '1' for update(id是主键)复制代码
select name,age from tb_user where id = '1' for update(id是主键,但不存在id = 1的数据)复制代码
select name,age from tb_user where age = 12 for update(age是普通字段)复制代码
select name,age from tb_user where age = 12,id = '1' for update(id是主键,age不是,但数据库有此数据)复制代码
注意:spa
- 对于排它锁的事务,其余事物可读,但不可进行更新操做
- for update仅使用与InnoDB,而且必须开启事务,在begin和commit之间才生效
- 当一个事务进行for update的时候,另外一个事务也有for update时会一直等待,直到以前的事务commit或rollback或断开链接释放锁才拿到锁进行后面的操做(排它锁不能共存)
- innoDB引擎.默认对update,delete,insert加排他锁,select语句默认不加锁
读取出记录,并将此版本一同读出,执行更新操做并对记录的版本号+1。此时,将待提交记录的版本号与数据库对应表的记录版本好进行对比,若是大于数据库原有版本号的话,予以更新;不然认为是过时数据,更新失败。目的是为了用于解决并发问题。
例:A、B两我的同时修改同一条记录,设数据库原有金额是100元,A对金额+100,B往数据库-50,正常结果是150,但因为并发,结果有多是200,或者50
解决:A B同时读取出数据版本为1,A对金额+100,并修改数据版本为2,提交数据,此时数据版本为1,更新成功。B读取数据版本1,对金额-50,此时结果为50,并修改数据版本为2,提交数据,对比数据库原版本2,没有比原版本高,更新失败
间隙锁是在索引记录之间的间隙的锁定,或在最后一个索引记录以前或以后的间隙上的锁定
使用惟一索引搜索惟一一行的一句不须要间隙锁锁定(不包括搜索条件包含多列惟一索引的某些列的状况,查询出的多条记录,会发生间隙索引),详细例子以下:
select id,name,age from tb_user where id = '1'复制代码
CREATE TABLE `tb_user` (
`id` int(10) NOT NULL,
`name` varchar(255) NOT NULL DEFAULT '',
KEY `index_id` (`id`),
KEY `index_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8复制代码
→ 初始化数据:
ps:我如今使用的是数据库默认的隔离级别:repeatable read,并在本地开启两个客户端进行测试。
→ 客户端1:开启事务,在客户端中修改id为1-6之间的数据,此时id=2这行记录是不存在的
→ 客户端2:开启事务,往数据库中添加id为2的记录时会发现该操做会被阻塞!
-->上述状况就说明了有间隙锁的存在
--> 接下来我修改了隔离级别为read commited,能够发现上述添加操做,即id =2 的记录会添加成功,说明read commited的隔离级别不会使用间隙锁。
注意:
- 间隙锁在InnoDB的惟一做用就是防止其它事务的插入操做,以此来达到防止幻读的发生,因此间隙锁不分什么共享锁与排它锁。
- 若是InnoDB扫描的是一个主键/惟一索引,那么InnoDB只会采用行锁(Record Lock)方式来加锁,而不会使用间隙锁(Next-Key Lock)的方式。
- 间隙锁只是阻止其余事物插入到间隙当中,并不阻止不一样的事物在同一间隙上得到间隙锁。
- 将隔离级别设置为read_commited或启用innodb_locks_unsafe_for_binlog系统变量(现已被弃用)可明确禁止使用间隙锁
MVCC,基于多版本的并发控制协议,最典型的是读不加锁,读写不冲突,其包含两种读操做,即快照读(snapshot read)与当前读(current read)。
那具体哪些操做为当前读,哪些操做又是快照读呢,让咱们来看一下:
→ 快照读:简单的读操做,属于快照读,不加锁。(不过有些会有点小例外)
例:select * from tb_user where ?
→ 当前读:特殊的读操做,属于当前读,须要加锁。
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;
全部以上的语句,都属于当前读,读取记录的最新版本。而且,读取以后,还须要保证其余并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其余的操做,都加的是X锁 (排它锁)。
以上是我这几天研究数据库锁作出的总结,有什么不足欢迎指正哈~
最后推荐一位大牛写的博客哈,里面写的很详细:hedengcheng.com/?p=771#_Toc…