Page
InnoDB minimum store unit is page, and every page size is 16KB
Buffer Pool:
InnoDB会在启动时申请一块连续的内存(用户空间), 用于做CPU和磁盘的缓冲:
可能会用到mmap, 这样少一次内核空间与用户空间之间的拷贝
Index
索引使用B+ 树, 每个节点约有1200个叉, 树高为4时(算上根)就是17亿条数据
B+树只有叶子节点保存数据
B+树比哈希表的优点
- 范围查询快
- 支持最左原则
索引分为
- 主键索引(聚簇索引)
- 非主键索引(非聚簇索引)
日志
binlog
binlog主要用于 主从复制 和 数据回放
Mysql的server层日志, 只有写(DDL DML)命令才会新增binlog, 且只有事务提交时才会将全部命令写到binlog
binlog has three format:(nomarly use MIXED)
STATEMENT: store original sql order, 问题是SQL中的不确定函数(比如time())回放时会有问题, 优点是空间占用小ROW: store changed sql data, 问题是占用的空间大, 优点是回放精准MIXED: 不确定函数用ROW(likeDATE()), 其他用STATEMENT
About Binlog Format : www.cnblogs.com/xinysu/p/66…
redolog
用于崩溃后的恢复和IO性能优化
因为一个事物可能修改到多个buffer pool page, 每个page又会随机读写不同磁盘, 如果直接写磁盘会有严重的IO瓶颈, 为了提高性能, 事务会顺序写redolog并更新内存, 然后会在操作系统空闲时将命令写到磁盘中以提高性能
redolog的数据储存格式是“对一个页做了什么修改”(物理日志)
redolog是Innodb的引擎层日志, the structure of redolog is a circle(four 1GB file):
2PC:
UPDATE A SET C=C+1 WHERE ID=2
Mysql would add
begincommitas default
Use words to describe 2PC:
server层的执行器调innodb的接口读到ID=2这一行record(innodb会先看buffer pool再读磁盘)- 执行器在内存把这行
record加1, 然后调innodb的写接口,innodb会将新的record更新到buffer pool, 然后写入redolog并写入磁盘, 此时redolog文件里的这个事务标记为prepare状态 - 执行器在收到
innodb写接口返回后将命令写入binlog并写入磁盘 - 执行器执行
commit命令, 调用innodb接口,innodb给redolog文件写入commited状态
这样crash safe的处理就是:
- 如果崩溃时没有写入
binlog, 恢复时redolog里的日志处于prepare状态则直接回滚 - 如果崩溃时写入
binlog, 但redolog还没有commit, 则恢复时重新提交commit转为commited状态 (因为写入binlog说明很可能已经主从复制用到了)
undolog
上面提到
crash safe时可能会回滚事务, 回滚便依赖于undolog
实际上, undo log提供了两个作用: 事务回滚和MVCC; undo log is a logic log which stores sql order:
〉 undolog内容会写到redolog中
ACID
REF: https://byte_dance.fei_shu.cn/wiki/wikcnq0RKomhKAVQGb4ZOXPTZtf
Isolation Level
isolation has four level:
read uncommited: transaction A can read transaction B's uncommited record.read commited: transaction A can only read transaction B's commited record.repeated read: InnoDB默认隔离级别, 一个事务中多次读同一个SQL结果是一样的seriablized: 纯串行执行
What's phantom read?
幻读是事务A第一次读没有行C, 第二次就读到行C, 这是因为其他事物可能插入行C
Consistency
一致性是指事务执行前后数据库完整性约束没有被破坏, AID保证了C
dirty read & phantom read都能破坏Consistency
MVCC & Lock
Lock
从粒度上分:
- 表锁: Generally lock in
serverlevel - 行锁: lock in
innodblevel
从兼容性来分:
- 共享锁:
select xxx from xxx lock in share mode - 排他锁:
select xxx from xxx for update
并发读和写
当前读和快照读
In innodb only read commited and repeatable read has two read modes:
当前读指的就是使用锁来读:
select xxx from xxx lock in share modeselect xxx from xxx for update
快照读指不使用上面两种方式来读的都是快照度, 因为本质是读不同的副本, 都不是同一份资源所以不用加锁
In innodb the read uncommited level 用的就是不加锁的当前读
In innodb the serialization level 用的是table lock
写
写包括insert update delete 这些操作都是加X锁的
MVCC
多版本并发控制
上文的快照读在innodb中指的就是MVCC(多版本并发控制), 一种无锁的并发读实现方案, 同理, MVCC只存在于read commited & repeatable read
对于reapeatable read来说, MVCC还能阻止phantom read, 因为只能读到小于等于当前事务ID的record(当前事务开启后, 再新插入的行的事物ID肯定大于当前ID
高可用
MySQL的高可用性解决方案目前大致分为6种,按照高可用的级别(99.9999%为最高级)排序依次为,主从复制、具有自动故障转移功能的主从复制、利用共享存储、OS或虚拟化软件实现主备架构、MySQL Group Replication 群组复制,以及 MySQL NDB Cluster
主从复制
Master-Slave Replication, ensure 99% availability
As traditional master-slave replication, master doesn't care about whether slave received binlog. So there would be a case that master has commited the transaction and hasn't send binlog to the slave, so this binlog would be lost.
To solve this problem and improve availability, we can use semi-syncronous replication
半同步复制指的是指不仅master要运行事务, 还要确保至少有一个slave也已经收到了这个事务的binlog
异地多活
参考这篇文章 kaito-kidd.com/2021/10/15/…
Abase异地多活的实现 https://tech.byte_dance.net/articles/7096127791057338404?from=lark_all_search
分库分表
分库
分库解决的是并发量过大问题, 数据库同时链接的句柄太多了
分表
分表解决的是数据量过大问题, 存储和IO遇到了瓶颈, 也就是单次查询性能不够了
横向分表:
技术难点是如何实现全局ID:
- UUID
- 设定自增步长
- ID分区, 比如0-1000, 1001-2000...
纵向分表:
讲不同的列分到不同的表
缓存一致性
看这篇文章: kaito-kidd.com/2021/09/08/…
先更新数据库, 再删除缓存
为了解决第二步失败(这里指删除缓存失败), 需要订阅binlog数据流(binlog发到mq里), 在另一个系统里来做删redis缓存的操作
延迟双删: 解决的问题是: 主库更新了A=新, 然后删除了缓存A, 但从库还没同步导致A=旧, 此时有读请求, 读完A=旧, 并更新到了缓存使A=旧, 然后主库同步才更新到从库, 使从库A=新, 这就导致了从库的A和缓存的A数据不一致
解决的方法是: 「延迟双删」:除了binlog删除缓存之外, 再起一个延时消息, 过一段时间后再删一次缓存