MySql--锁机制

锁机制

三种并发控制机制:悲观并发控制、乐观并发控制和多版本并发控制。悲观并发控制其实是最常见的并发控制机制,也就是锁;乐观并发控制其实也有另一个名字:乐观锁. MVCC多版本并发控制机制,可以与前两者中的任意一种机制结合使用,以提高数据库的读性能。

乐观锁:在访问数据之前,默认不会有其他事务对此数据进行修改,所以先访问数据,然后再查找在此期间是否有事务修改数据。这不是数据库自带的,需要我们自己去实现,一般基于版本去实现。

悲观锁:悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

按照锁的粒度把数据库锁分为表级锁和行级锁。

表级锁: 对当前操作的整张表加锁,实现简单,加锁快,不死锁,但并发能力低。

行级锁: 只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁

Record Lock记录锁: 锁住某一行如果表存在索引,那么记录锁是锁在索引上的,如果表没有索引,那么 InnoDB 会创建一个隐藏的聚簇索引加锁

Gap LocK间隙锁: 间隙锁是一种记录行与记录行之间存在空隙或在第一行记录之前或最后一行记录之后产生的锁。间隙锁可能占据的单行,多行或者是空记录。 对索引项之间的“间隙”加锁,锁定记录的范围,不包含索引项本身其他事务不能在锁范围内插入数据,这样就防止了别的事务新增幻影行。

Next-key Lock: 锁定索引项本身和索引范围。NK 是一种记录锁和间隙锁的组合锁。既锁住行也锁住间隙。即Record Lock和Gap Lock的结合。可解决幻读问题。

根据是否独占,锁又可以分为共享锁和排他锁

共享锁(Share Locks,简记为S)又被称为读锁,其他用户可以并发读取数据,但任何事务都不能获取数据上的排他锁直到已释放所有共享锁。

排它锁((Exclusive lock,简记为X锁))又称为写锁若事务T对数据对象A加上X锁,则只允许T读取和修改A,其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。

Innodb同时支持行锁和表锁。但行锁和表锁的同时存在会发生冲突,如A申请了行共享锁,而B再申请表互斥锁。这时B不仅需要查看是否已经存在其他表锁,以及逐个查看是否存在行锁,效率太低。于是又引入了意向锁。意向锁是一种表级锁,用来指示接下来的一个事务将要获取的是什么类型的锁(共享还是独占)。意向锁分为意向共享锁(IS)和意向独占锁(IX),依次表示接下来一个事务将会获得共享锁或者独占锁。

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

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

在意向锁存在的情况下,事务A必须先申请表的意向共享锁,成功后再申请一行的行锁。而事务B发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。

而且,申请意向锁的动作是数据库自动完成的,不需要我们手动申请。

 

MVCC多版本并发控制(Multiversion Concurrency Control),多版本控制: 指的是一种提高并发的技术。最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提高了InnoDB的并发度。

每一个写操作都会创建一个新版本的数据,读操作会从有限多个版本的数据中挑选一个最合适的结果直接返回;在这时,读写操作之间的冲突就不再需要被关注,而管理和快速挑选数据的版本就成了 MVCC 需要解决的主要问题。

各数据库中MVCC实现并不统一,MVCC只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下工作;

对于使用InnoDB存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列:(trx_id事务ID、roll_pointer上个版本指针,其实还有一个row_id的隐藏列但这里用不着);

每次对记录进行改动,都会把对应的事务id赋值给trx_id隐藏列,也会把旧的版本写入到undo日志中;

所以在并发情况下,一个记录可能存在多个版本,通过roll_pointer形成一个版本链。MVCC的核心任务就是:判断一下版本链中的哪个版本是当前事务可见的。这就有了ReadView的概念,这个ReadView中主要包含当前系统中还有哪些活跃的读写事务,把它们的事务id放到一个列表中,我们把这个列表命名为为m_ids;根据ReadView的活跃事务ID列表和版本链事务ID进行比较找出可见的事务ID最大的版本:

1、如果版本的trx_id属性值小于m_ids列表中最小的事务id,表明生成该版本的事务在生成ReadView前已经提交,所以该版本可以被当前事务访问。

2、如果版本的trx_id属性值大于m_ids列表中最大的事务id,表明生成该版本的事务在生成ReadView后才生成,所以该版本不可以被当前事务访问。

3、被访问版本的trx_id属性值在m_ids列表中最大的事务id和最小事务id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。

MVCC只在读已提交和可重复读这两个隔离机制下运行。这两个隔离机制下MVCC实现方式的区别就在于:读已提交是每次读取数据前都生成一个ReadView;而可重复读,是在第一次读取数据时生成一个ReadView,后序的重复查询就不再生产ReadView了。

       多版本并发控制指的就是在使用READ COMMITTD、REPEATABLE READ这两种隔离级别的事务在执行普通的SEELCT操作时访问记录的版本链的过程,这样子可以使不同事务的读-写、写-读操作并发执行,从而提升系统性能。READ COMMITTD、REPEATABLE READ这两个隔离级别的一个很大不同就是生成ReadView的时机不同,READ COMMITTD在每一次进行普通SELECT操作前都会生成一个ReadView,而REPEATABLE READ只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复这个ReadView就好了。

  Mysql死锁处理方式:

1、等待,直到超时,事务自动回滚。

2、发起死锁检测, 回滚一个事务,让其他事务执行。

死锁检测,构建一个以事务为起点,锁为边的有向图,看是否存在环。

 

不经一番彻骨寒,哪有梅花扑鼻香?
原文地址:https://www.cnblogs.com/zongyao/p/13831101.html