目录
- 一、事务的概念
- 二、支持事务的存储引擎
- 三、事务的提交方式
- 三、事务的操作
- 四、事务的隔离级别
- 五、一致性
一、事务的概念
- 事务由一条或多条SQL语句组成,这些语句在逻辑上存在相关性,共同完成一个任务,事务主要用于处理操作量大,复杂度高的数据。比如转账就涉及多条说SQL语句,包括查询余额(select)、在当前账户上减去指定金额(update)、在指定账户上加上对应金额(update)等,将这多条SQL语句打包便构成了一个事务。
- MySQL同一时刻可能存在大量事务,如果不对这些事务加以控制,在执行时就可能会出现问题。比如单个事务内部的某些SQL语句执行失败,或是多个事务同时访问同一份数据导致数据不一致的问题。
因此一个完整的事务并不是简单的SQL集合,事务还需要满足如下四个属性:
- 原子性: 一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中如果发生错误,则会自动回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
- 持久性: 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
- 隔离性: 数据库允许多个事务同时访问同一份数据,隔离性可以保证多个事务在并发执行时,不会因为由于交叉执行而导致数据的不一致,
- 一致性: 在事务开始之前和事务结束以后,数据库的完整性没有被破坏,这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联型以及后续数据可以自发性地完成预定的工作。
上面的四个属性简称ACID:
- 原子性(Atomicity,又称不可分割性)。
- 一致性(Consistency)。
- 隔离性(Isolation,又称独立性)。
- 持久性(Durability)。
为什么会有事务
- 事务被MySQL编写者设计出来,本质是为了当前应用程序访问数据库的时候,事务能够简化我们的编程模型,不需要用户自己去考虑各种各样潜在的错误和并发问题。
- 如果MySQL只是单纯的提供数据存储服务,那么用户在访问数据库时就需要自行考虑各种潜在问题,包括网络异常、服务器宕机等。因此事务本质是为了应用服务的,而不是伴随着数据库系统天生就有的。
二、支持事务的存储引擎
通过指令 show engines; 可以查看支持事务的存储引擎
- Engine: 表示存储引擎的名称。
- Support: 表示服务器对存储引擎的支持级别,YES表示支持,NO表示不支持,DEFAULT表示数据库默认使用的存储引擎,DISABLED表示支持引擎但已将其禁用。
- Comment: 表示存储引擎的简要说明。
- Transactions: 表示存储引擎是否支持事务,可以看到InnoDB存储引擎支持事务,而MyISAM存储引擎不支持事务。
- XA: 表示存储引擎是否支持XA事务。
- Savepoints: 表示存储引擎是否支持保存点。
三、事务的提交方式
事务常见的提交方式有两种,分别是自动提交和手动提交。
通过show命令查看autocommit全局变量,可以查看事务的自动提交是否被打开。如下:
可以看到事务的自动提交方式默认是打开的。
我们可以通过手动设置的方式更改事务提交方式
注意:这里需要知道的知识点就是,单条sql也是一个事务。
这里设置的自动提交事务的方式只是影响单条sql语句的事务。
begin和start transaction开启的事务必须使用commit手动关闭。
三、事务的操作
先做一个准备工作,将mysql的事务隔离级别(后面学习)设置为 read uncommited,设置完重启mysql服务
创建一张员工工资表
再创建一个客户端,记得事务设置为读未提交后需要重新启动以下mysql客户端,这样配置才可以生效。然后再打开一个客户端
现在准备工作已经做好,先插入两条数据在表中,此时查看表数据
开始演示
开启事务的两种方式
方式1:
start transaction;
方式2:
begin;
我们演示使用begin;
总结:
- 事务开启方式使用start transaction 或者 begin
- 可以设置保存点,用来回退事务,savepoint s1(保存点)
- 使用rollback to命令回退到保存的事务点,
- 使用rollback 命令回退所有事务。
- 使用commit提交一条事务。
事务的原子性体现
当一个事务在没有提交就进行退出或者异常退出时,恢复对sql表的修改,体现了事务的原子性,要么就不做,要么就做完。
四、事务的隔离级别
- MySQL服务可能会同时被多个客户端进程(线程)访问,访问的方式以事务的方式进行。
- 一个事务可能由多条SQL语句构成,也就意味着任何一个事务,都有执行前、执行中和执行后三个阶段,而所谓的原子性就是让用户层要么看到执行前,要么看到执行后,执行中如果出现问题,可以随时进行回滚,所以单个事务对用户表现出来的特性就是原子性。
- 但毕竟每个事务都有一个执行的过程,在多个事务各自执行自己的多条SQL时,仍然可能会出现互相影响的情况,比如多个事务同时访问同一张表,甚至是表中的同一条记录。
- 数据库为了保证事务执行过程中尽量不受干扰,于是出现了隔离性的概念,而数据库为了允许事务在执行过程中受到不同程度的干扰,于是出现了隔离级别的概念。
数据库事务的隔离级别有以下四种:
- 读未提交(Read Uncommitted):在该隔离级别下,所有的事务都可以看到其他事务没有提交的执行结果,实际生产中不可能使用这种隔离级别,因为这种隔离级别相当于没有任何隔离性,会存在很多并发问题,如脏读。幻读、不可重复读等。
- 读提交(Read Committed): 该隔离级别是大多数数据库的默认隔离级别,但它不是MySQL默认的隔离级别,它满足了隔离的简单定义:一个事务只能看到其他已经提交的事务所做的改变,但这种隔离级别存在不可重复读和幻读的问题。
- 可重复读(Repeatable Read): 这是MySQL默认的隔离级别,该隔离级别确保同一个事务在执行过程中,多次读取操作数据时会看到同样的数据,即解决了不可重复读的问题,但这种隔离级别下仍然存在幻读的问题。
- 串行化(Serializable): 这是事务的最高隔离级别,该隔离级别通过强制事务排序,使之不可能相互冲突,从而解决了幻读问题。它在每个读的数据行上面加上共享锁,但是可能会导致超时和锁竞争问题,这种隔离级别太极端,实际生成中基本不使用。
说明一下:
- 虽然数据库事务的隔离级别有以上四种,但一个稳态的数据库只会选择这其中的一种,作为自己的默认隔离级别。但数据库默认的隔离级别有时可能并不满足上层的业务需求,因此数据库提供了这四种隔离级别,可以让我们自行设置。
- 隔离级别基本上都是通过加锁的方式实现的,不同的隔离级别对锁的使用是不同的,常见的有表锁、行锁、写锁、间隙锁(GAP)、Next-Key锁(GAP+行锁)等。
查看设置隔离级别
查看隔离级别
设置隔离级别
这里需要注意的地方时,当前会话的事务隔离级别默认是读取全局属性的事务隔离级别,
如果在当前会话修改全局属性事务隔离级别,当前会话事务隔离级别不会随之改变,
需要进行set设置或者重启mysql服务,再次读到的就是设置的全局属性的事务隔离级别。
下面进行演示四种隔离级别
读未提交(read uncommitted)
事实上我们在做前面实验的时候使用的就是读未提交的隔离级别。
准备:启动两个mysql服务,使用两个事务并发访问的方式进行测试
- 读未提交是事务的最低隔离级别,几乎没有加锁,虽然效率高,但是问题比较多,所以严重不建议使用。
- 一个事务在执行过程中,读取到另一个执行中的事务所做的修改,但是该事务还没有进行提交,这种现象叫做脏读。
读提交(Read Committed)
启动两个终端,将隔离级别都设置为读提交
一个事务在执行过程中,两个相同的select查询得到了不同的数据,这种现象叫做不可重复读。
可重复读(Repeatable Read)
启动两个终端,将隔离级别都设置为可重复读,可重复读也是mysql服务默认的一个事务隔离级别
- 在可重复读隔离级别下,一个事务在执行过程中,相同的select查询得到的是相同的数据,这就是所谓的可重复读。
- 一般的数据库在可重复读隔离级别下,update数据是满足可重复读的,但insert数据会存在幻读问题,因为隔离性是通过对数据加锁完成的,而新插入的数据原本是不存在的,因此一般的加锁无法屏蔽这类问题。
- 一般的数据库在可重复读情况的时候,无法屏蔽其他事务insert的数据(为什么?因为隔离性实现是对数据加锁完成的,而insert待插入的数据因为并不存在,那么一般加锁无法屏蔽这类问题),会造成虽然大部分内容是可重复读的,但是insert的数据在可重复读情况被读取出来,导致多次查找时,会多查找出来新的记录,就如同产生了幻觉。这种现象,叫做幻读(phantom read)。很明显,MySQL在RR级别的时候,是解决了幻读问题的(解决的方式是用Next-Key锁(GAP+行锁)解决的。
串行化(Serializable)
启动两个终端,将隔离级别都设置为串行化
串行化是事务的最高隔离级别,多个事务同时进行读操作时加的是共享锁,因此可以并发执行读操作,但一旦需要进行写操作,就会进行串行化,效率很低,几乎不会使用。
五、一致性
事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态,当数据库只包含事务成功提交的结果时,数据库就处于一致性状态。
- 事务在执行过程中如果发生错误,则需要自动回滚到事务最开始的状态,就像这个事务从来没有执行过一样,即一致性需要原子性来保证。
- 事务处理结束后,对数据的修改必须是永久的,即系统故障也不能丢失,即一致性需要持久性来保证。
- 多个事务同时访问一份数据时,必须保证这个多个事务在并发执行时,不能因为由于交叉执行而导致数据不一致,即一致性需要隔离性来保证。
- 此外,一致性与用户的业务逻辑强相关,如果用户本身的业务逻辑有问题,最终也会让数据库处于一种不一致的状态。
也就是说,一致性实际是数据库最终要达到的效果,一致性不仅需要原子性、持久性和隔离性来保证,还需要上层用户编写出正确的业务逻辑。