ACID
- Atomicity:原子性(UNDO LOG实现),一组操作要么都成功,要么都失败
- Consistency:一致性(UNDO LOG实现),从一个合法状态变为另一个合法状态(语义上不是语法上)。比如转账之后余额为负数,虽然也能守恒,但是明显不合法。或者转账时A账户钱少了,B账户钱没多,也是不合法的
- Isolation:隔离性(锁机制实现),一个事务执行不受其他事务干扰(并发时事务是隔离的,互不干扰)
- Durability:持久性(REDO LOG实现),事务一旦提交不能再回滚
数据并发问题
- 脏写(Dirty Write),也叫修改丢失
- 事务A修改了事务B回滚的数据
- A 把 name 改成 张三,B把 name 改成李四,A 先提交,这时 name 就是张三,然后B回滚,name又改成最初的值了
- 脏读(Dirty Read)
- 事务A读取了事务B回滚的数据
- B 修改 name 为李四还没提交,此时A去读就会得到 name 是李四,等A读完,B回滚
- 不可重复读(Non-repeatable Read)
- 事务A读取了 name,事务B修改了 name(提交了),事务A再次读发现 name 不一样了。每次读取值都会不一样
- 这个讲道理是合法的,因为修改了值所以别人读到的值就不一样。但是这也确实是一个问题:每次读都不一样
- 幻读(Phantom)
- 事务A第一次读有1行数据,事务B插入了几行,事务A再次读,发现行数变多了
- 和不可重复度类似,讲道理是合法的,区别是一个是更新一个是插入导致
事务隔离级别
用来解决数据并发产生的问题,如果都要解决性能会受影响,所以需要平衡数据准确性和并发能力选择合适的级别
数据问题严重排序:脏写 > 脏读 > 不可重复读 > 幻读
隔离级别 | 脏读可能性 | 不可重复读可能性 | 幻读 | 加锁读 |
---|---|---|---|---|
READ UNCOMMITED(读未提交) | yes | yes | yes | no |
READ COMMITED(读已提交 oracle 默认) | no | yes | yes | no |
REPEATABLE READ(可重复读 mysql 默认) | no | no | yes | no |
SERIALIZABLE(串行化) | no | no | no | yes |
- 四种级别都能解决脏写
- SERIALIZABLE 串行化就把并行变成了串行所以能解决所有问题,并发能力最低
- 查看隔离级别
- 5.7.20 (包括)之前 : SHOW VARIABLES LIKE 'tx_isolation'
- 5.7.20之后: SHOW VARIABLES LIKE 'transaction_isolation'
- 所有版本都适用: SELECT @@transaction_isolation;
- 设置隔离级别
- set global transaction isolation level repeatable read;
- GLOBAL 和 SESSION 区别:GLOBAL 对新的连接生效,当前是无效的
事务日志
分为 redo 和 undo 日志,分别保证【持久性】和【原子性、一致性】
每次服务启动都会申请一片连续的内存空间作为 redo buffer,redo buffer 会刷到 redo log 中
redo
- 为什么需要 redo log?
- 保证数据的持久性
- 数据库和磁盘是以页为交互基本单位,哪怕该一个字段,也要读取一个完整的数据页,然后再刷盘,刷盘成本是很大的
- 可以不每次事务提交都刷盘,让保存在一个 redo log 中,后面再同步 redo log 和 磁盘(不需要每次dml都更新磁盘)
- redo log 数据来自于哪?
- 来自于 redo buffer
- redo buffer?
- 每次服务启动都会申请一片连续的内存空间作为 redo buffer
show variables like '%innodb_log_buffer_size%'
查看当前 redo buffer 大小,默认16m
- redo log 特点
- 是顺序写入的
- 事务执行过程中 redo log 不断记录(事务中有几条 dml 就记录多少次,bin log 是遇到 commit 记录所有的 dml,只记录一次)
- redo log 物理文件
- ib_logfile0 和 ib_logfile1
- 在 mysql 数据目录下
- redo log 流程
- 事务开始
- 读取数据到内存(将要修改的数据)
- 更新内存的数据保存在 redo buffer 中(修改后的数据)
- redo buffer 中的数据刷到 redo log 中(物理文件)
- 刷盘(有3中策略,通过 innodb_flush_log_at_trx_commit 参数确定,默认是 1 )
undo
- 为什么需要 undo log?
- 保证数据的原子性和一致性,回滚数据
- undo log 数据来自于哪?
- 来自于原来的数据
- 当需要删除时,会把删除前的数据写入 undo log;当需要更新时,把更新前的数据写入 undo log