目录
事务定义
事务是数据库管理系统(DBMS)执行过程中的一个逻辑单位(不可再进行分割),由一个有限的数据库操作序列构成(多个 DML 语句),要不全部成功,要不全部不成功
DDL (DataDefinitionLanguage)︰数据定义语言,用来定义数据库对象︰库、表、列等
DML(DataManipulationLanguage)︰数据操作语言,用来定义数据库记录(数据)
DQL (DataQueryLanguage):数据查询语言,用来查询记录(数据)
DCL (DataControlLanguage)︰数据控制语言,用来定义访问权限和安全级别
例如:
A 给 B 要转账,A 的账户-1000 元, B 的账户就要+1000 元,这两个 update 语句必须作为一个整体来执行,不然 A 扣钱了,B 没有加钱这种情况就是错误的。那么事务就可以保证 A 、B 账户的变动要么全部一起发生,要么全部一起不发生
事务特性
事务应该具有以下 4 个属性,这四个属性通常称为 ACID 特性
- 原子性(atomicity)
- 一致性(consistency)
- 隔离性(isolation)
- 持久性(durability)
原子性(atomicity)
一个事务必须被视为一个不可分割的最小单元,整个事务中的所有操作要么全部提交成功,要么全部失败,对于一个事务来说,不能只执行其中的一部分操作。比如上面的经典例子:
A 给 B 要转账,A 的账户-1000 元, B 的账户就要+1000 元,这两个 update 语句必须作为一个整体来执行,不然 A 扣钱了,B 没有加钱这种情况就是错误的。那么事务就可以保证 A 、B 账户的变动要么全部一起发生,要么全部一起不发生
一致性(consistency)
一致性是指事务将数据库从一种一致性转换到另外一种一致性状态,在事务开始之前和事务结束之后数据库中数据的完整性没有被破坏,例如上面例子中的账户总和是不变的,也就是一致的
隔离性(isolation)
一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰
上面的例子在多线程条件下会有并发问题,MySQL就是通过隔离性来控制并发问题的
持久性(durability)
一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,已经提交的修改数据也不会丢失
事务并发引起的问题
理论上在某个事务对某个数据进行访问时,其他事务应该进行排队,当该事务提交之后,其他事务才可以继续访问这个数据,这样的话并发事务的执行就变成了串行化执行
但是对串行化执行性能影响太大,我们既想保持事务的一定的隔离性,又想让服务器在处理访问同一数据的多个事务时性能尽量高些,这样根据隔离性的不同会引发下面的问题
脏读
当一个事务读取到了另外一个事务修改但未提交的数据,被称为脏读
不可重复读
当事务内相同的记录被检索两次,且两次得到的结果不同时,此现象称为不可重复读
幻读
在事务执行过程中,另一个事务将新记录添加到正在读取的事务中时,会发生幻读
SQL 标准中的四种隔离级别
MySQL 中的隔离级别
不同的数据库厂商对 SQL 标准中规定的四种隔离级别支持不一样,比方说 Oracle 就只支持 READ COMMITTED 和 SERIALIZABLE 隔离级别,MySQL 虽然支持 4 种隔离级别,但与 SQL 标准中所规定的各级隔离级别允许发生的问题却有些出入,MySQL 在 REPEATABLE READ 隔离级别下,是可以禁止幻读问题的发生的
MySQL 的默认隔离级别为 REPEATABLE READ,我们可以手动修改事务的隔离级别
如何设置事务的隔离级别
我们可以通过下边的语句修改事务的隔离级别:
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level;
其中的 level 可选值有 4 个:
- REPEATABLE READ
- READ COMMITTED
- READ UNCOMMITTED
- SERIALIZABLE
设置事务的隔离级别的语句中,在 SET 关键字后可以放置 GLOBAL 关键字、SESSION 关键字或者什么都不放,这样会对不同范围的事务产生不同的影响,具体如下:
使用 GLOBAL 关键字(在全局范围影响)
例如:
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
只对执行完该语句之后产生的会话起作用。当前已经存在的会话无效
使用 SESSION 关键字(在会话范围影响)
例如:
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
对当前会话的所有后续的事务有效
该语句可以在已经开启的事务中间执行,但不会影响当前正在执行的事务;如果在事务之间执行,则对后续的事务有效
不用关键字(只对执行语句后的下一个事务产生影响)
例如:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
只对当前会话中下一个即将开启的事务有效。下一个事务执行完后,后续事务将恢复到之前的隔离级别。该语句不能在已经开启的事务中间执行,会报错的
修改默认MySQL隔离级别
如果我们在服务器启动时想改变事务的默认隔离级别,可以修改启动参数 transaction-isolation 的值,比方说我们在启动服务器时指定了
--transaction-isolation=SERIALIZABLE
那么事务的默认隔离级别就从原来的 REPEATABLE READ 变成了 SERIALIZABLE
查看当前会话默认的隔离级别可以通过查看系统变量 transaction_isolation 的值来确定:
SHOW VARIABLES LIKE 'transaction_isolation';
使用下面的简写命令也可以:
SELECT @@transaction_isolation;
transaction_isolation 是在 MySQL 5.7.20 的版本中引入来替换 tx_isolation 的,如果使用的是之前版本的 MySQL,请将上述用到系统变量 transaction_isolation 的地方替换为 tx_isolation
MySQL 事务
事务基本语法
事务开始
- begin
- START TRANSACTION(推荐)
- begin work
事务回滚
rollback
事务提交
commit
保存点
如果你开启了一个事务,执行了很多语句,忽然发现某条语句有点问题,你只好使用 ROLLBACK 语句来让数据库状态恢复到事务执行之前的样子,然后一切从头再来,但是可能根据业务和数据的变化,不需要全部回滚。所以 MySQL 里提出了一个保存点(英文:savepoint)的概念,就是在事务对应的数据库语句中打几个点,我们在调用 ROLLBACK 语句时可以指定会滚到哪个点,而不是回到最初的原点。定义保存点的语法如下:
SAVEPOINT 保存点名称;
当我们想回滚到某个保存点时,可以使用下边这个语句(下边语句中的单词 SAVEPOINT 是可有可无的):
ROLLBACK TO [SAVEPOINT] 保存点名称;
不过如果 ROLLBACK 语句后边不跟随保存点名称的话,会直接回滚到事务执行之前的状态不过如果 ROLLBACK 语句后边不跟随保存点名称的话,会直接回滚到事务执 行之前的状态
如果我们想删除某个保存点,可以使用这个语句:
RELEASE SAVEPOINT 保存点名称;
隐式提交
使用 START TRANSACTION 或者 BEGIN 语句开启了一个事务,或者把系统变量 autocommit 的值设置为 OFF 时,事务就不会进行自动提交,但是如果我们输入了某些语句之后就会悄悄的提交掉,就像我们输入了 COMMIT 语句了一样,这种因为某些特殊的语句而导致事务提交的情况称为隐式提交,这些会导致事务隐式提交的语句包括:
- 执行 DDL
- 隐式使用或修改 mysql 数据库中的表
使用 ALTER USER、CREATE USER、DROP USER、GRANT、RENAME USER、REVOKE、SET PASSWORD 等语句时也会隐式的提交前边语句所属于的事务
- 事务控制或关于锁定的语句
- 在一个会话里,一个事务还没提交或者回滚时就又使用 START TRANSACTION 或者 BEGIN 语句开启了另一个事务时,会隐式的提交上一个事务
- 当前的 autocommit 系统变量的值为 OFF,我们手动把它调为 ON 时, 也会隐式的提交前边语句所属的事务
- 使用 LOCK TABLES、UNLOCK TABLES 等关于锁定的语句也会隐式的提交 前边语句所属的事务
- 加载数据的语句
使用 LOAD DATA 语句来批量往数据库中导入数据时,也会隐式的提交前边语句所属的事务
- MySQL 复制的一些语句
使用 START SLAVE、STOP SLAVE、RESET SLAVE、CHANGE MASTER TO 等语句时也会隐式的提交前边语句所属的事务
- 其它的一些语句
使用 ANALYZE TABLE、CACHE INDEX、CHECK TABLE、FLUSH、LOAD INDEX INTO CACHE、OPTIMIZE TABLE、REPAIR TABLE、RESET 等语句也会隐式的提交前边语句所属的事务