什么是事务
事务就是一组DML语句组成,这些语句在逻辑上存在相关性,这一组DML语句要么全部成功,要么全部失败,是一个整体。
为什么要有事务
1.事务是数据库维护数据一致性的基本单位,在每个事务结束时,都能保证数据一致性。
2.事务的提出主要是为了解决并发情况下保持数据一致性的问题。
如何使用事务
对于单挑SQL语句,会被MySQL自动包装成事务。
对于多条SQL语句,需要如下操作:
begin //或者 start transaction SQL1; (savepoint p1; 中途可以设置回滚点) SQL2; ... (rollback p1; 回滚到设置的回滚点,若未设置,则默认回滚到事务最开始的状态) commit;
事务的属性
原子性
一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
隔离性
隔离性是什么?
隔离性是MySQL的内部机制,让并发执行的各个事务,看到不同的数据修改(增删改),就叫做隔离性。
隔离级别是什么?
我们作为一个事务,可以看到不同可见性的数据,程度的不同,叫做隔离级别。
隔离级别解决的问题
脏读
一个事务执行时,读到了另一个事务未提交的数据。这种现象叫做脏读。
不可重复读
同一个事务内,同样的读取,在不同的时间段,读取到了不同的值。这种现象叫做不可重复读。重点是修改和删除。
幻读
幻读是不可重复读的一种特殊情况,重点在于新增。同样的条件,第一次和第二次读出来的记录数不一样。
这里的幻读仅指插入,不包含更新或删除后,查询到不一样的记录数。
一般的数据库在可重复读情况的时候,无法屏蔽其他事务insert的数据(为什么?因为隔离性实现是对数据加锁完成的,而insert待插入的数据因为并不存在,那么一般加锁无法屏蔽这类问题),会造成虽然大部分内容是可重复读的,但是insert的数据在可重复读情况被读取出来,导致多次查找时,会多查找出来新的记录,就如同产生了幻觉。这种现象,叫做幻读。很明显,MySQL在RR级别的时候,是解决了幻读问题的。
隔离级别分类
读未提交(Read Uncommited)
在该隔离级别,所有的事务都可以看到其他事务没有提交的执行结果。(实际生产中不可能使用这种隔离级别的),但是相当于没有任何隔离性,也会有很多并发问题,如脏读,幻读,不可重复读等。
读提交(Read Commited)
该隔离级别是大多数数据库的默认的隔离级别(不是 MySQL 默认的)。它满足了隔离的简单定义:一个事务只能看到其他的已经提交的事务所做的改变。这种隔离级别会引起不可重复读和幻读的问题。
可重复读(Repeatable Read)
这是 MySQL 默认的隔离级别,它确保同一个事务,在执行中,多次读取操作数据时,会看到同样的数据行。但是会有幻读问题(MySQL解决了该问题)。
串行化(Serializable)
这是事务的最高隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决了脏读、不可重复读和幻读等问题。它在每个读的数据行上面加上共享锁,但是可能会导致超时和锁竞争(这种隔离级别太极端,实际生产基本不使用)。
如何做到隔离级别?
MySQL通过MVCC机制实现了隔离级别。
为什么要存在隔离级别?
不仅仅是为了考虑安全问题,是在安全和效率之间,找平衡点。至于为什么存在多种隔离级别,是因为隔离级别的选择是由上层决定的,MySQL只是提供多种方案供上层选择。
安全体现在:数据具有隔离性,MySQL在RR级别下帮我们解决了脏读、不可重复读和幻读等问题,所以我们读到的数据是安全的。
效率体现在:在保证隔离级别的情况下,采用多版本并发控制(MVCC),让删,改,增加使用当前读,让读取使用快照读。对于同时进行的多事务而言时,因为隔离性的存在,写当前数据,读历史数据,使得当前读和快照读并发执行。
持久性
事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
一致性
在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
一致性不是一种具体的方案,而是事务维护的最终目标。由用户和MySQL共同决定
RC与RR的本质区别
三个列隐藏字段
-
DB_TRX_ID :6 byte,最近修改( 修改/插入 )事务ID,记录创建这条记录/最后一次修改该记录的事务ID。
-
DB_ROLL_PTR : 7 byte,回滚指针,指向这条记录的上一个版本(简单理解成,指向历史版本就行,这些数据一般在 undo log 中)。
-
DB_ROW_ID : 6 byte,隐含的自增ID(隐藏主键),如果数据表没有主键, InnoDB 会自动以 DB_ROW_ID 产生一个聚簇索引。
-
补充:实际还有一个删除flag隐藏字段, 既记录被更新或删除并不代表真的删除,而是删除flag变了。
undo log
MySQL 中的一段内存缓冲区,用来保存日志数据。
Read View
-
决定一个事务的可见性的数据结构,记录一个事务能看到那些历史版本的数据。
-
一个事务启动,一旦形成Read View,Read View如果不变,该事务可以看到的历史版本也就是确定的。一旦我们发生了快照读,读视图得到了确认,后续的快照读就会依赖首次出现的快照读。即某个事务中首次出现快照读,决定该事务后续快照读结果的能力。
ReadView是MySQL底层中的一个类,其结构的核心字段如下:
读的策略源码如下:
RR级别下
当一个事务首次 select(不加锁的快照读)时,会形成快照及Read View。一旦形成Read View,就不会再更新。所以RR级别下,没有不可重复读和幻读的问题。
RC级别下
一个事务在每次读快照时,都会新生成一个快照和Read View。所以RC级别下,会存在不可重复读的问题。