事务的概念
事务就是一组原始性的SQL操作,或者说一个独立的工作单元。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。
事务的特性
在MySQL中,只有InnoDB引擎支持事务,四种特性如下:
- 原子性(Atomicity) :一个事务必须被视为一个不可分割的最小工作单元,整个事务中的操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不能能只执行其中的一部分操作,这就是事务的原子性
- 一致性(Consistency) :数据库总是从一个一致性的状态转换到另外一个一致性的状态。
- 隔离性(Isolation):通常来说,一个事务所作的修改在最终提交以前,对其他事务是不可见的。
- 持久性(Durability):一旦事务提交,则其所作的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。
事务的隔离级别
SQL中定义了四种隔离级别,每一种隔离级别都规定了一个事务中所作的修改,哪些在事务内部和事务之间是可见的,哪些是不可见的。
下面说一下四种隔离级别
- READ UNCOMMINTTED(未提交读) :事务的修改即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读。除非有非常必要的理由,在实际应用中很少使用
- READ COMMITTED (提交读) : 也叫不可重复读,一个事务从开始到提交之前,所做的任务修改对其他的事务都是不可见的
- REPEATABLE READ(可重复读) :repeatable read 解决的脏读的问题。该级别保证了在同一个事务中多次读取同样的记录的结果是一致的。但理论上,可重读读隔离级别还是无法解决另外一个幻读问题。所谓 幻读 就是当某个事务在读取某个范围的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时会产生幻行。InnoDB 存储引擎通过 多版本并发控制(MVCC) 解决了幻读的问题。可重复读是MySQL默认的事务隔离级别
- SERIALIZABLE(可串行化) :serializable是最高的隔离级别,不支持并发。它通过强制事务串行执行,避免了幻读的问题。简单来说,serializable会在读取的每一行数据上都加上锁,所以可能导致大量的超时和锁争用的问题。实际应用中很少用到这个隔离级别,只有在非常需要确保数据一致性而且可以接受没有并发的情况下,才考虑采用
幻读和不可重复读的区别:
- 不可重复读的重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的数据不一样(因为中间有其他事务提交了修改)
- 幻读的重点在于新增或者删除:在同一事务中,同样的条件,第一次和第二次读出来的记录数不一样(因为中间有其他事务提交了删除或插入)
并发事务一般有哪些问题
- 更新丢失(Lost Update):当两个或多个事务选择同一行,然后基于最初选定的值更新该值时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题,最后的更新覆盖了由其他事务所作的更新
- 脏读(dirty reads):一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致的状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些脏数据,并据此做进一步的处理,就会产生未提交的数据依赖关系,这就叫脏读。
- 不可重复读:一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变,或某些记录已经被删除了,这就叫做不可重复读。
- 幻读:就是当某个事务在读取某个范围的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时会产生幻行。
多版本并发控制(MVCC):
多版本并发控制实现是通过保存数据在某个时间点的快照来实现的,也就是说,不管需要执行多长时间,每个事务看到的数据都是一致的。根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。
Innodb的mvcc是通过在每行记录后面保存两个隐藏的列来实现的,这两个列,一个保存了行的创建时间,一个保存行的过期时间。当然存储的并不是实际的时间值,而是系统版本号。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。
mvcc只在repeatable read和read committed两个隔离级别下工作。因为read uncommitted 总是读取最新的行,而不是符合当前事务版本的数据行。serializable 则会对所有读取的行都加锁。
下面看一下在repeatable read隔离级别下,mvcc具体是如何操作的:
-
SELECT
- innodb只查找版本早于当前事务版本的数据行(也就是行的系统版本号小于或等于事务的系统版本号),这样可以确保数据读取的行,要么实在事务开始前已经存在的,要么实在事务自身插入或者修改过的
- 行的删除版本要么未定义,要么大于当前的事务版本号。这个可以确保事务读取到的行,在事务开始之前未被删除。
- 只有符合上述两个条件的记录,才能作为作为查询结果
-
INSERT
- InnoDB为新插入的每一行保存当前系统版本号作为行版本号
-
DELETE
- InnoDB为删除的每一行保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除的标识。
mysql事务实现是基于数据库的存储引擎,不同的存储引擎对事务的支持程度不一样。MySQL中支持事务的存储引擎有innodb和ndb。事务的隔离性是通过锁实现的,而事务的原子性,一致性和持久性则是通过事务日志实现的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。