1.事务概念
事务是由一系列SQL组成的逻辑处理单元,要么全部执行,要不全不执行。事务有四个属性,如下:
原子性:事务是一个原子操作单元,其对数据的修改,要么全部执行,要么全不执行。
一致性:在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束时,所有的内部数据结构也都必须是正确的。
隔离性:数据库系统提供一定的隔离机制,保证事务不受外部并发操作影响的“独立环境”运行。这意味着事务处理过程中的中间状态对外是不可见的。
持久性:事务完成之后,它对于数据的修改是永久性的。
2.并发事务带来的问题
事务的运行常常是并发的,因为并发的事务执行效率高于串行执行,然而并发往往会带来大量的问题。
更新丢失:当多个事务同时更新同一行记录时,如果没有做限制的话,可能出现事务修改内容被覆盖(被覆盖的事务还没有提交)的情况。
脏读:一个事务对数据做了修改但是事务还没有提交,这时另一个事务读取了被修改的数据,就是发生了脏读。
不可重复读:一个事务读取某条数据后的某个时间,重复读取这条数据,得到了不一样的结果,这种情况叫做不可重复读。
幻读:一个事务相同的范围查询查询多次,每次读取的总记录数不一样,就是发生了幻读。
3.事务的隔离级别
“脏读”、“不可重复读”、“幻读“这些其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。数据库实现隔离机制主要有下面两种方式:
在读取数据前,对其加锁,阻止其他事务对数据进行修改。
不加任何锁,通过一定机制生成一个数据请求时间点的一致性数据快照,并用这个快照提供一定级别的一致性读取。对用户来说,就好像一条数据有多个版本一样,InnoDB管这个叫做MVCC(多版本并发控制)。
数据库的隔离级别越高,并发“副作用”越小,但是付出的代价也就越大。因为事务隔离的本质其实就是让事务在一定程度上“串行化”执行。下面表格是InnoDB四种隔离级别以及可能产生的“副作用”:
| 事务隔离级别 | 脏读 | 不可重复读 | 幻读 | 幻读 |
| 读未提交 | 是 | 是 | 是 | 是 |
| 读已提交 | 否 | 是 | 是 | 是 |
| 可重复读 | 否 | 否 | 是 | 是 |
| 串行化 | 否 | 否 | 否 | 否 |
MySQL遵循的是二段锁协议,将事务分成两个阶段,加锁阶段/解锁阶段。
加锁阶段:该阶段可以加锁,对任何数据读操作以前先申请读锁(其他事务可以再申请读锁,不能申请写锁),进行写操作之前申请写锁(其他食物不能申请任何锁)。加锁不成功,事务进入等待状态,直到加锁成功,才继续执行。
事务释放了一个封锁之后,事务进入解锁阶段,该阶段只能解锁,不能加锁。
不同隔离级别加锁操作:
读未提交,任何操作都不加锁。
读已提交,写操作写锁维持到事务结束。判断操作是否有索引,有索引的话只锁指定的数据行(Record Lock),没有的话是表锁。但是真要是表锁的情况,MySql会做一些改进,在MySQL Server过滤条件,发现不满足后,回调用unlock_row方法把不满足条件的记录锁释放掉,可重复读级别也是如此。
可重复读,读锁和写锁都是维持到事务结束。判断操作是否有索引,没有索引是表锁,有索引是next key lock。
串行化,任何操作都会加锁,而且都是表锁。
事务隔离需要注意的点:
事务隔离为读已提交时,写操作只会锁住对应的数据行。
隔离为可重复读时,写操作如果检索条件有索引的时候,默认加锁是next-key(record lock+gap lock),如果检索条件没有索引的情况,写操作是表锁。gap lock是为一个间隙加锁,这样的话,这个间隙就不能再插入记录了,可以防止幻读。
隔离为串行化时,读写都是表锁。





