事务
事务特性
原子性(A)
事务要么全部成功,要么全部失败,不会处于中间状态
一致性(C)
对数据有特定的预期状态,如非空,外键等等,这些更应该由应用层提供,数据库应该只用来保存数据而不处理具体逻辑
MySQL 官方手册所说是防止数据崩溃
根据论文作者评论所说 C 只是为了使 ACID 更加顺口,当时并不觉得有多么重要(来自 DDIA)
隔离性(I)
并发执行的多个事务之间需要一定的隔离级别来保证数据的正确性
持久性(D)
数据库需要保证事务一旦提交,就应该永久保存到磁盘中
重要日志
MySQL 采用了 WAL 技术,先写日志再写磁盘来保证数据的完整性
当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 Redo Log 里面,并更新内存,这个时候更新就算完成了
同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做
Redo Log(重做日志)
重做日志是一种基于磁盘的数据结构,有了 Redo Log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失
默认有两个文件 ib_logfile0
和 ib_logfile1
存放在磁盘上,MySQL 采用循环写的方式对文件进行顺序写入,当第一个文件写满时,会转向第二个文件,以此类推
innodb_flush_log_at_trx_commit
参数控制 Redo Log 的刷盘时机
- 0:每秒刷到到磁盘
- 1:每次事务提交时都刷新到磁盘
- 2:将重做日志异步写到磁盘,即写到文件系统的缓存中
Binary Log
Binary Log 是 MySQL Server 层维护的,记录的是对于表的逻辑更改,如插入、更新、删除操作,采用追加写的方式写入,主要用于主从复制、数据恢复
MySQL 通过 sync_binlog
参数控制 Binary Log 的刷盘时机,取值范围是 0 ~ N
- 0:不去强制要求,由系统自行判断何时写入磁盘
- 1:每次 commit 的时候都要将 binlog 写入磁盘
- N:每当 N 个事务提交后,才会将 binlog 写入磁盘
默认为 sync_binlog=1
是最安全的,但速度是最慢的
Undo Log
当对事务进行回滚的时候就需要用到 Undo Log
MySQL 会对每个 INSERT 语句生成对应的 DELETE 语句,UPDATE 对应相反的 UPDATE
提交流程
当事务执行后,会先写入 Redo Log 中,此时事务处于 Prepare 阶段,当 Binary Log 写入完成后会进行 Commit
事务隔离级别
隔离级别越高,并行性能越低,从而导致性能下降
查看 MySQL 的默认隔离级别,可以看到 MySQL 默认隔离级别为:可重复读
mysql> show variables like 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name | Value |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
读未提交
当事务还没提交时,其所做的更改就会被其它事务看到,会导致脏读(Dirty Read)
读提交(不可重复读)
当一个事务提交后,所做修改才会被其它事务所看到
该隔离级别是大多数数据库默认的,如 Oracle, PostgreSQL, SQL Server 等
可重复读
幻读:当某个事务 T1 读取某个范围内的记录时,另外一个事务 T2 又在该范围内插入了新的记录,则 T1 事务再次再次读取该范围内记录时,就会发生幻行,InnoDB 采用 MVCC 解决了幻读问题
串行化
对于某一行数据的读取和修改都会加锁,会严重影响性能
MySQL 采用两阶段提交(2PL)来实现可串行化隔离
幻读的解决
- 快照读:当使用 SELECT 语句不加行锁(S 锁,X 锁),会读取 MVCC 创建的快照
- 当前读:当使用当前读时,读操作已经加锁了,再使用快照读已经没有什么意义;当使用行锁时如
SELECT * FROM child WHERE id > 100 FOR UPDATE;
会使用 Next-Key Locking 锁定,阻塞其它获取锁的事务
MVCC
MVCC 也叫非锁定性一致读,可以允许进行读写操作不加锁,如果读取的行正在进行 INSERT
或 UPDATE
操作时,读取事务不会等待锁释放,而会去读取 InnoDB 创建的快照版本(可能会有多个版本),这样会大大提升并发性能,快照版本存放在 Undo Log 的回滚段中
只在以下两个隔离级别下工作:
- 在
READ COMMITTED
隔离级别下会读取锁定行的最新的快照版本 - 在
REPEATABLE READ
隔离级别下会读取事务开始时的快照版本
Next-Key Locking
当使用 SELECT * FROM child WHERE id > 100 FOR UPDATE;
时,不止会锁定 id=100
的行,而是锁定(100,+∞)之间的记录,防止别的事务对范围内数据进行修改
MySQL 事务启动方式
显示启动事务
begin
或 start transaction
配套的提交语句是 commit,回滚语句是 rollback。
set autocommit=0
这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit
或 rollback
语句,或者断开连接。
可以通过 select * from information_schema.INNODB_TRX;
查询当前数据库的所有事务
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。