事务用来保证数据库的完整性——要么都修改,要么都不修改。事务必须知足ACID四个特性。mysql
ISO和ANIS SQL标准制定了四种事务隔离级别的标准:算法
InnoDB既支持行级锁,也支持表级锁,默认状况下是采用行级锁。
InnoDB存储引擎实现了两种标准的行级锁:sql
InnoDB存储引擎支持多粒度锁定,容许行级锁和表级锁同时存在。为了支持在不一样粒度上进行加锁操做,InnoDB存储引擎提供了意向锁。
意向锁是表级别的锁,其设计目的主要是为了在一个事务中揭示下一行将被请求的锁的类型。InnoDB存储引擎支持两种意向锁:数据库
意向锁不会阻塞除全表扫描之外的任何请求。
并发
一致性的非锁定读(consistent nonlocking read)是指InnoDB存储引擎经过多版本控制(multi versioning)
的方式来读取当前执行时间数据库中行的数据。若是读取的行正在执行DELETE、UPDATE操做,这是读取操做不会所以而等待行上锁的释放,相反,InnoDB存储引擎会去读取行的一个快照数据。以下图所示:测试
上图能够看出,读取的数据是一份快照数据,快照数据是指执行当前更改操做前的数据,该实现是经过undo段来实现的,因此快照数据自己是没有额外的开销的。由于快照数据是一份历史数据,是只读的,因此不须要上锁。
快照可能有多个版本,也就是说可能有多份不一样的快照数据,这种技术称为行多版本技术
,由此带来的并发控制称为多版本并发控制(Multi Version Concurrency Control, MVCC)
。
一致性非锁定读是InnoDB存储引擎默认的读取方式,可是不一样的事务隔离级别下,读取的方式不一样,并非每一个事务隔离级别下读取的都是一致性读,即便都是一致性读,不一样的事务隔离级别读取的快照数据也不一样。例如Read Commited和Repeatable Read下,使用的都是一致性的非锁定读,但它们读取的是不一样的快照数据。在Read Commited级别下,读取的老是被锁定行的最新一份快照数据,而在Repeatable Read级别下,读取的是事务开始时的第一份快照数据。atom
step 1. 初始化测试表 mysql> create table t (id int, -> primary key (id)) -> engine innodb; Query OK, 0 rows affected (0.62 sec) mysql> insert into t values(1); mysql> select * from t; +----+ | id | +----+ | 1 | +----+ 1 row in set (0.00 sec) step 2. 开启一个会话A,并在会话A中开启一个事务,查看下测试表的数据,但不提交事务 # Session A mysql> begin; Query OK, 0 rows affected (0.03 sec) mysql> select * from t; +----+ | id | +----+ | 1 | +----+ 1 row in set (0.00 sec) step 3. 开启另外一个会话B,模拟并发的状况,在会话B中开启事务,修改测试表中id为1的数据,但不提交 # Session B mysql> begin ; Query OK, 0 rows affected (0.00 sec) mysql> update t set id = 3 where id = 1; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 step 4. 在会话A中查看测试表数据,发现仍是修改以前的数据 # Session A mysql> select * from t; +----+ | id | +----+ | 1 | +----+ 1 row in set (0.00 sec) setp 5. 在会话B中提交事务 # Session B mysql> commit; Query OK, 0 rows affected (0.04 sec) step 5. 再在会话A中查看测试数据,在Read Commited和Repeatable Read级别下获得的结果就不同了。对于Read Committed级别,它老是读取最新版本,因此它获得的结果是一个id为3的记录(幻读)。而Repeatable Read级别下,它老是读取事务开始时的行数据,因此它获得的结果仍然是一个id为1的记录。
默认状况下,InnoDB存储引擎的Select操做使用的是一致性非锁定读,可是有些状况下,须要用户主动对读取操做进行加锁。InnoDB存储引擎对Select语句加锁有两种操做:spa
对于一致性非锁定读,即便读取的行已被使用Select ... For Update,也是能够读取的。Select ... For Update 和 Select ... Lock In Share Mode 必须在事务中使用,当事务提交了,锁也就释放了。
设计
InnoDB存储引擎有3种行锁算法:版本控制
Record Lock锁住的永远是索引而不是记录
,若是在建表的时候没有设置索引,InnoDB存储引擎会使用隐式的主键来进行锁定。多个事务同时修改同一行记录,可能出现丢失更新的问题。
能够看出,事务1的更新操做丢失了。避免丢失更新的作法,就是事务在查询数据的时候加上排他锁。
脏读是指事务A读到了事务B中尚未提交的更新(违反了数据库的隔离性)。脏读只在隔离级别为Read UnCommitted的状况下才会发生。避免脏读的办法,就是把事务的隔离级别至少设置成Read Committed。
幻读是指在一个事务中屡次读同一数据,拿到的结果不一样(违反了数据库的一致性)。例如,事务A读了一行数据,事务B修改了这行数据并提交了,事务A再读这行数据,拿到的结果与事务A前一次读取的结果不一样。 幻读通常是能够接收的,由于它读到的确实是其余事务已经提交的数据。 InnoDB存储引擎中,经过使用Next-Key Lock算法来避免幻读问题。在Next-Key Lock算法下,对于索引的扫描,不只仅锁住的是扫描到的索引,并且还锁住这些索引覆盖的范围(gap),所以在这个范围内的修改都是不容许的。