InnoDB,锁,事务。

前面记录事务的时候,有些地方有点蒙蔽,再记录下mysql的引擎InnoDB的知识点。

mysql InnoDB引擎实现的是基于多版本的并发控制协议-MVCC。

MVCC读不加锁,读写不冲突,极大的增加了系统的并发性能,几乎所有的关系型数据库管理系统都支持MVCC。


在MVCC并发控制中,读操作可以分为两类:快照读和当前读。

快照读:读取的是记录的可见版本,不用加锁。

当前读:读取的是当前的最新版本,并且当前返回的记录都会加锁,来保证其他并发事务不会并发修改这条记录。


InnoDB当前读有以下两种类型的锁。 

共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。 

排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。

 另外,为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁。 

意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。 

意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。

InnoDB,锁,事务。_第1张图片

如果一个事务请求的锁模式与当前的锁兼容,InnoDB就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。 意向锁是InnoDB自动加的,不需用户干预。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁;事务可以通过以下语句显示给记录集加共享锁或排他锁。 

共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。 

排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE。


在mysql InnoDB中快照读和当前读:

快照读:简单的select的属于快照读,不加锁(也有例外,后面记录),即select * from xxx where xxx=xxx;

当前读:特殊的读操作、插入/更新/删除操作,属于当前读,会加锁。

如:select * from xxx where xxx=xxx lock in share mode; 加共享锁,即S锁

select * from table where xxx = xxx for update; 加排他锁,即X锁

insert/update/delete;加排他锁,即X锁

为什么将insert/update/delete都归为当前读?因为这些操作虽然是写操作,但其中却都包含了当前读的操作。

InnoDB,锁,事务。_第2张图片

从图中,可以看到,一个Update操作的具体流程:当Update SQL被发给MySQL后,MySQL Server会根据where条件,读取第一条满足条件的记录,然后InnoDB引擎会将第一条记录返回,并加锁 (current read)。待MySQL Server收到这条加锁的记录之后,会再发起一个Update请求,更新这条记录。一条记录操作完成,再读取下一条记录,直至没有满足条件的记录为止。因此,Update操作内部,就包含了一个当前读。同理,Delete操作也一样。Insert操作会稍微有些不同,简单来说,就是Insert操作可能会触发Unique Key的冲突检查,也会进行一个当前读。

根据上图的交互,针对一条当前读的SQL语句,InnoDB与MySQL Server的交互,是一条一条进行的,因此,加锁也是一条一条进行的。先对一条满足条件的记录加锁,返回给MySQL Server,做一些DML操作;然后在读取下一条加锁,直至读取完毕。

注:InnoDB是行锁,是基于索引的,所以如果没有索引,行锁会升级为表锁,会把整个表锁住。

例子:

第一个事务加共享锁读,第二个事务更新的时候只能等待,如果第一个事务普通读,第二个就可以更新。

InnoDB,锁,事务。_第3张图片

InnoDB,锁,事务。_第4张图片

锁搞清楚了,再来看下两阶段锁,2PL上篇记录事务的文章有。

InnoDB,锁,事务。_第5张图片

事务中具体什么语句加什么锁,和是什么索引以及mysql什么实现有关。下一篇记录。


你可能感兴趣的:(java)