InnoDB存储引擎中的锁机制和事务模型

并发访问和锁

锁的作用

 

锁用于协调多个线程对同一资源的并发访问。MySQL数据库中的资源主要是指数据库中的表和表中的记录,也就是数据库中的数据。为什么需要锁呢?因为如果没有锁机制,多个并发修改数据的线程可能会使被修改的数据处于混乱的状态。而且,在修改数据期间,如果不加锁的话,查看数据的线程看到的可能是处于部分修改状态的数据。因此,必须引入锁机制使这些线程对数据的访问协调一致。

 

 

锁的访问级别

 

对数据库中数据的操作主要分为两种类型,一是读取(select),一是修改(insert, update, delete)。对于读取线程,因为它们不会修改数据,所以可以允许多个线程同时读取相同的数据。而对于修改线程,只能独占的访问数据,不允许其他的线程读取或修改数据。因此,从对数据操作的类型出发,可以把锁分为读锁和写锁两种访问级别,通过引入访问级别可以显著提高数据访问的并发性。读锁又称为共享锁,写锁又称为排他锁。

锁粒度

 

为了提高对资源的并发访问,经常采用的一种方式就是把资源划分成更小的部分(也就是更小的粒度),从而允许多个并发访问的线程同时访问不同部分。这样就可以把锁加在不同粒度的资源上,从而形成不同粒度的锁。在InnoDB中,主要定义了两种锁粒度,即表级锁和行级锁。表级锁是加在整个表上的。行级锁是加到表中一条条记录上面的,所以它具有更小的粒度,同时也具有更好的并发性。但是,锁本身也是需要开销的,因此行级锁可能会具有更大的开销。在日常使用中,我们一般只关注InnoDB中的行级锁。InnoDB中的行级锁具有上面介绍的两种访问级别,即共享锁和排他锁。

事务

 

事务的概念

事务是一个独立的不可分割的工作单元。一个事务可以包含多个操作(也就是多个SQL语句),这些操作是作为一个整体而存在的。事务执行时,这些操作要么全都执行成功,要么全都执行失败(错误发生时回滚所有已执行的操作),而不会出现只有一部分执行成功的情况。一个常见的例子就是从A账户转账200元到B账户,可以分为3个操作:

  1. 确保A账户余额高于200元;
  2. 从A账户扣除200元;
  3. 把B账户余额增加200元。

这3个操作必须同时成功,或者同时失败,如果没有事务,执行完第二个步骤后失败了,那么A账户将凭空消失200元。所以这3个操作需要作为一个整体执行,如果任何一个步骤失败了,就回滚所有之前的操作,使得系统仍然处于一致的状态。

事务的ACID属性

 

事务应该满足ACID属性,下面分别介绍。

原子性(Atomicity) 

事务是一个不可分割的工作单元,其中的操作要么全部成功,要么全部失败。

一致性(consistency)

在事务执行后,系统是从一个一致性的状态转换到另一个一致性的状态,而不会处在被破坏的状态。一致性其实更多是从应用程序的角度来说的,因为一个事务包含哪些操作是由应用程序定义的,因此,应用程序要保证事务里面的操作本身是符合业务规则的。而数据库更多的是通过AID三个属性来保证数据库的状态符合业务规则的定义。

隔离性(isolation)

隔离性是针对多个事务而言的,简单来说就是一个事务执行时,就好像拥有了自己的独立执行环境一样,而不受其他同时执行的事务的影响。实际上隔离性受到多种因素的影响,例如事务隔离级别、锁机制等。

持久性(durability)

持久性是说一旦一个事务执行完成,那么它对数据库的修改就会被永久保存下来,而不会凭空消失。即使事务完成后系统发生崩溃,事务对数据的修改也不会丢失。

事务的隔离级别

 

事务的隔离级别主要规定了一个事务在执行过程中或者提交后,它对数据的修改对于其他并发执行的事务的可见程度。InnoDB中主要支持4种隔离级别,默认的隔离级别为"可重复读"。

读未提交(READ UNCOMMITTED)

一个事务修改了数据还未提交时,其他事务就能看到它对数据的修改。这种隔离级别可能会发生"脏读"的问题,因为事务还未提交,所以它对数据的修改可能最终被回滚掉,或者它在随后对数据又进行了进一步修改。这样其他事务看到的可能就不是数据最终的状态,而只是临时的状态。这种隔离级别会有很大的问题,所以在实际的生产环境中很少被使用。

读提交(READ COMMITTED)

一个事务对数据的修改只有在提交之后才能被其他事务看到。这种隔离级别可以避免脏读的问题,但是可能会产生"不可重复读"的问题。不可重复读是指一个事务两次读取同一条记录却得到不同的值。例如,有A和B两个事务,B开始时读取了记录r得到值v1,之后A事务修改了r的值为v2并提交,然后B事务再次读取r记录,发现读取到的值为v2。这种情况可能会对B事务产生不好的影响,因为B可能会基于r的值作出一些操作。

可重复读(REPEATABLE READ)

 

这种隔离级别解决了不可重复读的问题,即一个事务多次读取一条记录得到的值是相同的。但是可能会产生"幻读"的问题。"幻读"是指一个事务在开始时根据指定条件读取到了一些记录,之后再次以同样的条件查询时读取到的记录却比之前多,好像产生了幻影行一样。这是InnoDB的默认隔离级别,但是InnoDB通过引入MVCC(多版本并发控制)的机制解决了幻读问题。

串行化(SERIALIZABLE)

最高的隔离级别,它强制事务串行执行,从而解决了"幻读"问题。它会在读取到的每一条记录上加锁,所以可能导致大量的锁争用,极大的影响系统性能,因此在实际中很少使用。

查看MySQL事务隔离级别

show variables like 'transaction_isolation';  # MySQL >= 5.7.20
show variables like 'tx_isolation'; # MySQL < 5.7.20

 

事务和锁的关系

事务中包含一个或者多个SQL语句,每一个SQL语句在执行的过程中可能需要获取锁。因此,一个事务在执行过程中可能会包含多个锁请求,在事务执行完成后需要释放使用到的锁。在InnoDB中,事务获取和释放锁的过程主要有两点需要注意:

  1. 锁的获取是逐步进行的,执行到某条SQL时才申请相应的锁。
  2. 锁的释放是一次性进行的,在事务提交或者回滚时一次性释放事务占用的所有锁。

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值