-
MySQL分层
-
接入层 -> MySQL服务层 -> 存储引擎层 -> 系统文件层
-
接入层: 不同语言客户端通过MySQL协议与MySQL服务器连接通信, 该层负责权限验证, 连接池管理
-
MySQL服务层: SQL解析器, SQL优化器, 缓存
-
存储引擎层, mysql服务器中对数据的读取和写入是交给存储引擎来处理的
-
系统文件层: 保存数据, 索引, 日志
-
-
binlog (归档日志)
-
逻辑日志, binlog是MySQL Server的日志, 用于主从同步, 数据复制 (如MySQL至ES)
-
同步
-
异步复制: 从库会开启"IO线程", 不断读取主库binlog, 并写入中继日志(relay log). 从库"SQL线程"会读取并顺序执行中继日志, 与主库保持一致
-
全同步复制: 主库在响应客户端事务提交前保证所有从库接收并写入中继日志
-
半同步复制: 主库在响应客户端事务提交前保证至少一个从库接收并写入中继日志
-
-
binlog三种模式
-
statement模式
-
记录数据修改的SQL (增删改)
-
优点是binlog量很小, 缺点是函数很难同步, 比如last_insert_id()
-
-
row模式
-
记录每一行记录在修改前后的数据
-
优点是避免statement模式下函数无法同步的问题, 缺点是binlog量很大
-
-
mixed模式
混用statement模式与row模式
-
-
-
事务日志
-
redo log与undo log是innodb引擎的日志, 用于实现事务
-
redo log用于前滚操作, 保证事务的持久性. undo log用于回滚操作与MVCC, 保证事务的原子性, 一致性
-
前滚与回滚
-
前滚
未完全提交的事务, 即该事务已被commit, 但该事务所涉及的数据中只有一部分落盘, 另一部分还在内存中. 此时数据库崩溃, 恢复时需要利用前滚, 使得该事务涉及的数据完全落盘
-
回滚
未提交的事务, 即该事务未被commit
-
-
redo log (重做日志)
-
物理日志, 记录某个数据页上的所有修改, 用于崩溃处理
-
事务提交时, MySQL将事务修改的所有数据落盘来保证持久性. innodb是以页为单位与磁盘交互的. 事务提交时, 将所有修改落盘, 会影响性能, 故产生redo log. 这种对记录修改先写日志, 再写磁盘的技术, 就是WAL技术.
-
有了redo log, innodb就能保证提交的记录永丢失, 这种能力叫做crash-safe
-
redo结构
-
redo log包括内存中的"redo log 缓冲区"与磁盘上的"redo log 文件". SQL执行后, 数据页上的修改先入"redo log 缓冲区", 再入磁盘上的"redo log 文件". 缓冲区就是用来提高性能, 减少IO. 缓冲区刷新到磁盘有三种时机: 每秒/事务提交时/日志缓存可用空间少于一半时.
-
redo log占用的空间是固定的, 其通过循环队列的方式将redo log里的数据修改落盘.
-
-
并发的多个事务共享redo log, 当一个事务提交时, redo log会落盘. 此时会将其他未提交事务的日志也一并落盘.
-
二阶段提交
-
redo log与binlog必须保持一致, 否则两日志不一致最终会导致主从不一致. 由此引出二阶段提交, 其可以保证redo log与binlog一致. 二阶段提交即是将redo log的写入拆成prepare与commit两个阶段. 二阶段提交是经典的分布式问题
-
事务在提交时, 分为"时间点1" -> "prepare阶段" -> "时间点2" -> "commit阶段" -> "时间点3"
-
prepare阶段, 当前事务中新生成的redo log会被刷入磁盘, redo log中将该事务标记为prepare状态
-
commit阶段, 释放innodb锁, binlog刷入磁盘, redo log中将该事务标记为commit状态
-
故障模拟
-
时间点1故障: redo log与binlog都在内存中, 重启后相当于事务回滚
-
时间点2故障: redo log已落盘, redo log为prepare阶段, 但binlog不确定. 需判断binlog是否完整, 完整则redo log至为commit, 提交事务, 否则回滚事务
-
时间点3故障: redo log与bin log都在磁盘中, 无影响
-
-
-
崩溃恢复
数据恢复时, 会重做redo log中所有记录, 包括未提交的事务, 然后通过undo log回滚那些未提交的事务
-
-
undo log (回滚日志)
-
逻辑日志, 存放记录被修改前的值, 如果修改出现异常, 可使用undo log来回滚, 保证事务的原子性与一致性. undo log还是实现多版本并发控制的关键
-
一条insert, 对应一条delete的undo log; 一条update/delete, 对应一条相反的update的undo log
-
undo log被看作数据, undo log也会被记录到redo log中, 这样就保证了undo log的持久化
-
undo log/redo log/事务
假设有字段为key, val的数据表. key=A时, val=1; key=B时, val=2. 现在把key=A时, 值修改为10; key=B时, 值修改为20
1) 事务开始
2) 记录A=1到undo log
3) 修改A=10 (内存)
4) 记录A=10到redo log
5) 记录B=2到undo log
6) 修改B=20 (内存)
7) 记录B=20到redo log
8) 事务提交 (redo log写入磁盘)
-
-
-
MVCC
-
MVCC即"多版本并发控制". 通过维护数据的多个历史版本, 来解决并发访问下数据一致性的问题. 读操作时, 读到的数据是某个历史版本, 写操作时, 不覆盖已有数据, 而是创建一个新版本. 不同的事务仅看到自己特定版本的数据
-
与MVCC相对应的是"锁的并发控制", MVCC的并发能力远优于"锁的并发控制" (悲观锁)
-
实现
-
表中每行记录后面都有两个隐藏字段: trx_id和roll_pointer
-
trx_id: 创建该行时的事务id (事务id是不停自增的)
-
roll_pointer: 回滚指针, 创建该行时的undo log地址
-
当该行记录被多次修改后, 该行记录的undo log就会串联起来形成一个"版本链". 如果隔离级别是"读取未提交内容", 查询时只要读取版本链中最新版本记录即可; 如果隔离级别是"读取已提交内容"或"可重复读", 则需要遍历"版本链"中每条记录, 根据trx_id判断该记录对当前事务是否可见, innodb中通过readview实现该功能, "读取已提交内容"和"可重复读"生成readview的时机不同, 造成了不同的隔离效果
-
-