精讲MySQL 第十五章(一):锁概览

事务的隔离性由锁(LOCK)来实现。

1. 概述


是计算机协调多个进程或线程并发访问某一资源的机制。在程序开发中会存在多线程同步的问题,当多个线程并发访问某个数据的时候,尤其是针对一些敏感的数据(比如订单、金额等),我们就需要保证这个数据在任何时刻最多只有一个线程在访问,保证数据的一致性完整性。在开发过程中加锁是为了保证数据的一致性,这个思想在数据库领域中同样重要。
在数据库中,除传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供许多用户共享的资源。为保证数据的一致性,需要对并发操作进行控制,因此产生了锁。同时锁机制也为实现MySQL的各个隔离级别提供了保证。锁冲突也是影响数据库并发访问性能的一个重要因素。所以锁对数据库而言显得尤其重要,也更加复杂。

2. MySQL并发事务访问相同记录


并发事务访问相同记录的情况大致可以分为3种:

2.1 读-读情况

读-读情况,即并发事务相继读取相同的记录。读取操作本身不会对记录有任何影响,并不会引起什么问题,所以允许这种情况的发生。

2.2 写-写情况

写-写情况,即并发事务相继对相同的记录做出改动。
在这种情况下会发生脏写的问题,任何一种隔离级别都不允许这种问题的发生。所以在多个未提交事务相继对这一条记录做改动时,需要让它们排队执行,这个排队的过程其实是通过来实现的。这个所谓的锁其实是一个内存中的结构,在事务执行前本来是没有锁的,也就是说一开始是没有锁结构和记录相关联的,如图所示:

15.1 锁-记录.png

当一个事务想对这条记录做改动时,首先会看看内存中有没有与这条记录相关联的锁结构,当没有的时候就会在内存中生成一个锁结构与之关联。比如,事务T1要对这条记录做改动,就需要生成一个锁结构与之关联:
15.2 锁-结构.png

锁结构里有很多信息,为了简化理解,只把两个比较重要的属性拿了出来:

  • trx信息:代表这个锁结构是那个事务生成的。
  • is_waiting:代表当前事务是否在等待。

当事务T1改动了这条记录后,就生成一个锁结构与改记录关联,因为之前没有别的事务为这条记录加锁,所以is_waiting属性就是false,我们把这个场景就称之为获取锁成功或者加锁成功,然后就可以继续执行操作了。

当事务T1提交之前,另一个事务T2也想对该记录做改动,那么先看看有没有锁结构与这条记录关联,发现有一个锁结构与之关联后,然后也生成了一个锁结构与这条记录关联,不过锁结构的is_waiting属性值为true,表示当前事务需要等待,我们把这个场景就称之为获取锁失败,或者加锁失败,图示:

15.3 加锁失败.png

在事务T1提交之后,就会把该事务生成的锁结构释放掉,然后看看还有没有别的事务在等待获取锁,发现了事务T2还在等待获取锁,所以把事务T2对应的锁结构的is_waiting属性设置为false,然后把该事务对应的线程唤醒,让它继续执行,此时事务T2就算获取到锁了。效果图就是这样:

15.4 锁释放.png

小结几种说法:

  • 不加锁
    意思就是不需要在内存中生成对应的锁结构,可以直接执行操作。
  • 获取锁成功,或者加锁成功
    意思就是在内存中生成了对应的锁结构,而且锁结构的is_waiting属性为false,也就是事务可以继续执行操作。
  • 获取锁失败,或者加锁失败,或者没有获取到锁
    意思就是在内存中生成了对应的锁结构,不过锁结构的is_waiting属性为true,也就是事务需要等待,不可以继续执行操作。

2.3 读-写或写-读情况

读-写写-读,即一个事务进行读取操作,另一个进行改动操作 。这种情况下可能发生脏读不可重复读幻读的问题。

各个数据库厂商对SQL的支持都不能一样。比如MySQL在REPEATABLE READ隔离级别上就已经解决了幻读问题。

3. 小结


本篇简单介绍了锁作为解决并发问题在程序和数据库中应用,主要讲述三种并发访问问题,在写写-读写-写读场景下会出现并发问题。

你可能感兴趣的:(精讲MySQL 第十五章(一):锁概览)