是数据库区别于文件系统的重要特性之一,用于保证数据库的完整性,数据库会从一种一致性状态转换为另一种一致性状态。
1、四大特性
(1) 原子性
数据库的每个事务都是不可分割的单元,只有事务中的所有 SQL 语句都执行成功,才算整个事务成功,如果事务中有一个 SQL 语句执行失败,整个事务都将回滚。
(2) 一致性
数据库从一种一致性状态转换为另一种一致性状态,即数据库的完整性约束没有被破坏,要么都是新数据,要么都是老数据。
(3) 隔离性
一个事务的影响在该事务提交之前对其他事务都是不可见的,通过锁机制来实现。
(4) 持久性
事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
2、语法:
在 MySQL 命令行默认设置下,事务是自动提交的,即执行了 SQL 语句之后马上会执行 commit 操作,可以通过 set autocommit = 0 来禁用当前会话的自动提交。
(1) 开始事务
begin 或 start transaction
(2) 提交事务
commit,即 commit [work]
(3) 事务回滚
rollback,即 rollback [work]
(4) 定义保存点
savepoint xxx,一个事务中可以有多个保存点
(5) 删除保存点
release savepoint xxx,保存点不存在时会抛出异常
(6) 回滚到某个保存点
rollback to [savepoint] xxx
回滚到某个保存点并不能让事务结束,要提交事务或回滚事务。
3、InnoDB 的实现
隔离性由锁机制实现,原子性、一致性、持久性都是通过数据库的 redo 日志和 undo 日志来完成。
redo 日志:重做日志,它记录了事务的行为;
undo 日志:对数据库进行修改时会产生 undo 日志,也会产生 redo 日志,使用 rollback 请求回滚时通过 undo 日志将数据回滚到修改前的样子。
InnoDB 存储引擎回滚时,它实际上做的是与之前相反的工作,insert 对应 delete,delete 对应insert,update 对应相反的 update。
ps:
1、commit 和 commit work 的区别
commit work 可以控制事务接收后的行为,可以用 select @@completion_type 查看值,默认是0。
completion_type = 0,两者等价。
completion_type = 1,commit work 等价于 commit and chain,表示马上开启一个相同隔离级别的事务。
completion_type = 2,commit work 等价于 commit and release,表示事务提交后自动断开与服务器的连接。
2、隐式提交
有些语句会造成隐式提交,主要有3类:
(1) DDL 语句:create event、create index、alter table、create database、truncate 等等,所有 DDL 语句都是不能回滚的。
(2) 权限操作语句:create user、drop user、grant、rename user 等等。
(3) 管理语句:analyze table、check table 等等。
3、隔离级别
通过锁机制实现,大部分数据库都没有提供真正意义上的隔离性。
数据库制造商在标准和性能之间进行了权衡,MySQL 还是比较贴近该标准的,ISO 和 ANSI SQL 标准了四种事务隔离级别的标准。
可以用 select @@tx_isolation 来查看它的值。
(1) read uncommitted
隔离级别最低,开销也是最小,目前没有数据库会设置它为默认隔离级别,因为会出现脏读。
脏读:读到未提交的数据(脏数据),n 事务中读到 m 事务中修改了,但未提交的数据,即在不同事务下可以读到其它事务未提交的数据。
严格来说,它已经破坏了事务的隔离性。
(2) read committed
它不允许在别的事务未提交时读取到别的事务的修改数据,容易出现不可重复读,但是可以被人们接受,因为事务已经提交,数据已经持久化到磁盘了,隔离级别越低,事务请求的锁就越少,或者保持锁定的时间就越短,这也是大多数数据库默认的事务隔离级别都是 read committed 的原因。
不可重复读:在一个事务中多次读同一个数据,由于其他事务在第一个事务还没结束时修改了数据,造成第一个事务中两次读到的数据不一致。
脏读与不可重复读:脏读读取的是未提交的数据,不可重复读读到的是已提交的数据。
(3) repeatable read
InnoDB 存储引擎默认支持的隔离级别。不会出现脏读、不可重复读和幻读(InnoDB 避免了该问题)。
(4) serializable
SQL 和 SQL2 标准的默认事务隔离级别,它是真正意义上的隔离,当然性能也是最差。
设置事务隔离级别:
set [global|session] transaction isolation level {read uncommitted|...};
比如:set tx_isolation='read-committed';