MySQL 事务
MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!
- 在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
- 事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
- 事务用来管理 insert,update,delete 语句
一般来说,事务是必须满足4个条件(ACID):原子性(Atomicity,或称不可分割性)
、一致性(Consistency)
、隔离性(Isolation,又称独立性)
、持久性(Durability)
。
-
原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
-
一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
-
隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
-
持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
在 mysql 命令行的默认设置下,事务都是自动提交的,即执行 sql 语句后就会马上执行 commit 操作。因此要显式地开启一个事务务须使用命令 begin 或 start transaction,或者执行命令 set autocommit=0,用来禁止使用当前会话的自动提交。
事务控制语句:
-
begin 或 start transaction 显式地开启一个事务;
-
commit 也可以使用 commit work,不过二者是等价的。commit 会提交事务,并使已对数据库进行的所有修改成为永久性的;
-
rollback 也可以使用 rollback work,不过二者是等价的。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;
-
savepoint identifier,savepoint 允许在事务中创建一个保存点,一个事务中可以有多个 savepoint;
-
release savepoint identifier 删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;
-
rollback to identifier 把事务回滚到标记点;
-
set transaction 用来设置事务的隔离级别。innodb 存储引擎提供事务的隔离级别有read uncommitted、read committed、repeatable read 和 serializable。
-
show variables like ‘t_isolation’查询事务的隔离级别
-
set session transaction isolation level 级别名设置事务隔离级别
savepoint 是在数据库事务处理中实现“子事务”(subtransaction),也称为嵌套事务的方法。事务可以回滚到 savepoint 而不影响 savepoint 创建前的变化, 不需要放弃整个事务。
rollback 回滚的用法可以设置保留点 savepoint,执行多条操作时,回滚到想要的那条语句之前。
使用 savepoint
savepoint savepoint_name; // 声明一个 savepoint
rollback to savepoint_name; // 回滚到savepoint
删除 savepoint
保留点再事务处理完成(执行一条 rollback 或 commit)后自动释放。
mysql5 以来,可以用:
release savepoint savepoint_name; // 删除指定保留点
MYSQL 事务处理主要有两种方法:
- 用 begin, rollback, commit来实现
begin 开始一个事务
rollback 事务回滚
commit 事务确认 - 直接用 set 来改变 mysql 的自动提交模式:
set autocommit=0 禁止自动提交
set autocommit=1 开启自动提交
脏读、不可重复读、幻读、第一类丢失更新、第二类丢失更新
-
脏读
事务A读到(就是select语句)事务B 尚未提交的修改(insert、delete、update)。脏读的后果:事务A读到事务B做的修改,如果事务B发生事务回滚,则事务A读取到的就是垃圾数据。(注:脏读的后果很严重)
e.g.
1.Mary的原工资为1000, 财务人员将Mary的工资改为了8000(但未提交事务)
2.Mary读取自己的工资 ,发现自己的工资变为了8000,欢天喜地!
3.而财务发现操作有误,回滚了事务,Mary的工资又变为了1000
像这样,Mary记取的工资数8000是一个脏数据。 -
不可重复读
同一个事务再次读取数据时,与之前读取到的数据不一致。(注:如果允许不重复读,则读取到的数据是不确定的。)
e.g.
1.在事务1中,Mary 读取了自己的工资为1000,操作并没有完成
2.在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.
3.在事务1中,Mary 再次读取自己的工资时,工资变为了2000
解决办法:如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题。
- 虚读(幻读)
事务A读取到where子句指定的数据,事务B加入了一行与事务A的where条件一样的数据。然后事务A再次使用相同的查询条件读取,此时事务A看到的可能就是事务B所添加的。
e.g.
目前工资为1000的员工有10人。
1.事务1,读取所有工资为1000的员工。
2.这时事务2向employee表插入了一条员工记录,工资也为1000
3.事务1再次读取所有工资为1000的员工 共读取到了11条记录,
解决办法:如果在操作事务完成数据处理之前,任何其他事务都不可以添加新数据,则可避免该问题
-
第一类丢失更新
撤销一个事务时, 把其他事务已经提交的更新数据覆盖(此情况在事务中不可能出现, 因为一个事务中修改时此记录已加锁, 必须等待此事务完成后另一个事务才可以继续UPDATE) -
第二类丢失更新
这是不可重复读中的特例, 一个事务覆盖另一个事务已提交的更新数据
不可重复读的重点是修改 :
同样的条件, 你读取过的数据,再次读取出来发现值不一样了
幻读的重点在于新增或者删除
同样的条件, 第 1 次和第 2 次读出来的记录数不一样
在一个程序中,依据事务的隔离级别将会有三种情况发生。
-
脏读:一个事务会读进还没有被另一个事务提交的数据,所以你会看到一些最后被另一个事务回滚掉的数据。
-
不可重复读:一个事务读进一条记录,另一个事务更改了这条记录并提交完毕,这时候第一个事务再次读这条记录时,它已经改变了。
-
幻读:一个事务用Where子句来检索一个表的数据,另一个事务插入一条新的记录,并且符合Where条件,这样,第一个事务用同一个where条件来检索数据后,就会多出一条记录。
数据库提供了四种隔离级别
因此数据库提供了四种隔离级别来解决上面的问题, 不同的隔离级别采用不同的锁类开来实现.
-
1.读未提交(read uncommitted 最低级别)
读取尚未提交的数据,哪一个问题都不能解决。 -
2.读已提交(read committed)
读取已经提交的数据,可以解决脏读。(oracle、sql Server默认) -
3.可重复读(repeatable read)
可以解决脏读和不重复读取。(mysql默认) -
4.串行化(serializable 最高级别)
可以解决脏读、不重复读、虚读。(相当于锁表)
事务的隔离级别越高,隔离性越强,所拥有的问题越少,并发能力越弱,所以,我们可以用如下表格进行总结
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted (未提交读) | √ | √ | √ |
Read committed (已提交读) | × | √ | √ |
Repeatable read(可重复读) | × | × | √ |
Serializable(可串行化) | × | × | × |
参考
MySQL 事务
MySQL事务
脏读、幻读和不可重复读 + 事务隔离级别
mysql/mariadb知识点总结(20):事务控制语句 (事务总结之二)
网友评论