1. sql操作流程图
2. 流程图解析
1)前台操作触发mysql服务器执行请求
前台用户各种操作触发mysql执行,通过web项目中自带的数据库连接池:dbcp、c3p0、druid等,与数据库服务器建立网络连接
数据库连接池中的线程监听到请求后,将接收到的sql语句通过sql接口响应给查询解析器,查询解析器按照sql语法解析出查询哪些表的哪个字段,查询条件是啥;再通过查询优化器处理,选择该sql最优的一套执行计划,然后执行器负责调用存储引擎的一系列接口,执行该计划完成整个sql语句的执行
2)InnoDB存储引擎-缓冲池完成基本更新操作
具体执行这些执行计划得要存储引擎来完成,如图所示,首次更新user表中id=10的这条数据,缓冲池一开始肯定没有该条数据的,得要先从磁盘中将被更新的原始数据加载到缓冲池中
同时为了保证并发更新数据安全问题,会对这条数据先加锁,防止其他事务更新
接着将更新前的值先备份到undo log 中,便于回滚
最后更新缓存页中的数据为最新的数据,至此就完成了在缓冲池中的执行流程
3)Redo Log 和 BinLog保证事务的可靠性
缓冲池中更新完数据后,需要将本次更新的数据顺序写到redo log和bin log日志中(此时信息还在内存中,后续的刷盘策略如图所示),一般为了保证数据不丢失会配置双写策略,redo log 落盘后,写bin log 落盘,再将bin log 的文件名、文件所在路径信息以及commit标记同步顺序写到redo log中(其中以commit标记是否更新到redo log中,是判定事务是否提交成功的一个重要标准),redo log和bin log分别在物理和逻辑层面为本次事务、提供数据上的一致性保障,如图
4)将事务的操作持久化
前面的一系列操作后,InnoDB存储引擎后台还有一个IO线程,会在数据库压力的低峰期间,将缓冲池中被事务更新、但还没来得及写到磁盘中的数据(脏数据,因为磁盘数据和内存数据已经不一致了)给刷到磁盘中,完成事务的持久化,如图
3. buffer pool
1)内存结构
查询参数:innodb_buffer_pool_size,默认大小128M。
注意:buffer pool中缓存页对应磁盘中的数据页,另外还有一块内存关于数据的描述信息,大概是数据页的5%左右, 800字节左右,所以一块buffer pool实际内存要比128M大一点。
2)3大链表
l free链表
buffer pool中并没有单独开辟空间来维护free链表,而是在 描述数据 中维护着两个指针,free_pre和free_next,分别向上一个free链表的节点,和下一个free链表的节点,构成了free链表。
数据加载到buffer pool中的缓存页中,把free链表上对那个的缓存页节点删除掉。
判断数据有没有加载到buffer pool中,还需要引入数据库自己维护的hash表数据结构,会用 表空间号+数据页号,作为一个key,缓存页的地址作为value。
l flush链表
数据结构同free链表,用于刷脏,脏数据指的是缓存页中增删改的数据,把flush链表上的缓存页数据刷到磁盘
避免每次刷盘都刷整个buffer pool中所有数据。
数据刷入磁盘之后,该缓存页从flush链表上移除,重新添加到free链表上。
l lru链表(least recently used)
数据结构同free链表,记录着缓存页数据被操作的命中率,如果一个缓存页的数据经常被执行增删改操作,称之为高命中,排在链表头部,相反则排在尾部。
一条数据被操作的时候,该缓存页会被提升到lru链表的头部。
如果缓存页不够的话,会把lru尾部不常操作的数据刷入到磁盘,重新把缓存页添加到free链表中。
注意:由于mysql的预读机制和全表扫描,经常会把相邻的数据页和整个表的数据页加载到buffer pool中,引起的问题就是根本不会操作的数据占据了lru链表比较靠前的位置,而可能操作的数据被挤到了尾部,从而造成了尾部数据重新加载。
lru链表优化
参数:冷热数据比例innodb_old_blocks_pct 默认37
冷区数据移动到热区数据的时间阈值innodb_old_blocks_time 默认1000毫秒
4. undo log
buffer pool中更新事务未提交的数据,mysql就宕机了,此时数据就需要回滚到未更新之前的状态,引入了undo log,记录了当前事务操作的逆向操作(也就是操作之前的数据),保证事务失败后数据的回滚。
在磁盘数据页加载到buffer pool中缓存页之后,数据更新之前,把逆向日志记录到redo log中。
5. redo log
buffer pool中更新事务提交的数据,如果还没来得及刷入磁盘,就断电了,这时候的数据在内存中,断掉就丢失了;所以引入了redo log机制,保证事务提交的时候,以日志的形式记录到redo log文件中,此时断电,重启后只需要把之前的事务根据redo log重做一次就好,保证事务提交的数据不丢失。
而且redo log采取的是日志追加的顺序写,性能比刷盘的随机写高很多。
redo log格式:--日志类型+表空间号+数据页号+偏移量+修改几个字节的值+具体的值。
注意:redo log 并不是一条一条的追加,而是引入了redo log block的数据结构,512k,类似于字节流设置多少字节操作一次一样;mysql启动的时候就会在buffer pool中申请一个redo log buffer空间用来存放redo log block,默认16M。
redo log buffer何时刷盘呢?
1)redo log buffer 写入的数据超过了总空间的一半
2)Innodb_flush_log_at_trx参数控制事务提交后的几种刷盘策略
3)后台线程每隔1s刷盘一次
4)mysql关闭的时候