文章目录
一. 概念
事务是数据库管理系统(DBMS)执行过程中的一个逻辑单位(不可进行分隔),由一个有限的数据库操作序列构成(多个DML语句,select语句不包含事务),要么全成功,否则都失败。
例如:
A赚钱给B,A账户-1000,B账户+1000。
2个update语句必须作为一个整体来执行,不然A扣钱,B没有加钱会导致数据问题。那么事务就可以保证A,B账户的变动要么全成功,或者全失败。
二. 特性
事务具有的4个属性:
-
原子性(atomicity):一个事务被视为一个不可分割的最小单元,整个事务中的所有操作要么全提交成功,要么全失败。对于一个事务来说,不能只执行其中的一部分操作。如果原子性不能保证,会出现一致性问题。
-
一致性(consistency):事务将数据库从一种一致性转换到另一种一致性,在事务的开始之前和结束之后,数据库中的数据的完整性没有被破坏。
-
隔离性(isolation):一个事务的执行不能被其他事务干扰。既一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。就比如2个事务,没有隔离的话,那同时读取数据如1000,2个事务需要同时修改减少100,那么最后修改后只会是900,如下图:
-
持久性(durability):事务提交后,则所做的修改就会永远保存到数据库中,此时系统崩溃,已经提交的修改数据也不会丢失。
三. 并发问题
MySQL是一个客户端/服务器架构的软件,对于同一个服务器来说,可以有若干个客户端与之连接,每个客户端与服务器连接之后,可称为一个会话(Session)。每个客户端都可以在自己的会话中向服务器发出请求语句,一个请求语句可能是某个事务的一部分,对于服务器而言,会产生同时处理多个事务。
事务的特性里包含一个隔离性,理论来说某个事务对数据访问时,其他事务应该排队,当该事务提交后,其他事务才可访问。这样的话并发事务的执行就变成了串行化执行。
但串行化执行,性能影响太大,但我们想保持事务的一定的隔离性,又想让服务器在处理访问同一数据的多个事务时,性能提高。当舍弃隔离性,会带来什么问题?
1. 脏读
当一个事务读取到了另外一个事务修改但未提交的数据的现象,称为脏读。
事务A进行数据资源的修改,修改后,因为没有隔离性,事务B读取到了事务A修改的数据。
事务A由于某些原因,事务A并没有完成提交,发生了RollBack操作,事务B此时读出来的数据就是脏数据。
2. 不可重复读
当事务内相同的记录被检索2次,且2次得到的结果不同时,称为不可重复读。
事务B进行2次数据资源的访问,在此过程中事务A进行数据资源的修改,导致事务B访问的数据资源前后不一致。
3. 幻读
在事务执行过程中,另一个事务将新记录添加到正在读取到的事务中时,会发生幻读。
事务B前后读取同一个范围的数据,在事务B两次读取的过程中事务A新增了数据,导致事务B后一次读取到前一次查询没有看到的行。
幻读和不重复读之间,幻读强调的是新增的数据资源,不可重复读强调的是现有资源的修改。
四. SQL标准的四种隔离级别
事务并发执行带来的问题,有轻重缓急之分,问题严重性排序:
脏读 > 不可重复读 > 幻读
之前所说事务特性中的隔离性
,舍弃一部分隔离性,换取一部分性能。
隔离级别越低,性能越好,问题约严重。
有一群人制定了所谓的SQL标准,在标准设立了4个隔离级别:
- READ UNCOMMITTED:未提交读
- READ COMMITTED:已提交读
- REPEATABLE READ:可重复读
- SERIALIZABLE:可串行化
SQL标准规定,针对不同隔离级别,并发事务可以发生不同严重程度的问题:
五. MySQL的隔离级别
不同数据库厂商对SQL标准中规定的四种隔离级别支持不一样,比如Oracle只支持READ COMMITTED和SERIALIZABLE隔离级别。MYSQL中支持了4个中隔离界别,但MySQL在REPEATABLE READ隔离级别下,可以紧致部分幻读问题。MySQL默认隔离级别为REPEATABLE READ。
1. 事务隔离级别设置
SQL语句
set [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL
level等级
level:{
READ UNCOMMITTED
|READ COMMITTED
|REPEATABLE READ
|SERIALIZABLE
}
设置事务隔离级别语句中,在SET关键字后可放置GLOBAL
,SESSION
或者什么都不放,这样会对不同范围的数据产生不同影响,如下:
GLOBAL
(全局范围):只对执行完该语句后的后续会话事务起作用,历史会话事务无效。SESSION
(会话范围):对当前会话的所有后续事务有效。对历史事务已经在执行中,开启SESSION
,不会影响历史事务,并只对后续事务生效。不使用关键字
(下个语句范围):只对执行语句后的下一个事务产生影响。下一个事务执行完后,后续事务将恢复之前的隔离级别。该语句不能在已经开启的事务中间执行,否则会报错。
如果想在服务器启动时,修改事务默认的隔离级别。下一个事务执行完后,后续事务将恢复到之前的隔离级别。该语句不能在已经开启的事务中间执行,否则报错。
查看当前会话默认的隔离级别:
-- 5.7.20版本以前,使用tx_isolation
SHOW VARIABLES LIKE 'transaction_isolation'
-- 或
select @@transaction_isolation;
六. MySQL 事务
该章节所用到的SQL
create table tx_a(
id int(10) primary key auto_increment,
val int(10)
)
1. 事务基本语法
开启事务:
begin;
-- 推荐
start transaction;
begin work;
事务回滚:
rollback;
事务提交:
commit
⭐️示例操作
-
开启事务 -> 插入数据 -> 提交
-
开启事务 -> 插入数据 -> 回滚
2. 保存点
开启一个事务后,执行了很多语句,发现其中语句是有问题,这时只能通过rollback进行回滚,让数据恢复到事务执行之前的状态,但有些业务,不需要全部回滚。
MySQL提出保存点
(savepoint)概念。事务对应的数据库语句打几个点,我们调用rollback语句时,可以指定回滚到哪个点,而不是回到最初的原点。
savepoint 保存点名称;
-- 不指定保存点名称,直接回滚事务执行之前状态
rollback to [savepoint] 保存点名称;
rollback to 保存点名称;
-- 删除某个保存点
release savepoint 保存点名称;
⭐️示例:
-
关闭自动提交
自动提交的坏处:
- 过于频繁的事务开始和提交会导致性能下降,因为每次事务的开始和结束都会产生额外的开销。
- 在
AUTOCOMMIT=1
的情况下,如果操作失败并且需要回滚,之前的操作可能已经不可回滚。 - 在
AUTOCOMMIT=1
的情况下,如果需要原子性的执行多个操作,不能保证在一个事务中。
-- 每一个SQL语句会被独立的事务自动提交,即每条SQL语句执行后都会立即提交。 show variables like 'autocommit'; select @@autocommit; --关闭自动提交 set autocommit = 'off';
-
开始测试:
3. 隐式提交
当开启一个事务后,或把autocommit
改为off
时,事务不会进行自动提交,如果我们输入某些语句之后就会悄悄的提交掉,就像输入commit
语句一样,这种因为某些特殊语句而导致事务提交的情况称为隐式提交。以下篇幅介绍各种隐式提交场景。
1)执行DDL
定义或修改数据库对象对象的数据定义语言(Data Definition Language,DDL)。
所谓数据库对象,指的是数据库,表,视图,存储过程等等。当我们使用create
,alter
,drop
等语句去修改这些所谓数据库对象时,会隐式提交前边语句。
2)隐式使用/修改MySQL表
当使用alter table
,create table
,drop table
,grant
,rename table
,revok
,set password
等语句,也会产生隐式提交。
3)事务控制/锁定语句
在开启的事务,还没提交或回滚时,又使用start transaction
开启另一个事务时,将导致隐式提交。
或者autocommit
开启,会隐式提交前边执行的语句(如果rollback
,则会重新回滚,消耗资源)。
或者使用LOCK TABLES
,UNLOCK TABLES
等语句,也会产生隐式提交。
4)加载数据语句
使用LOAD DATA
语句批量往数据库导入数据。
5)MySQL复制一些语句
使用START SLAVE
,STOP SLAVE
,RESET SLAVE
,CHANGE MASTER TO
等语句。
6)其他语句
使用ANALYZE TABLE
,CACHE INDEX
,CHECK TABLE
,FLUSH
,LOAD INDEX INTO CACHE
,OPTIMIZE TABLE
,RESET
等语句。