思考
1.一条查询语句如何执行?
2.一条更新语句如何执行?
3.innodb的redolog是什么?
4.什么是写缓冲
5.写缓冲一定好吗?
6.什么情况会引发刷脏页
查询语句
关于一条mysql查询语句在mysql中的执行流程
image.png
如select name from test where id=10;
1.连接器---先与mysql服务端连接器建立连接,若查询缓存命中则直接返回 (查询缓存的弊端:查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。)
2.分析器---词法分析告诉服务端你要干什么(我要找 test表中id为10的名字) (其中sql语法错误在这块暴露)
3.优化器---服务端会思考该怎么执行最优(索引的选择)
4.执行器---检查用户对库对表的权限
执行器执行子语句的逻辑
1.from字句组装来自不同数据源的数据(先join再on);
2.where字句基于指定条件对记录进行筛选
3.group by字句将数据划分为多个分组
4.使用聚合函数进行计算
5.使用having字句筛选分组
6.计算所有表达式
7.select的字段
8.使用order by排序结果集
以上每一个步骤都会产生一个虚拟表该虚拟表被用作下一步的输入,只有最后一个表才会返回给调用者
执行器的查询数据逻辑
比如我们这个例子中的表 T (innodb引擎)中,ID 字段没有索引,那么执行器的执行流程是这样的:
1.调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过,如果是则将这行存在结果集中;
2.调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。
3.执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。至此,这个语句就执行完成了。 也就是说没有索引的话,会进行全表扫描 慢查询日志中看到一个 rows_examined 的字段,表示这个语句执行过程中扫描了多少行。这个值就是在执行器每次调用引擎获取数据行的时候累加的。
5.存储引擎--存储数据,提供读写接口
更新语句
以update a set name=1 where id=1;
主要区别在于在查询到数据之后(select name from a where id=1),如果是innodb引擎它会进行日志的两阶段提交:
1.开启事务,写入redolog(innodb引擎特有),并更新内存
3.写入binlog,提交事务,commit
redolog和binlog的区别: redolog是物理日志,循环写,记录了某个数据页上做了什么修改,是innodb独有的。
binlog是逻辑日志,追加写,记录了一条mysql的原始逻辑 即原始语句
redo log 用于保证 crash-safe 能力。innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘。这个参数我建议你设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。sync_binlog 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。这个参数我也建议你设置成 1,这样可以保证 MySQL 异常重启之后 binlog 不丢失。
写缓冲ChangeBuffer
我们知道mysql数据存储包含内存与磁盘两个部分,innodb是按数据页(通常为16k)从磁盘读取到内存中的(剩余操作在内存中执行),当要更新数据时,若目标数据的数据页刚好在内存中,则直接更新。不在呢?
将这个更新操作(也可能是插入)缓存在change buffer中(redolog也会记录这个change buffer操作)等到下一次查询要用到这些数据时,再执行这些操作,改变数据(称为合并操作记录称为merge)。
写缓冲的目的是降低写操作的磁盘IO,提升数据库性能(一次内存操作,一次redolog写盘操作)。
写缓冲除了上面这个情况,也会定期被刷盘的,数据库正常关闭和redo log写满也会进行merge操作
小实验
查看mysql的change_buffer配置 image.pnginnodb_change_buffer_max_size
介绍了写缓冲的大小,占整个缓存池的比例,默认25%
innodb_change_buffering
配置哪些写操作启用写缓冲,可以设置all/none/inserts/deletes等。
redolog写满了怎么办?
先介绍两个概念
在上文的changebuffer中我们提到了,mysql是按页读取数据到内存中的,无论要更新的数据是否在内存中,只要是更新操作就一定是在内存中执行。当内存数据页和磁盘数据页内容不一致时,我们称这个内存页为脏页,内存写入磁盘(称为flush操作),两者一致则为干净页
因为redolog是环形日志,当redolog写满时,就需要“擦掉”开头的一部分数据来达到循环写,这里的擦掉指,指将redolog日志的checkpoint位置从 CP推进到CP‘ ,同时将两点之间的脏页刷到磁盘上(flush操作),此时系统要停止所有的更新操作(防止更新操作丢失)
image.png
除了redolog写满还有什么会引发flush操作?
1.系统内存不足。当要读取新的内存页时就要淘汰一些数据页,如果淘汰的正好是脏页,就要执行一次flush操作
2.Mysql认为系统处于“空闲状态”
3.正常关闭Mysql
flush操作对性能的影响
上述后两者场景(系统空闲和正常关闭)对于性能都没太大影响。
当为第一种redolog写满时,系统无法执行更新操作,所有操作都会堵塞
当为第二种内存不够用时,如果淘汰脏页太多,影响mysql响应时间
后两者刷脏页会影响性能,所以Mysql需要有刷脏页控制策略,可以从以下几个设置项考虑
1.设置innodb_io_capacity告诉innodb所在主机的IO能力
//利用fio工具来测试磁盘随机读写能力
fio -filename=$filename -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest
- innodb_max_dirty_pages_pct设置脏页比例上限 控制刷脏页速度
3.innodb_flush_neighbors=1 来设置“连坐”
一旦一个查询请求在执行过程中需要刷掉一个脏页,可以利用Mysql的一个连坐机制,即在准备刷掉一个脏页时把邻居(前提邻居也是脏页)也拖下水,邻居也可以把他的邻居给拖下水
网友评论