mysql技术内幕_MySQL技术内幕

本文摘自《MySQL技术内幕 InnoDB存储引擎》,由腾讯T4数据库总监姜承尧撰写。主要内容涵盖InnoDB存储引擎、索引、锁、事务、复制和RAID。InnoDB支持事务、行级锁和外键,适合OLTP;MyISAM则适用于OLAP。文章详细讨论了InnoDB的存储结构,如Redo Log、缓冲池、插入缓冲、自适应哈希索引等,并介绍了事务提交流程、锁机制、不同隔离级别的差异。此外,还涉及到了数据库复制和RAID技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

f21d8855680e15986e9c4b645a9ccec4.png

内容摘取自<MySQL技术内幕 InnoDB存储引擎>by姜承尧,作者目前为腾讯T4的数据库总监,本文为书本内容的子集。

内容列表:

  • InnoDB存储引擎
  • 索引(index)
  • 锁(lock)
  • 事务(transaction)
  • 复制(replication)
  • RAID

名词解释:

  • 数据库:文件的集合,frm,ibd等;
  • 数据库实例:程序,位于用户与OS之间的一层数据管理软件
  • OLTP:在线事务处理 OnLine Transaction Processing
  • OLAP:在线分析处理 OnLine Analytical Processing
  • Dirty Page:脏页,内存页和磁盘页数据不一致
  • Redo Log:重做日志
  • LSN:序列号,Log Sequence Number
  • FRM:FoRMat,InnoDB表定义文件
  • IBD:InnoBase Data,InnoDB数据文件

事务提交流程:Write Ahead Log策略:当事务提交时,先写Redo Log,再修改页。

  • 先记录 undo/redo log,确保日志刷到磁盘上持久存储。(确保2/3宕机不丢数据/主从一致)
  • 更新数据记录,缓存操作并异步刷盘。
  • 将事务日志持久化到 binlog。
  • 提交事务,在 redo log 中写入commit记录。

InnoDB存储引擎

69daaab7efc82571651e06dc4cb08cb9.png
  1. 存储引擎
  • 存储引擎是基于表的,而不是数据库。数据库还包括连接池 / Parser / Optimizer / File System等。
  • MySQL数据库重要特点之一就是其插件式的表存储引擎
  • InnoDB存储引擎支持事务,行粒度锁,外键聚集索引;支持mvcc;主要处理OLTP。
  • MyISAM存储引擎不支持事务,表粒度锁,支持空间数据;无崩溃恢复;查询效率更高(唯一优势,且不明显);主要处理OLAP。

fc5b64bbfd7ade80fe78966646ddcda7.png

2.InnoDB存储引擎

  • Redo Log先放到Redo Log Buffer,然后按一定策略到文件系统的Redo Log文件或磁盘innodb_flush_log_at_trx_commit。
  • 分区partition:不同子分区生成不同的idb文件,如t#P#p0.ibd, t#P#p1.ibd;分区类型包括Range(值范围),List(指定值),Hash(用户表达式),Key(数据库提供的哈希函数)。
  • 缓冲池(innodb_buffer_pool)
    • 不包含重做日志Redo Log
    • 页的修改操作,首先修改缓冲池中的页,再以一定机制/频率刷回磁盘。
    • 包含:数据页,索引页,锁信息,插入缓冲,自适应哈希索引
    • 页的大小默认为16KB,使用LRU算法对缓冲池进行管理
    • 插入缓冲Insert Buffer
      • 对于非聚集索引的插入或更新,由于数据不在一个索引页,所以效率底
      • 先放入insert buffer,再以一定频率合并操作,通常在一个索引页
    • 自适应哈希Adaptive Hash Index
      • 如果观察到建立哈希索引可以带来速度提升,则通过B+树构造
      • 将3次查询减少到1次
    • 伙伴算法进行页的管理
      • 从缓冲池中申请4KB大小的页
      • 检查4KB的LRU列表,若有,则直接使用
      • 检查8KB的LRU列表,若有,分成两个4KB的页,放到其LRU列表
      • 检查16KB的LRU列表,若有,分成一个8KB,两个4KB的页,放入对应LRU
  • CheckPoint:将某种策略脏页刷回磁盘的机制
      • Async/Sync Flush:
          • size = 重做日志LSN - 已经fsync LSN
          • sync: size > 90% * 总重做日志
          • async: size > 75% * 总重做日志
      • FLUSH_LRU_LIST:当空闲页小于阈值时fsync
      • Dirty Page Too Much:当脏页大于阈值时强制fsync
  • 多线程模型
    • Master Thread:
      • 将缓存写到磁盘(flush to disk),即CheckPoint执行;每秒;详情可参考事务后文中的redo log部分。
      • 合并插入缓冲Merge Insert Buffer。
      • 等等其他下列thread不做的。
    • IO Thread:insert buffer thread / log thread / read thread / write thread
    • Purge Thread:回收已经使用并分配的undo log页
    • Page Cleaner Thread:脏页刷新

索引(index)

a0d090f3c1845f5cede593ad145d1d62.png

1.B+树

  • B+树中的B代表的不是Binary,而是Balance。
  • B+树索引并不能找到一个给到键值的具体行,只是被查找数据行所在的页;数据库把整页读到内存中,再在内存中找到具体的行。
  • 平衡二叉树:二叉查找树+任何节点的两个子树的高度最大差为1。
  • 数据库中,B+树的高度一般是2~4层,查找某一记录的行记录时最多只需要2到4次IO,一般的机械磁盘每次IO大概为10毫秒。

2.索引方式

  • 聚集索引(clustered index):按照每张表的主键构造一棵B+树,同时叶子节点存放的即是整张表的整行记录数据。
  • 辅助索引/非聚集索引(secondary index / non-clustered index):构造一棵B+树,叶子节点不存储数据,而是存储聚集索引中的位置。
  • 如果在高度为3的辅助索引树中查找数据,需要6次IO(3次找到主键+3次查聚集索引树)。
  • 联合索引:对多个列进行索引,比如(a,b)。适用于where a=xxx and b=xxx 和 where a=xxx,但是不适用于where b=xxx。
  • 一张表中包含一个聚集索引构成的B+树,以及若干辅助索引构成的B+树。

锁(lock)

1.乐观锁和悲观锁

  • 乐观锁:是一种思想,并不是真正意义上的锁;尝试对资源进行修改,再回写时判断资源是否进行了更改;如果没有发生改变就写回,否则回滚;整个执行过程都没有对数据库进行加锁。在事务隔离级别Read Committed和Repeatable Read下,InnoDB存储引擎使用一致性非锁定读,使用undo log mvcc实现。
  • 悲观锁:在获取资源前对资源进行加锁,确保同一时刻只有有限的线程可以访问该资源,其他想要尝试获取资源的操作进入等待状态,其他前面操作释放锁。
  • 选择:
    • 乐观锁适用:并发量高,响应速度快;读多写少
    • 悲观锁适用:回滚成本高,冲突频率大

2.InnoDB中的锁

  • 锁可以实现事务的隔离性需求,使得事务可以并发地工作,提高并发度。
  • InnoDB锁的级别为行,悲观锁。
  • 共享锁:S Lock,(Shared)允许事务读一行数据。
  • 排他锁:X Lock,(eXclusive)允许事务删除或更新一行数据;经实验,其他事务仍可以读。
  • select ... for update:对读取的行记录加X锁
  • select ... lock in share mode:对读取的行记录加S锁
  • 只有S Lock和S Lock可以共存;S & X,X & X皆不可共存。
  • 读意向锁:IS Lock,Intention Shared Lock:表级别锁,不会阻塞,只表示有人在读表。
  • 写意向锁:IX Lock,Intention eXclusive Lock:表级别锁,不会阻塞,只表示有人在写表。
  • 意向锁在全表扫描类操作中可以提升效率。

3.锁的算法

  • Record Lock:行锁
  • Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
    • 对于select * from t where a>2 for update
    • 对范围(2, 正无穷)加了X锁
    • 可以避免幻读
  • Next-Key Lock:Record Lock + Gap Lock:没有找到翻译。

4.锁带来的问题

  • 脏读:读到未提交的数据
    • Session A: select * from t; // {id=1,a=1}
    • Session B: insert into t select 2;
    • Session A: select * from t; // {id=1,a=1}{id=2,a=2}
  • 不可重复读:读到已经提交的数据,但是违法了数据库事务一致性要求。
    • Session A: select * from t; // {id=1,a=1}
    • Session B: update t set a=2 where id=1;
    • Session B: commit;
    • Session A: select * from t; // {id=1,a=2}
  • 幻读:与不可重复读区别为update vs insert。
    • Session A: select * from t; // {id=1,a=1}
    • Session B: insert into t select 2;
    • Session B: commit;
    • Session A: select * from t; // {id=1,a=1}{id=2,a=2}
  • 更新丢失:略,任何隔离级别更新时加锁不会发生。

5.死锁

  • 解决方法:将任何等待化为回滚;超时;等待图(wait-for graph)检测
  • innodb_lock_wait_timeout默认为50s。

事务(transaction)

1.特性ACID

  • 原子性 Atomicity:redo log实现;要么都做,要么都不做
  • 一致性 Consistency:undo log MVCC实现;逻辑上正确
  • 隔离型 Isolation:undo log MVCC/lock实现;concurrency control / serializability / locking
  • 持久性 Durability:redo log实现;数据库宕机时通过redo log来恢复

2.事务的实现

45a289568ff18e43e9dda6f74420f876.png
  • Redo Log:保证事务原子性和持久性
    • 物理日志,记录的是页的物理修改操作。
    • redo块(block)大小和磁盘扇区一样,都是512B。
    • innodb_flush_log_at_trx_commit=0:事务提交时不fsync,等待main thread
    • innodb_flush_log_at_trx_commit=1:事务提交时必须调用一次fsync(性能最差/默认)
    • innodb_flush_log_at_trx_commit=2:事务提交时写入文件系统缓存;与0的区别:MySQL宕机不会丢数据;0和2操作系统宕机都会丢数据。
    • redo log vs binlog
      • redo log速度比binlog速度快很多
      • binlog:用来恢复recovery,复制replication(更精简,利于网络间传送),审计audit(分析判断是否有注入攻击)
      • binlog产生于数据库上层;redo log产生于存储引擎
      • binlog记录逻辑日志,对应SQL语句;redo log是物理格式日志,记录每个页的修改
        • 比如表CREATE TABLE t (a INT, b INT, PRIMAR KEY(a), KEY(b))
        • SQL语句:INSERT INTO t SELECT 1, 2;
        • Redo Log:
          • page(2, 3), offset 32, value 1,2 // 聚集索引
          • page(2, 4), offset 64, value 2 // 辅助索引
      • binlog只在事务提交完成后写入磁盘;redo log每个事务对应多条日志
  • Undo Log:保证事务一致性
    • 逻辑日志
    • 帮助事务回滚
      • 对于delete,执行insert
      • 对于insert,执行delete
      • 对于update,执行相反update
    • 实现MVCC
      • 记录历史操作
      • delete不会删除数据,会先打上flag。purge进程完成最终清理记录。
  • LSN:Log Sequence Number,日志序列号
    • Log sequence number:当前LSN
    • Log flushed up to:刷新到redo log文件系统的LSN,数据库宕机,OS没宕机可从此恢复
    • Last checkpoint at:刷新到磁盘的LSN

3.隔离级别

  • Read Uncommitted => 不加锁;问题:脏读(读到未提交的行)
  • Read Committed => 乐观锁;undo log MVCC实现,读取被锁定行,最新一份快照数据;问题:不可重复读(值前后读取不一样,被其他事务修改)
  • Repeatable Read => 乐观锁;undo log MVCC实现,读取被锁定行,事务开始时的数据;问题:幻读(有新的值插入,被其他事务新增)
  • Serializable => 加悲观锁S Lock或间隙锁Gap Lock / Next-Key Lock

4.隔离级别说明

  • InnoDB存储引擎在Repeatable Read隔离级别下,使用Next-Key Lock,避免幻读,效果上等于Serializable。
  • 事务隔离级别越低,事务请求的锁越少或保持锁的时间越短。

5.使用习惯

  • 自动提交Auto Commit默认打开。
  • 在显示开启事务后,MySQL会自动执行SetAutoCommit=0的命令,并在Commit或Rollback结束一个事务后执行SetAutoCommit=1。
  • 子事务提交并不会马上生效,除非父事务已经提交。
  • 树中任意一个事务的回滚会引起它的所有子事务一起回滚。
  • InnoDB使用Repeatable Read隔离级别,所以Select默认使用乐观锁;只有select ... for update 和 select ... in share mode的时候才会使用悲观锁。

复制(replication)

86ec94970dab6ce99c30f5156a4c16d7.png

1.注意事项

  • RelayLog:中继日志
  • 上述架构有缺陷:如果drop table / drop database无法恢复,因此还需配合快照。
  • 事务提交流程:
    • master事务提交 -> write binlog -> slave replicate binlog -> innodb redo log commit
    • 在replicate之后数据库宕机会造成主从不一致
    • master事务提交 -> innodb redo log prepare (sync) -> write binlog -> ...

RAID

1.RAID介绍

  • Redundant Array of Independent Disks,独立磁盘冗余数组
  • 基本思想:把多个相对便宜的磁盘组合起来,成为一个磁盘组,使性能达到一个价格昂贵容量大的硬盘。
  • RAID 0: 它将两个以上的磁盘并联起来,成为一个大容量的磁盘。在存放数据时,分段后分散存储在这些磁盘中,因为读写时都可以并行处理,所以在所有的级别中,RAID 0的速度是最快的。但是RAID 0既没有冗余功能,也不具备容错能力,如果一个磁盘(物理)损坏,所有数据都会丢失。
  • RAID 1: 两组以上的N个磁盘相互作镜像,在一些多线程操作系统中能有很好的读取速度,理论上读取速度等于硬盘数量的倍数。
  • RAID 10:多组RAID 1,然后以RAID 0的方式组合。

8c476f8d1728dfb6908c7bb478a3e5c4.png

71b5da404cf1d2416536b5c4d14f2184.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值