Mysql 技术内幕读书笔记

Mysql 技术内幕读书笔记

数据库:文件系统上的文件集合 实例:由后台线程和共享内存区组成的进程 架构:单进程多线程 配置读取:以最后一个配置文件为准 特点:插件式的表插件引擎

各种存储引擎

InnoDB

行锁设计,支持外键,默认读取操作不会产生锁 MVCC多版本并发控制获得高并发性

MyISAM

不支持事务,表锁设计,支持全文索引 缓存池只缓存索引,不缓存数据文件

连接数据库

本质是连接进程和数据库实例间的进程通信 因此支持:TCP,管道,命名管道,UNIX域套接字等方式

TCP连接的时候,数据库会先检查权限视图进行鉴权,表名为 user

InnoDB 存储引擎

架构: 多个后台线程 + 内存池 | 文件

后台线程的主要作用是刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据,此外将已修改的数据刷新到磁盘文件

Master Thread 数据异步刷新到磁盘,保证数据一致性

IO Thread 处理异步写IO请求的回调

Purge Thread 回收事务提交之后不再使用的 undo 页

Page Cleaner Thread 将脏页的刷新操作都放到单独的线程中完成

缓冲池可以类比内核的页高速缓存 页刷新回磁盘使用 checkpoint 机制 池中页的类型:索引页,数据页,undo页,undo页,插入缓冲,自适应哈希索引,锁信息,数据字典信息

缓冲池实例有多个,每个页根据哈希值平均分配到不同缓存,减少数据库内部资源竞争。缓冲池页的默认大小是 16KB,使用 LRU 管理

全表扫描可能会导致 LRU 列表被污染

checkpoint

用于解决:

  1. 缩短数据库恢复时间
  2. 缓冲池不够用将脏页刷新回磁盘
  3. 重做日志不可用,刷新脏页

数据库宕机的时候不需要重做所有日志,只需要对 checkpoint 后的重做日志进行回复

脏页太多导致不够用的时候会强制执行 checkpoint 刷回磁盘

重做日志需要重用的时候,触发 checkpoint

两种类型:

Sharp Checkpoint :数据库关闭时将所有脏页都刷新回磁盘 Fuzzy Checkpoing :只刷新一部分脏页,整个过程异步进行 检测磁盘 IO 负载情况,负载较低,可以进行刷新

插入缓冲 Insert Buffer

插入聚集索引(primary key)一般是顺序(自增)的,不需要磁盘随机读取,因此插入速度快 其他的辅助索引是非聚集的,因为 B+树的结构而导致插入的时候需要随机读取

对于非聚集索引的插入:

  1. 判断插入的非聚集索引页是否在缓冲池,在,则直接插入
  2. 不在,则先暂存写请求,然后再通过一定频率进行插入缓冲辅助索引页子节点进行合并,此时通常能将多个插入合并到一个操作中(因为在一个索引页中),大大提高了插入的性能

同样的对于删除和修改都有都应的 buffer 来进行缓冲

内部实现使用了 B+树,存放在共享表空间中,负责对所有表的辅助索引进行插入缓冲

两次写

doublewrite 会同时写一个页的副本来防止数据丢失,写入失效发生时,先通过页的副本来还原该页,再进行重做

异步IO

最大的优势就是可以进行 IO Merge 提高 IO 性能 内核原生支持 脏页刷新,磁盘的写入,read ahead方式的读取都是通过 AIO 完成

索引

iostat 出现 100% 磁盘使用率,可能是添加了过多的索引

B+树索引

这种索引并不能找到一个给定键值的具体行,只能找到被查找数据行所在的页,然后数据库通过把页读到内存,再在内存中进行查找

B+树在保持平衡的时候会在插入的时候进行大量的页拆分,因此 B+树可以进行类似旋转操作来尽量减少页拆分

层数一般保持在 2-4层,机械磁盘每秒至少可以做100次IO,2-4次的IO意味着查询时间大约在 0.02 - 0.04 秒

聚集索引与辅助索引的不同在于叶子节点存放的是否是一整行的信息

聚集索引

叶子节点存放整张表的行记录数据,叶子节点称为数据页,每个数据页都通过一个双向链表来进行链接

辅助索引

叶子节点除了键值之外,包含一个指向行主键值的指针。因此辅助索引找到数据页所需要的 IO 次数为 辅助索引层数 + 聚集索引层数

堆表:按照插入顺序存放。此时的指针可以是行标识符,可以更快的定位到行数据

B+树索引适用情况:在访问表中很少一部分的时候适用,高选择性的字段才能加索引。高选择性看 Cardinality 值,它表示索引中不重复记录数量的预估值

联合索引

对多个字段同时索引,也是一颗 B+ 树。叶子节点的键值组会进行排序,如果是 (a,b)方式存放,那么 where a = xx 也可以使用这个索引,而 where b = xx 不行是因为无序,只有在确定了第一列的情况下的叶子节点的第二列是有序的

覆盖索引

从辅助索引中就得到查询的记录,而不需要查询聚集索引中的记录,大小远小于聚集索引,可以减少大量 IO 操作

优化器不选择索引?

多发生于范围查找,JOIN联表的情况

自适应哈希索引 AHI

适用于等值查找 引擎观察到建立哈希索引可以带来速度提升,则建立哈希索引。 自动根据访问频率和模式来自动地为某些热点页建立哈希索引

条件:

  1. 对这个页的连续访问模式必须是一样的
  2. 以该模式访问了 100 次
  3. 页通过该模式访问了 N 次

倒排索引

用于全文搜索,使用红黑树来作为全文检索索引缓存,作用类似于插入缓存

latch:轻量级的锁,要求锁定时间必须非常短,无死锁检测和处理,仅通过加锁顺序保证无死锁,存在于每个数据结构对象中 | 互斥量和读写锁

lock:针对的对象是事务,用来锁定数据库中的对象,如表,页,行 一般的 lock 仅在事务 commit 或 rollback 后释放,与事务隔离级别有关,有死锁检测和处理。存在于 lock manager 的哈希表中

行级锁

共享锁 S Lock,允许事务读一行数据 排他锁 X Lock,允许事务删除或更新一行数据 只有 S Lock 之间是兼容的 InnoDB 支持多粒度锁定,允许事务在行级上的锁和表级上的锁同时存在

意向锁:将锁定的对象分为多个层次,意味着事务希望在更细粒度上进行加锁 意向共享锁 IS Lock:事务想要获得一张表中某几行的共享锁 意向排他锁 IX Lock:事务想要获得一张表中某几行的排他锁

加锁层次: 数据库 | 表1, 表2 … | 页1,2… | 记录 1,2….

如果要对记录1上 X 锁,那么就得对它的所有父辈都上意向排他锁 如果有其他锁存在且不兼容,那么就需要等待

一致性非锁定读

通过多版本控制的方式来读取当前执行时间数据库中行的数据。如果当前行在执行更新或删除 ,这时读取操作不会等待行锁的释放,而是直接读取行的快照数据

在默认的事务隔离级别 READ COMMITED 和 REPEATABLE READ 下默认是非锁定的一致性读,前者的总是读取最新版本的快照数据,后者总是读取事务开始时的行数据版本

一致性锁定读

某些情况下我们需要数据逻辑的强一致性,语句级别支持一致性锁定读 SELECT … FOR UPDATE (加 X 锁) SELECT … LOCK IN SHARE MODE (加 S 锁,其他事务加 X 锁会阻塞) 必须在事务中使用

自增长

在 AUTO INCREMENT 的列中存在对自增长计数器的锁,是一种特殊的 表锁,在完成自增长插入后立刻释放,新版本中使用轻量级的互斥量来实现

外键

InnoDB会自动为外键列加索引,因为这样可以避免表锁 外键值的插入或更新会首先查询父表中的记录,因此要进行锁定读防止数据不一致

行锁

  1. 行记录锁 Record Lock:单个行记录上的锁
  2. 间隙锁 Gap Lock:锁定一个范围,但不包含记录本身
  3. Next-key Lock:前两者的结合,锁定一个范围并且锁定记录本身

对于行的查询一般都是使用第 3 种查询

幻读问题

指在同一个事务下,连续执行两次相同的SQL语句可能导致不同的结果,第二次SQL语句可能会返回之前不存在的行。 违反了事务的隔离性,一个事务可以看到另一个事务的结果

使用 Next-key Locking 解决,不仅锁定值而且锁定查询的范围,对范围加了 X 锁,因此对这个范围的修改都是不允许的,从而解决了问题 在 REPEATABLE READ 事务隔离级别下采用

脏读

脏数据:事务对缓冲池中的行记录进行了修改,但还没有提交。指未提交的已修改数据 如果被另外一个事务读到未提交的数据,那么就会违反数据库的隔离性 发生条件:事务隔离级别为 READ UNCOMMITTED 应用场景:从节点的副本,而且在该节点额查询并不需要特别精确的返回值

不可重复读

与脏读的区别为:脏读读到的是未提交的数据,不可重复读读到的是已提交的数据 一般来说不可重复读是可以接受的,因为其已经提交 级别:READ COMMITTED 下允许

丢失更新

一个事务的更新导致另一个事务的更新操作被覆盖,从而导致数据不一致 解决:事务操作串行化

死锁

暴力方法:任何等待都回滚并重新开始,但是性能差

超时机制:等待时间超过某一阈值,其中一个事务回滚,另一个等待的事务继续 等待图:DFS 死锁检测,更为主动,存在回路即发现死锁,选择回滚 undo 量最小的事务

锁升级

InnoDB没有锁升级,根据每个事务访问的每个页对锁进行管理,采用位图。不管多少个锁的开销都是一致的

事务

要么所有修改都已经保存,要么都不保存 A 原子性 C 一致性,事务开始之前和结束之后,数据库的完整性约束没有被破坏 I 隔离性 D 持久性,事务一旦提交,其结果就是永久性的,即使宕机也能恢复数据

扁平事务,带保存点的扁平事务 链事务 嵌套事务 分布式节点,不同节点之间运行事务

实现

redo:包含重做日志缓存和重做日志文件,用来保证持久性,顺序写 undo:帮助事务回滚及MVCC,需要随机读写

确保缓冲写日志文件,每次都需要调用一次 fsync,防止内容停滞在文件系统缓冲区 日志文件以块(512字节)进行存储,超过了就进行分割 恢复的时候仅考虑 checkpoint 开始的日志部分

事务隔离级别

READ UNCOMMITTED 浏览保护(仅针对事务) READ COMMITTED 无幻读保护 REPEATABLE READ 解决幻读 SERIALIZABLE

隔离级别越低,事务请求的锁越少或保持锁的时间就越短

备份与恢复

  • 热备:在线备份
  • 冷备:离线备份
  • 温备:在线备份加锁

完全备份,增量备份,日志备份