目录
事务介绍
事务概念
比如在进行CRUD时:
客户端A检查时还有一张票,于是将票卖掉,但还未去更新数据库,此时客户端B检查时也发现还有一张票,于是也将票卖掉。这便出现了问题。
事务由一条或多条SQL语句组成,这些语句在逻辑上存在相关性,共同完成一个任务这一组语句要么全部成功,要么全部失败,是一个整体。并且MySQL同一时刻可能存在大量事务,如果不对这些事务加以控制,在执行时就可能会出现问题。
一个完整的事务,绝对不是简单的 sql 集合,还需要满足如下四个属性:
原子性: 一个事务( transaction )中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback )到事务开始前的状态,就像这个事务从来没有执行过一样。一致性: 在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。隔离性: 数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读 未提交( Readuncommitted )、读提交( read committed )、可重复读( repeatable read )和串行化( Serializable )【后面介绍】持久性: 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
可以通过show engines命令查看数据库引擎
Support: 表示服务器对存储引擎的支持级别,YES表示支持,NO表示不支持,DEFAULT表示数据库默认的存储引擎
Transactions: 表示存储引擎是否支持事务,可知MyISAM不支持事务。
事务提交方式与演示
事务常见的提交方式有两种,自动提交与手动提交。
其中ON表示自动提交被打开,值为OFF表示自动提交被关闭,为手动提交。
可通过set autocommit=0,=1 的方式进行设置
在读未提交的情况下进行演示:
读为提交:一个事务在执行过程中,读取到另一个执行中的事务所做的修改
进行设置: set global transaction isolation level read uncommited;
设置全局隔离级别后当前会话的隔离级别不会改变,只会影响后续与MySQL新建立的连接,需要重启终端才能看到会话的隔离级别被成功设置 。
创建一个简单的学生表,进行操作。
常规操作(读未提交)
左边窗口开启一个事务,此时表中无数据,右边查看也是无数据。
左边插入一条数据,此时未提交,右边也可直接查看数据
左终端中的事务使用savepoint命令创建一个保存点,然后继续向表中插入一条记录,右边也可查看
左终端中的事务使用rollback命令回滚到保存点,那么右边事务中第二条记录会被删除
若回滚到最开始,则右端看不到任何数据:
原子性
若启动一个事务,在提交之前因为某些原因与MySQL断开连接,那么MySQL会自动让事务回滚到最开始,右端看不到任何数据。
持久性
若启动一个事务,在插入一些数据后,最后进行提交,那么右端一定可以看到插入的数据。
使用begin或start transaction命令,可以启动一个事务。
使用savepoint 保存点命令,可以在事务中创建指定名称的保存点。
使用rollback to 保存点命令,可以让事务回滚到指定保存点。
使用rollback命令,可以直接让事务回滚到最开始。
使用commit命令,可以提交事务,提交事务后就不能回滚了。
使用begin或start transaction命令启动的事务,都是要使用commit命令手动提交,数据才会被持久化,与是否设置autocommit无关。
实际全局变量autocommit是否被设置影响的是单条SQL语句,若autocommit为ON,则单条SQL语句执行后会自动被提交,如果为OFF,则SQL语句执行后需要使用commit进行手动提交。但是InnoDB中的每一条SQL都会默认被封装成事务。
例如:
当autocommit设置为on时:
左边插入数据,右边一定可以看到,左终端在执行单条SQL后不使用commit进行提交,而直接与MySQL断开连接,右端依旧可以看到数据,因为单条SQL在执行后被自动提交持久化了。
当autocommit设置为off时,退出时未提交:
退出时进行提交:
所以之前我们所使用的都是单SQL事务,autocommit默认是打开,语句执行后就被提交了。
事务的隔离级别
读未提交【 Read Uncommitted 】 : 在该隔离级别,所有的事务都可以看到其他事务没有提交的 执行结果。(实际生产中不可能使用这种隔离级别的),但是相当于没有任何隔离性,也会有很多并发问题,如脏读,幻读,不可重复读等,我们上面为了做实验方便,用的就是这个隔离性。读提交【 Read Committed 】 :该隔离级别是大多数数据库的默认的隔离级别(不是 MySQL 默认的)。它满足了隔离的简单定义: 一个事务只能看到其他的已经提交的事务所做的改变。这种隔离级别会引起不可重复读, 即一个事务执行时,如果多次 select , 可能得到不同的结果。可重复读【 Repeatable Read 】 : 这是 MySQL 默认的隔离级别, 它确保同一个事务,在执行中,多次读取操作数据时,会看到同样的数据行,但是会有幻读问题。串行化【 Serializable 】 : 这是事务的最高隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决了幻读的问题。它在每个读的数据行上面加上共享锁 。但是可能会导致超时和锁竞争(这种隔离级别太极端,实际生产基本不使用)
查看与设置隔离级别
select @@global.tx_isolation:
可以查看全局隔离级别
select @@session.tx_isolation或select @@tx_isolation:可以查看当前会话隔离级别
set session transaction isolation level 隔离级别 可以设置当前会话的隔离级别
set global transaction isolation level 隔离级别 可以设置全局隔离级别(设置全局隔离级别只会影响后续的会话,当前会话不受影响)
读未提交(Read Uncommitted)
在上面《事务提交方式与演示》中,采用的就是读未提交,一个事务未提交,另一个事务就可以看到所作的修改,这也叫做脏读。
读提交(Read Committed)
启动两个终端,将隔离级别都设置为读提交,A事务在对数据做修改时且未提交时,B事务无法看到被修改的数据,只有当A事务提交后,B事务才能看到修改后的数据。B事务在执行过程中,两个相同的select查询得到了不同的数据,这种现象叫做不可重复读。
可重复读(Repeatable Read)
启动两个终端,将隔离级别都设置为可重复读,在两个终端中都启动一个事务。在左边终端中插入数据后,右边终端依旧看不到新插入的数据,左边终端提交后也是一样。
只有当右边终端commit后,右端才能看到左边终端所做的修改
在可重复读隔离级别下,一个事务在执行过程中,相同的select查询得到的是相同的数据,这就是所谓的可重复读。一般的数据库在可重复读隔离级别下,update数据是满足可重复读的,但insert数据会存在幻读问题,因为隔离性是通过对数据加锁完成的,而新插入的数据原本是不存在的,因此一般的加锁无法屏蔽这类问题。
一个事务在执行过程中,相同的select查询得到了新的数据,如同出现了幻觉,这种现象叫做幻读。
串行化(Serializable)
在两个终端中都启动一个事务,如果这两个事务都对表进行的是读操作,那么这两个事务可以并发执行,不会被阻塞。若有一个事务要进行写操作,那么会被阻塞,直到另一个事务提交后,才会被唤醒。