latch 能够认为是应用程序中的锁,能够称为闩锁(轻量级的锁) 由于其要求锁定的时间必需要很是短,若持续时间长,则会致使应用性能很是差,在InnoDB存储引擎中,latch又能够分为mutex(互斥锁)和rwlock(读写锁),其目的用来保证并发线程操做临界资源的正确性,而且没有死锁检测的机制html
在InnoDB存储引擎中的latch,能够经过命令SHOW ENGINE INNODB MUTEX 来进行查看mysql
mysql > SHOW ENGINE INNODB MUTEX;算法
lock能够认为是数据库提供的锁,用来锁定的是数据库中的数据。而且通常lock对象仅在事务commit或rollback后进行释放(不一样事务隔离级别释放的时间可能不一样),lock是有死锁机制的。sql
在InnoDB存储引擎中的lock 能够经过show engine innodb status,information_schema.INNODB_LOCKS,INNODB_TRX,INNODB_LOCK_WATIS信息来查看数据库
线程获取lock的流程:并发
在对数据加lock的时候会先对数据所在的页面添加latch,而后再对数据添加lock,添加完lock后再释放页面的Latch。mvc
这种机制主要是为了保证线程获取的行数据的一致性和完整性.性能
若是lock被其余的线程占有,线程先释放页面latch,等待lock,待获取lock后会再次对页面添加latch,查看页面数据是否有改动,而后尝试再次获取对应的lockspa
innodb储存引擎提供了以下两种标准的行级锁线程
共享锁(S) 容许一个事务去读一行
排他锁(X) 容许得到排他锁的事务更新或删除数据
同时innodb储存引擎支持多粒度锁定,为了支持在不一样的粒度上进行加锁操做,innodb支持另外一种额外的锁方式,称之为意向锁
意向共享锁(IS) 事务想要得到一张表中某几行的共享锁
意向排他锁(IX)事务想要得到一张表中某几行的排他锁
在行锁的实现上
mysql提供了三种的行锁的算法
分别是
Record Lock 记录锁,单个记录上的锁
Gap Lock 间隙锁,锁定一个范围,但不包含记录自己
Next-key Lock Gap Lock + Record Lock 锁定一个范围,而且锁定记录自己
非特殊注明 默认在RR隔离级别下进行讨论
InnoDb的行锁是对索引加锁的,对扫描的行边扫描边加锁,若是走的是二级索引(非聚簇索引)除了须要对二级索引加锁外,还须要根据二级索引里面的主键信息扫描主键的聚簇索引,对主键加锁,
加锁的数据行数会受到Mysql是否支持Index Condition PushDown而影响(Mysql 5.6支持ICP),加锁的数量可能远远大于知足条件的记录数量
这里须要加两次锁的缘由是
若是
语句A 使用二级索引对记录X进行更新操做,
语句B使用聚簇索引对记录X进行更新操做,
若是A仅对二级索引进行加锁,那么并发的语句B将感觉不到语句A的存在,违背了同一条记录上的更新/删除必须串行执行的约束
RC级别下 : 无需加锁,一致性非锁定读,使用快照读,读取被锁定行的最新一份数据,所以会出现先后读取数据不一致的状况
RR级别下:无需加锁,一致性非锁定读,使用快照读,读取事务开始时的行数据版本,所以先后读到的数据是同样的
Serializable级别下:使用当前读,须要加锁,innodb内部将select语句转换为了select … lock in share mode
insert会对插入成功的行加上记录锁,不会阻止其余并发的事务往这条记录以前插入记录。在插入以前,会先在插入记录所在的间隙加上一个插入意向意向锁(并发的事务能够对同一个间隙加插入意向锁锁)。若是insert 的事务出现了duplicate-key error ,事务会对duplicate index record的记录加共享锁。这个共享锁在并发的状况下是会产生死锁的,好比有两个并发的insert都对要对同一条记录加共享锁,而此时这条记录又被其余事务加上了排它锁,排它锁的事务将这条记录删除后,两个并发的insert操做会发生死锁。
delete操做仅是将主键列中对对应的记录delete flag设置为1,记录并无被删除,仍是存在于B+树中
真正的删除操做被延迟了,最终在purge操做中完成
延迟到purge操做的缘由是的innodb支持mvcc多版本控制,因此记录不能在事务提交时当即进行删除,只有当对应的行记录不被任何其余事务引用的时候,才能够由purge进行真正的删除
delete操做过程当中:
找到知足条件的记录,而且记录有效,则对记录加X锁
找到知足条件的记录,可是记录无效(标识为删除),则对记录加next key锁、;
未找到知足条件的记录,则对第一个不知足条件的记录加Gap锁,保证没有知足条件的记录插入;
对知足条件的记录next-key锁,若是是等值匹配而且使用惟一索引或是聚簇索引,那么能够只添加记录锁
惟一索引中含NULL值的记录,将不会添加记录锁,转而为next-key锁 由于NULL不等于NULL,NULL和任何值比较均返回NULL,包括NULL自己,可是 NULL is NULL
create table `deadlocktest`
(
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`a` bigint(20) unsigned NOT NULL,
`b` bigint(20) unsigned NOT NULL,
`c` bigint(20) unsigned NOT NULL,
`d` bigint(20) unsigned NOT NULL,
`e` bigint(20) unsigned NOT NULL,
PRIMARY KEY(`id`),
UNIQUE KEY `I_a`(`a`),
KEY `I_b` (`b`),
KEY `I_c` (`c`)
)ENGINE=InnoDb ;
insert into deadlocktest (a,b,c,d,e)values(1,999,3,4,5);
insert into deadlocktest (a,b,c,d,e)values(2,998,4,5,6);
insert into deadlocktest (a,b,c,d,e)values(3,997,4,5,6);
insert into deadlocktest (a,b,c,d,e)values(4,996,3,4,5);
...
insert into deadlocktest (a,b,c,d,e)values(1000,1,3,4,5);
事务A |
事务B |
事务C |
begin; |
begin; |
begin; |
insert into deadlocktest (a,b,c,d,e)values(4,996,3,4,5); |
|
|
|
insert into deadlocktest (a,b,c,d,e)values(4,996,3,4,5); |
|
|
|
insert into deadlocktest (a,b,c,d,e)values(4,996,3,4,5); |
rollback; |
|
|
|
1 row affected |
Deadlock found when trying to get lock; try restarting transaction |
事务A 得到排他锁,插入数据成功
事务B 事务C,由于记录duplicate-key error转而持有行的共享锁
事务A回滚,释放了持有的排他锁,事务B和事务C须要得到该行的排他锁,可是因为互相都持有对应行的共享锁,互相等待,形成死锁
事务A |
事务B |
begin; |
begin; |
update deadlocktest force index(I_b) set e = sleep(5) where b>0; |
|
|
update deadlocktest force index(I_c) set e = sleep(5) where c>2; |
Deadlock found when trying to get lock; try restarting transaction |
Rows matched: 4 Changed: 4 Warnings: 0 |
两个update事务,加锁顺序不同致使的死锁
InnoDb的行锁是对索引加锁的,对扫描的行边扫描边加锁,若是走的是二级索引(非聚簇索引)除了须要对二级索引加锁外,还须要根据二级索引里面的主键信息扫描主键的聚簇索引,对主键加锁
事务A |
事务B |
事务B |
begin; |
begin; |
begin |
delete from deadlocktest where a=550 |
|
|
|
delete from deadlocktest where a=550 |
|
|
delete from deadlocktest where a=550 |
|
commit; | 0 rows affected | Deadlock found when trying to get lock; try restarting transaction |
delete操做仅是将主键列中对对应的记录delete flag设置为1,实际的删除延迟到purge中
delete删除时若是找到知足条件的记录,可是记录无效(标识为删除),则对记录加next key锁、;
死锁日志
3个delete的死锁比较难以复现,我是利用以下脚本完成的
MY_DB="mysql -hxxx -Pxxx -uxxx -pxxx"
while :
do
echo "use test;begin; delete from deadlocktest where a=499;rollback;" | $MY_DB
done
该类delete死锁的出现条件
一、针对惟一索引上等值查询的删除
二、有3个以上并发删除操做
三、事务的隔离级别是RR
四、INNODB储存引擎
https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html