DBA专题
DBA授课
DBA公开课
DBA训练营三天
01.Mysql基础入门-数据库简介
02.Mysql基础入门-部署与管理体系
03.MySQL主流版本版本特性与部署安装
04.Mysql-基础入门-用户与权限
05 MySQL-SQL基础2
06 SQL高级开发-函数
07 MySQL-SQL高级处理
08 SQL练习 作业
09 数据库高级开发2
10 Mysql基础入门-索引
11 Mysql之InnoDB引擎架构与体系结构
12 Mysql之InnoDB存储引擎
13 Mysql之日志管理
14 Mysql备份,恢复与迁移
15 主从复制的作用及重要性
16 Mysql Binlog Event详解
17 Mysql 主从复制
18 MySQL主从复制延时优化及监控故障处理
19 MySQL主从复制企业级场景解析
20 MySql主从复制搭建
21 MySQL高可用-技术方案选型
22 MySQL高可用-MHA(原理篇)
23 MySQL MHA实验
24 MySQL MGR
25 部署MySQL InnoDB Cluster
26 MySQL Cluster(MGR)
27 MySQL ProxySQL中间件
相信可能就有无限可能
-
+
首页
11 Mysql之InnoDB引擎架构与体系结构
# 1.InnoDB介绍 > 存储引擎:用各种不同的技术来存储文件(或者内存).使用不同的存储机制、索引技巧、锁定水平获得不同的数据操作功能 ```sql #介绍 1.InnoDB-MySQL默认存储引擎,相当于linux的文件系统和数据存储介质 2.OLTP交易类型首选,有以下优势 (1)DML操作遵循ACID模型,事务可以提交,回滚,崩溃恢复。 (2)行级锁和事务隔离级别保证多用户高并发操作性能 (3)InnoDB磁盘B+树索引组织数据,最小化I/O ``` # 2.InnoDB特性 ![image-20240320110337471](https://img.sunrisenan.com/img/2024/03/20/110339122.png) # 3.InnoDB架构 ```sql 分为内内存结构和磁盘结构 1.内存结构: - buffer pool - change buffer - Adaptive hash Index - Log Buffer 2.磁盘结构: - table - index - tablespace 3.InnoDB后台线程 - Master Thread - IO Thread - Purge Thread - Page Cleaner Thread - Read Thread - Write Thread - Redo log Thread - Change Buffer Thread - Checkpoint - Error monitor Thread - Lock monitor Thread ``` 详细架构 ![64](https://img.sunrisenan.com/img/2024/03/20/110605514.png) 抽象架构 ![65](https://img.sunrisenan.com/img/2024/03/20/110643091.png) # 4.InnoDB结构之内存结构 ## 4.1.InnoDB体系结构-后台线程 ### 4.1.1.**Master Thead** ```sql 核心后台线程,主要负责将buffer pool中数据异步刷新到磁盘,保证数据一致性 (1)最高级别优先级别 (2)内部多个循环loop组成: - 主循环(loop) - 后台循环(backgroup loop) - 刷新循环(flush loop) - 暂停循环(suspend loop) (3)根据数据库运行状态在各个循环之间切换 ``` ```sql 主循环,两大部分主要操作(1s和10s),伪代码 (1)loop循环 void master_thread(){ loop: for(int i=0;i<=100;i++)( do thing once per second sleep 1 second if necessary #通过thead sleep实现,负载大可能有延迟不是1s或者10s ) do things once per ten seconds goto loop; } 每秒一次操作 - 日志缓冲刷新到磁盘 (log buffer -->redo)(always,即使事务未提交) - 合并插入缓冲(change buffer)(possible) - 最多刷新100个InnoDB的缓冲池中的脏页到磁盘 (possible) - 没有用户线程活动,切换到backgroud loop (possible) 每10秒操作: - 刷新100个脏页到磁盘(possible) - 合并最多5个change buffer(always) - log buffer刷新到磁盘(always) - 删除无用undo页(always) - 产生一个checkpoint (always) - 刷新100个或者10个脏页到磁盘(always) 执行流程: 1.InnoDB先判断过去10s内磁盘IO操作是否小于200次 2.是,刷新100个脏页到磁盘 3.合并change buffer(每10s一次的必执行) 4.将log buffer刷新到磁盘 5.做一次full purge操作,删除无用的undo页(如果不涉及一致性读,立即删除),尝试回收最多20个undo页 6.Innodb判断buffer pool中脏页比例,超过70%刷新100个脏页,小于70%,刷新10%脏页到磁盘 (2)background loop:没有用户线程或者数据库关闭切换到,有以下操作 1.删除无用undo页(always) 2.合并20个chang buffer(always) 3.跳回主循环(always) (3)suspend_loop:讲master thread挂起,等待事件发生 (4)flush loop:刷新100个页 #InnoDB刷线100个脏页过于浪费SSD性能,于是后续InnoDB1.2.x版本做出优化和参数调整 innodb_io_capacity:200 - 默认每秒刷新脏页数量 innodb_max_dirty_pages_pct:75% - 触发刷盘脏页百分比。达到buffer_pool 75%刷新 200个(innodb_io_capacity)到磁盘 - 并不是绝对情况,以下情况也会触发刷新脏页 - innodb_adaptive_flushing参数判断生效 - REDO日志快满的时候 - MySQL实例正常关闭时候 - LRU淘汰尾部一部分作为free list,发现是脏页 innodb_adaptive_flushing = ON - 自适应刷新,影响每秒刷脏页数量 - 通过buf_flush_get_desired_flush_rate 函数判断脏页刷新数量(判断redo log) innodb_purge_batch_size:300 - full purge操作时候回收undo页数量,默认300 ``` ### 4.1.2.IO Thread ```sql 1.InnoDB存储引擎使用AIO(Async IO)处理IO请求 2.IO Thread负责处理这写IO请求的回调(call back) 3.分为四种 - write thread:默认4 - read thread:默认4 - insert buffer thread - log IO thread mysql>show engine innodb status\G -------- FILE I/O -------- I/O thread 0 state: waiting for completed aio requests (insert buffer thread) I/O thread 1 state: waiting for completed aio requests (log thread) I/O thread 2 state: waiting for completed aio requests (read thread) I/O thread 3 state: waiting for completed aio requests (read thread) I/O thread 4 state: waiting for completed aio requests (read thread) I/O thread 5 state: waiting for completed aio requests (read thread) I/O thread 6 state: waiting for completed aio requests (write thread) I/O thread 7 state: waiting for completed aio requests (write thread) I/O thread 8 state: waiting for completed aio requests (write thread) I/O thread 9 state: waiting for completed aio requests (write thread) Pending normal aio reads: 0 [0, 0, 0, 0] , aio writes: 0 [0, 0, 0, 0] , ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0 Pending flushes (fsync) log: 0; buffer pool: 0 307638565 OS file reads, 9594705178 OS file writes, 6345424377 OS fsyncs 18.49 reads/s, 16384 avg bytes/read, 101.00 writes/s, 100.98 fsyncs/s ``` ### 4.1.3.Purge Thread ```sql 作用:事务提交后回收undolog。 (1)InnoDB 1.1.版本后独立出master线程,减轻master thread工作,1.2后支持多个线程 (2)56版本是1,57版本是4 ``` ### 4.1.4.Page Cleaner Thread ```sql (1)InnoDB 1.2.x引入 (2)脏页刷新操作放入此线程,减轻Master Thread负担和用户查询阻塞 ``` ### 4.1.5.其余Thread Read Thread ```sql (1)read thread为mysql的读线程,默认为4个, (2)其负责将数据页从磁盘上读入,其由innodb_read_io_threads选项控制. (3)用户线程发起读请求并将其放至读请求队列,read threads从读请求队列获取读任务并完成. ``` Write Thread ```sql (1)write thread为mysql的写线程,默认为4个. (2)负责将数据页从缓冲区写出到磁盘,其由innodb_write_io_threads控制选项控制. (3)page_cleaner线程发起写请求并将其放至写请求队列,write threads从写请求队列获取写任务并完成 ``` Redo log Thread ```sql redo log thread负责把日志缓冲中的内容刷新到redo log文件中. ``` Change Buffer Thread ```sql change buffer thread负责把改变缓冲(change buffer)中的内容刷新到磁盘. ``` Checkpoint ```sql checkpoint thread负责在redo log发生切换时,执行checkpoint. ``` Error monitor Thread ```sql error monitor thread负责mysql报错的监控. ``` Lock monitor Thread ```sql lock monitor thread负责mysql锁的监控 ``` ## 4.2.Buffer pool ### 4.2.1.简介 ```sql #简介 缓冲池(InnoDB Biffer Pool),一块连续的内存,使CPU读取或者写入数据避免走低速磁盘. 每次使用内存也数据,放在Buffer_pool中,下次在访问走buffer pool即可,不加载硬盘,提高访问性能。 缓冲池缓存的数据页类型 (1)索引页 (2)数据页 (3)undo页 (4)change buffer (5)AHI (6)InnoDB存储锁信息(lock info) (7)数据字典 ``` InnoDB内存数据对象图解: ![66](https://img.sunrisenan.com/img/2024/03/20/111335795.png) ### 4.2.2.基础概念 ```sql #1.Buffer Pool Instance (1)buffer pool实例,一个池子有多个instance,分担锁压力。每个实例有锁,物理块和逻辑链表,无竞争关系,可以并发读取或者写入。 (2)大小为innodb_buffer_pool_size/innodb_buffer_pool_instances(一般64G建议8个) (3)多实例之间没有任何关系,单独申请,单独管理,单独刷盘 #2.数据页 InnoDB数据管理最小单位,默认16KB。 #3.Buffer Chunks (1)buffer pool最小单位实际的物理存储块数组,启动时候向OS申请内存,实例关闭释放。 (2)包含两部分:缓存页和对应的控制块(包含指向数据的指针) 缓存页:一页16KB与数据页一致 控制块:存放缓存页描述信息(表空间编号,页号,缓存页在缓冲池地址,锁信息) 碎片:剩余空间不足以划分新的控制块和缓存页 (3)默认128MB #4.Control Block(控制块) 根据读入的数据页在内存状态动态生产,包括 1.页面管理的普通信息,互斥锁, 页面的状态等 2.脏回写(flush)管理信息 3.lru控制信息 4.快速查找的管理信息 5.Frame:指针 ``` Buffer_pool的逻辑结构 ![67](https://img.sunrisenan.com/img/2024/03/20/111444604.png) ### 4.2.3.buffer pool 初始化分配 ```sql #知识补充 内存A:内存映射文件的方法.让用户程序直接访问设备内存,不需要调用read(),write() 分配步骤: (1)InnoDB使用mmap分配Buffer pool到VIRT(虚存),只有内存被使用算进RES中. (2)开始分配每一个buffer pool 实例,以下是每个实例单独初始化, (3)分配完内存,在buf_chunk_init()函数中,将buffer pool分为两部分,page为16KB - 数据页控制体(buf_block_t) - 页面地址frame - 页面信息结构buf_page_t,描述页面信息,所属表空间ID号,页面号,被修改产生的LSN,使用状态 - 保护页面的互斥mutex - 访问页面对这个也上锁Lock(read/write) - 数据页 - 控制体:数据页:1:38.6(40G的buffer pool 1G多存放控制体) (4)划分完毕后,遍历控制体,buf_block_t::frame指针-->数据页 (5)将数据页加入到Free list中。 (6)初始化其他结构体(page hash,zip hash) 互斥访问: 1.每个instance pool有一个专门mutex保护这个pool的数据域 2.每个数据页控制体有一个专门的mutex保护。 ``` ### 4.2.4.**逻辑链表** - 数据页的控制体:有指针指向真正的数据页,Innodb Buffer Pool 有3种逻辑链表: (1)free list ```sql 1.Free List (1)初始化缓存页划分完毕后,Buffer Chunks所有数据页用free list管理所有空闲缓存页 (2)需要缓存页加载数据页,从此链表的控制块中获取指向空闲页的地址。 (3)如果free list没有空余,从FLU list或LRU List淘汰节点。 (4)结构包含链表的头节点指针,尾节点指针,链表节点数量,每个控制块有free list的上一个和下一个指针 ``` Free 链表结构 ![68](https://img.sunrisenan.com/img/2024/03/20/111559901.png) (2).LRU链表 ```sql 1.概念:近期最少使用链表(Least Recently Used),InnoDB最重要的链表 2.作用:所有新读取进来数据页放在上面,按照最少使用排序算法,最少使用节点放在链表末尾,free list没有则淘汰LRU中的节点,供free list使用。 3.组成: 默认 - 5/8为young list:存储经常被使用的热点page - 3/8为old list:新读入page默认在old list 头部,满足一定条件转移到young list。 - 设计作用:预读数据页 防止全表扫描污染Buffer_pool - 分为三种数据页 - 未修改的页面,可以从该列表中摘除,然后移到free列表中 - 已修改还未刷新到磁盘的页面 - 已修改且已经刷新到磁盘的页面 4.LRU算法: - midpoint是young list尾部和old list头部。old list是被淘汰对象 - MySQL访问某个数据页,buffer pool没有此页 - InnoDB首先从硬盘中加载此页 - 找到一个空闲的数据页存放此页 - 将缓存页对应的控制块插入到LRU链表(midpoint-old list的头部) - MySQL访问数据页,页在Buffer pool中,将缓存页对应的控制块移动到 LRU new list的头部 5.LRU算法优化1 - 防止扫表污染buffer pool:业务有需求或者对索引使用不当 - 优化: - 扫表所有数据进old list,不进young list(保护热点数据) - 扫表数据页停留时间超过1s(被访问),从old list移动到new list,小于1s则不会移动(防止全表扫描污染buffer pool,old list数据是不经常访问的,保护热点数据,之后被慢慢淘汰) 6.LRU算法优化2 - 当缓存页在young list的前1/4,不移动到young list的head - 在后3/4,访问缓存页,移动到young list头部 - 作用:链表数据移动到头部,修改元素指针指向,会加锁,这样为了提高效率,减少锁竞争. 6.参数: - innodb_old_blocks_time控制移动时间 - innodb_old_blocks_pct 控制old list长度 ``` LRU结构 ![69](https://img.sunrisenan.com/img/2024/03/20/111655158.png) (3)FLU链表 ```sql 概念:链表所有节点都是脏页(dirty page,被修改但是没有刷盘到磁盘上,数据不一致),需要刷回磁盘,对脏页的控制块建立一个链表管理. 组成:数据页记录了第一次被修改的lsn进行排序,最开始被修改的放在链表末尾 刷盘方式: 1.BUF_FLUSH_LIST方式:后台线程定时将flush链表中的部分脏页同步到硬盘中 2.BUF_FLUSH_LRU方式:后台线程定时从LRU链表的尾部开始扫描一定数量的缓存页,如果发现脏页则会同步到硬盘中。扫描缓存页的数量可通过(全局)系统变量innodb_lru_scan_depth进行控制 3.BUF_FLUSH_SINGLE_PAGE:当用户线程需要将一个页缓存到Buffer Pool时,如果发现已无未被使用的、空闲的缓存页,此时就需要淘汰LRU链表尾部的一个缓存页。这个时候,如果待淘汰的缓存页恰好为脏页的话,就需要先将其同步到硬盘中 ``` ### 4.2.5.Buffer pool分配方式 ```sql #知识补充 内存映射函数mmap:内存映射文件的方法.让用户程序直接访问设备内存,不需要调用read(),write() 分配步骤: (1)InnoDB使用mmap分配Buffer pool到VIRT(虚存),只有内存被使用算进RES中. (2)分配完内存,在buf_chunk_init()函数中,讲buffer pool分为两部分,page为16KB - 数据页控制体(buf_block_t) - 数据页 - 控制体:数据页:1:38.6(40G的buffer pool 1G多存放控制体) (3)划分完毕后,遍历控制体,置buf_block_t::frame指针-->数据页 (4)将数据页加入到Free list中。 (5)初始化其他结构体(page hash,zip hash) 互斥访问: 1.每个instance pool有一个专门mutex保护这个pool的数据域 2.每个缓冲区的block有一个专门的mutex保护。 ``` ### 4.2.6.Buffer pool数据加载 ```sql MySQL重启,buffer pool中无数据,业务访问需要重新预热。 为了缩短这个过程,MySQL关闭前,buffer pool页面信息保存到磁盘,重启加载到buffer pool 1.buffer pool dump 遍历所有Buffer Pool Instance的LRU List,对于其中的每个数据页,按照space_id和page_no组成一个64位的数字,写到外部文件中 2.Buffer Pool Load 读取指定的外部文件,把所有的数据读入内存后,使用归并排序对数据排序,以64个数据页为单位进行IO合并,然后发起一次真正的读取操作。排序的作用就是便于IO合并。 ``` ### 4.2.7.**数据页访问机制** ```sql 1.访问页面在buffer pool命中,直接访问改页面. - 每个Buffer pool instance维护一个page hash(space id-->page no) - 读入一个page,根据space id和page no找到对应的pool instance和page - 如果page hash没有,需要从磁盘读取 2.没命中,从磁盘加载到pool,从free list中找到空闲内存块缓存 3.free list用完,从LRU找寻替换的缓存页,淘汰 加入free list。 4.如果LRU没有替换页,进行单个脏页刷新,释放内存块到空闲列表 5.free list所有用户线程共享,存在争用,用户线程会反复查找到空闲内存块 ``` ## 4.3.Change buffer **1.设计原因** - 对读请求,通过Buffer pool,写请求如何优化? - 如果是聚簇索引,插入顺序按照主键递增,不需要磁盘随机读取。写请求会很快 - 如果是普通索引(非唯一索引),插入操作,数据页插入不是顺序插入,需要离散访问数据页,存在随机读取导致insert性能下降(但是如果业务字段是递增,比如用户订单创建时间,则也是顺序),举例 - 更改页号40的索引页,正好不在缓冲池中(无buffer pool) - 40号索引页,从磁盘加载到buffer pool,一次磁盘随机读操作 - 修改缓冲池中的页,一次内存操作 - 写入redo log一次磁盘顺序写操作 - 没命中缓冲池,至少一次磁盘I/O - Change Buffer为了解决写磁盘I/O出现 ![70](https://img.sunrisenan.com/img/2024/03/20/111859404.png) **2.设计功能** ```sql (1)普通索引插入或者更新,先判断插入的索引页是否在缓冲池中 - 在,直接插入 - 不在,放在change buffer中 (2)change buffer以一定频率(空闲或者缓慢数据库关闭期间)加载到缓冲池, (3)Master Thread线程负责与磁盘普通索引的叶子节点merge(合并)。 (4)完成多个插入操作合并到一个操作(在一个索引页),提升插入性能,避免随机访问I/O (5)对DML-insert buffer, delete buffer, update buffer 都进行缓冲 (6)如果change buffer数据页过多,merge可能要很久,数据库宕机启动恢复也会很久(几小时) ``` **3.架构图** ![71](https://img.sunrisenan.com/img/2024/03/20/111956424.png) 如果有了chnage buffer,那么刚才的操作变成 - 在change buffer 记录这个操作,一次内存IO - 写入redo log,一次磁盘顺序写操作,40这页没有在buffer_pool中,此时有读取操作 - 载入索引页,发现buffer pool没有 - 从change buffer读取信息,加载到buffer pool的LRU list中 ![72](https://img.sunrisenan.com/img/2024/03/20/112032508.png) - 那些操作触发刷新change buffer ```sql (1)Master Thread线程,会认为数据库空闲时; (2)Change Buffer不够用 (3)数据库正常关闭 (4)redo log写满(几乎不会) ``` - 什么业务场景不适合写缓冲机制 ```sql 1.数据库都是唯一索引 2.写入一个数据,立马读取 这两类场景,写操作后就要进行也读取,正常走buffer pool,change buffer成为负担 ``` - 适合写缓冲机制 ```sql 1.数据库大部分是非唯一索引; 2.业务是写多读少,或者不是写后立刻读取 可以使用写缓冲,将原本每次写入都需要进行磁盘IO的SQL,优化定期批量写磁盘. 场景:账单流水业务。 ``` **4.参数** ```sql 1.innodb_change_buffering 默认为all,更改缓冲功能 all:DML都会缓冲 none:无缓冲操作 inserts:支持插入操作缓冲 deletes:支持删除操作缓冲 changes:支持插入和删除操作缓冲 purges: 2.innodb_change_buffer_max_size 默认25%的buffer_pool mysql> show variables like '%innodb_change_buffer%'; +-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | innodb_change_buffer_max_size | 25 | | innodb_change_buffering | all | +-------------------------------+-------+ ``` **5.如何查看** ```sql Mysql>SHOW ENGINE INNODB STATUS\G ------------------------------------- INSERT BUFFER AND ADAPTIVE HASH INDEX ------------------------------------- Ibuf: size 1, free list len 1229, seg size 1231, 51774 merges merged operations: insert 107862, delete mark 198944, delete 126510 discarded operations: insert 0, delete mark 0, delete 0 Hash table size 13596637, node heap has 23109 buffer(s) 28880.02 hash searches/s, 3056.72 non-hash searches/s free list len 1229 空闲列表长度 seg size:当前插入缓冲大小 1231X16KB=20MB merges:合并次数 size:已经合并页的数量 insert,delete:插入 产出的记录数 ``` ### 1.4.3.AHI(索引笔记) ### 1.4.4.Log buffer ```sql 作用:Log buffer是保存要写入磁盘上日志文件(redo log)的数据的内存区域,即ib_logfile0,ib_logfile1 参数:innodb_log_buffer_size 默认16MB 运行:日志缓冲区的内容会定期刷新到磁盘,节省磁盘I/O ```
李延召
2024年3月20日 11:24
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码