mysql重学(一)mysql语句执行流程
思考
- 一条查询语句如何执行?
- mysql语句中若列不存在,则在哪个阶段报错
- 一条更新语句如何执行?
- redolog和binlog的区别?
- 为什么要引入WAL
- 什么是Changbuf?如何工作
- 写缓冲一定好吗?
- 什么情况会引发刷脏页
- 删除语句会造成什么后果?会改变磁盘文件大小吗
- 如何收缩空间
1.查询语句
关于一条mysql查询语句在mysql中的执行流程
如select name from test where id=10;
- 连接器---先与mysql服务端连接器建立连接,若查询缓存命中则直接返回 (查询缓存的弊端:查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。还容易造成内存泄漏,8.0版本以后删除,是个被抛弃的功能)
查看查询缓存设置
show variables like 'query_cache%';
//my.cnf设置关闭查询缓存
query_cache_type=0
连接完成后,若长时间处于空闲状态,则会自动断开
查看当前mysql的连接状态
show processlist;
查看wait_timeout连接最长闲置时间
show variables like 'wait%';
mysql异常重启现象:使用长连接后,mysql内存可能涨的很快(mysql执行过程使用的内存管理在连接对象中,这些资源断开连接才会被释放),长连接多了后,内存占用太大,被系统强行杀掉。因此我们需要考虑:1.定期断开长连接(比如对客户端的连接池中的连接设置一个过期时间<wait_timeout);2.执行较大操作后,再执行mysql_reset_connection来初始化连接资源(5.7以上,当然这是c函数)
int mysql_reset_connection(MYSQL *mysql)
- 分析器---词法分析告诉服务端你要干什么(我要找 test表中id为10的名字) ,也就是解析客户端命令的token,生成一颗对应的解析树,然后分析器再进一步检查解析树的合法性,比如表和列是否存在等(其中sql语法错误在这块暴露)
优化器---服务端会思考该怎么执行最优(比如索引的选择、表的连接顺序),生成执行计划。存储引擎的各种数据获取方法都是已经定好的静态方法,优化器能决定执行器选择存储引擎的哪个方法去获取数据
执行器---先检查用户对库对表的权限,再继续执行执行计划
执行器执行子语句的逻辑
1.from字句组装来自不同数据源的数据(先join再on);
2.where字句基于指定条件对记录进行筛选
3.group by字句将数据划分为多个分组
4.使用聚合函数进行计算
5.使用having字句筛选分组
6.计算所有表达式
7.select的字段
8.使用order by排序结果集
以上每一个步骤都会产生一个虚拟表该虚拟表被用作下一步的输入,只有最后一个表才会返回给调用者
执行器的查询数据逻辑
select * from T where ID=10;
比如我们这个例子中的表 T (innodb引擎)中,ID 字段没有索引(有索引就直接跳到那行),那么执行器的执行流程是这样的:
1.调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过,如果是则将这行存在结果集中;
2.调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。
3.执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。至此,这个语句就执行完成了。 也就是说没有索引的话,会进行全表扫描 慢查询日志中看到一个 rows_examined 的字段,表示这个语句执行过程中扫描了多少行。这个值就是在执行器每次调用引擎获取数据行的时候累加的。
- 存储引擎--存储数据,提供读写接口,对于执行器来说,是个黑箱
1.2源码角度
源码分析sql执行过程
2.更新语句
以update a set name=1 where id=1;
主要区别在于在查询到数据之后(select name from a where id=1),如果是innodb引擎它会进行日志的两阶段提交(WAL技术Write-Ahead Logging先写日志再写磁盘):
- 开启事务,写入redolog(innodb引擎特有),并更新内存
- 写入binlog,提交事务,commit
update T set c=c+1 where ID=2;
需要注意的是上方提到的先写日志也是先写磁盘,只是写日志是顺序写,这也就引出的redolog和binlog的区别:
- redolog是Innodb引擎独有的,文件是固定大小的(默认情况下,ib_logfile0 and ib_logfile1两个文件表示),是循环写的,写满了从头写(记录了某个数据页上做了什么修改,是innodb独有的。)。这种不停的在日志文件末尾追加日志,是磁盘顺序读写,磁盘顺序读写性能很高。
mysqld查看redolog文件大小show variables like 'innodb_log_file_size'
my.cnf设置文件大小
innodb_log_file_size=xxxxx
innodb_log_files_in_group
默认在/var/lib/mysql文件夹下
- binlog 是追加写的,写满了再新建文件接着写。
my.cnf设置binlog过期时间,过期删除
expire_logs_days = 5
开启binlog日志
log-bin=mysql-bin
2.1WAL的设计理念来源
关于WAL的设计(摘抄自别人评论,觉得挺有道理)
并不是仅仅为了提升IO性能才设计的WAL。如果仅仅是为了提升性能,那为了WAL所采取的一系列措施也太得不偿失了。 WAL的出现是为了实现关系型数据库的原子性和持久性。实现原子性和持久性的最大困难是“写入磁盘”这个操作并不是原子性的,不仅有“写入”与“未写入”状态,还客观存在“正在写”的中间状态。 由于写入中间状态与崩溃都不可能消除,所以如果不做额外保障的话,将内存中的数据写入磁盘,并不能保证原子性与持久性。所以可能出现以下情形: 1:未提交事务,写入后崩溃(比如修改三个数据,程序还没修改完,但数据库已经将其中一个或两个数据的变动写入磁盘,此时出现崩溃) 2:已提交事务,写入前崩溃(程序已经修改完三个数据,但数据库还未将全部三个数据的变动都写入磁盘,此时出现崩溃) 由于写入中间状态与崩溃都是无法避免的,为了保证原子性和持久性,只能在崩溃恢复后采取补救措施,这种能力就被称为“崩溃恢复”。 为了能够实现崩溃恢复,采取了写日志的方式,写日志成功后再去写磁盘,这种事务实现方式被称为“提交日志(CommitLogging),目前阿里的OceanBase就采用这种方式,但是Commit Logging存在一个巨大缺陷:所有对数据的真实修改都必须发生在事务提交之后,即成功写入日志之后。在此之前,即使磁盘IO有足够的空闲,即使某个事务修改的数据量非常庞大,占用了大量的内存缓冲区,都不允许在事务提交前写入磁盘,因此这种方式对数据库性能的提升十分不利。 基于Commit Logging的问题就,提出了“提前写入日志”(Write-Ahead Logging)的日志改进方案,“提前写入”就是允许在事务提交之前写入变动数据的意思。而对于提前写入磁盘,在数据库崩溃后需要回滚的数据,给出的解决办法是增加另外一种被称为Undo Log的日志类型,当变动数据写入磁盘前,必须先记录Undo Log,以便在事务回滚或者崩溃恢复时根据Undo Log对提前写入的数据变动进行擦除。
mysql的ref也能验证上面这个说法
The redo log is a disk-based data structure used
during crash recovery to correct data
written by incomplete transactions.
redo log 用于保证 crash-safe 能力。innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘。
show variables like 'innodb_flush%';
//my.cnf配置
innodb_flush_log_at_trx_commit = 1
这个参数我建议你设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。sync_binlog 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。这个参数我也建议你设置成 1,这样可以保证 MySQL 异常重启之后 binlog 不丢失。
2.2写缓冲ChangeBuffer
我们知道mysql数据存储包含内存与磁盘两个部分,innodb是按数据页(通常为16k)从磁盘读取到内存中的(剩余操作在内存中执行),当要更新数据时,若目标数据的数据页刚好在内存中,则直接更新。不在呢?
将这个更新操作(也可能是插入)缓存在change buffer中(redolog也会记录这个change buffer操作)等到下一次查询要用到这些数据时,再执行这些操作,改变数据(称为合并操作记录称为merge)。
写缓冲的目的是降低写操作的磁盘IO,提升数据库性能(一次内存操作,一次redolog写盘操作)。
写缓冲除了上面这个情况,也会定期被刷盘的,数据库正常关闭和redo log写满也会进行merge操作
小实验
查看mysql的change_buffer配置
innodb_change_buffer_max_size
介绍了写缓冲的大小,占整个缓存池的比例,默认25%
innodb_change_buffering
配置哪些写操作启用写缓冲,可以设置all/none/inserts/deletes等。
2.3redolog写满了怎么办?
在上文的changebuffer中我们提到了,mysql是按页读取数据到内存中的,无论要更新的数据是否在内存中,只要是更新操作就一定是在内存中执行。当内存数据页和磁盘数据页内容不一致时,我们称这个内存页为脏页,内存写入磁盘(称为flush操作),两者一致则为干净页
因为redolog是环形日志,当redolog写满时,就需要“擦掉”开头的一部分数据来达到循环写,这里的擦掉指,指将redolog日志的checkpoint位置从 CP推进到CP‘ ,同时将两点之间的脏页刷到磁盘上(flush操作),此时系统要停止所有的更新操作(防止更新操作丢失)
除了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的一个连坐机制,即在准备刷掉一个脏页时把邻居(前提邻居也是脏页)也拖下水,邻居也可以把他的邻居给拖下水
3.删除语句执行流程
delete from t where a=300 //假设a为索引
如该命令,在通过分析器-优化器-执行器找到数据后,innodb引擎会把a=300这条记录标记为删除(空间仍存在),当要再插入一个a为300的值时,可能会复用这个位置(磁盘文件并不会减少,这里指记录的复用)
delete from t where a>300 and a<500;
如该命令为范围删除,我们知道mysql的数据是按数据页存储的(默认16kb),万一刚好删掉了一个数据页的记录呢(如上)?答案是整个数据页可能会被复用(有新值插入时,如插入a=400可以直接复用,但是a=600则不能)
另外,如果两个相邻的数据页利用率很小,系统会把两个页上的数据合到其中一个上,另一个数据页被标记为可复用。
delete from t
所有数据页都被标记为可复用,但是磁盘上文件不会变小。
因此delete命令其实只是把记录的位置(或数据页)标记为可复用,但磁盘文件的大小不会变
这些可复用的但是没被使用的空间就成了碎片。
这可能也是业务上更推荐使用软删除的原因吧
3.1只有delelte命令会产生碎片吗
当插入数据时,如果数据是按照索引递增插入,那么索引是紧凑的,但是数据如果是随机插入的呢?
1.如果插入后数据页没满,ok插入
2.如果插入后数据页数据溢出了,那么再插入这些数据时,就不得不再申请一个新的数据页来保存数据
select * from t where id<500 and id>0;
insert into t(id) values(550),(551),(560).....
如上,那么记录id为550的数据就不得不保存到新数据页中,页分裂完成后,旧的数据页留下了500-550的碎片空间(插入数据造成的空洞)
更新索引上的值也会造成碎片空间的产生,更新索引即删除一个旧的索引,再插入一个新值,也就是说,经过大量增删改的表都是可能存在碎片空间的
通过重建表来收缩空间
即重新构建索引树,让索引之间更紧凑
alter table a engine=innodb
关于重建表时能不能进行写操作
5.7之后引入了Online DDL
1.建立一个临时文件,扫描表a主键的所有数据页
2.用数据页中表A的记录生成B+树,存储到临时文件中
3.生成临时文件的过程中,将所有对A表的操作记录到一个日志文件中
4.临时文件生成后,将日志文件中的操作应用到临时文件,得到一个逻辑数据上与a表相同的数据文件
5.用临时文件替换a的数据文件
可以看到因为第三步的日志记录,使得重建表时也能进行写操作(alter 语句在启动时需要获取MDL写锁,但是这个写锁在真正拷贝数据之前旧退化成读锁)
重建方法都会扫描原表数据和构建临时文件,当表很大时很消耗IO和CPU资源,推荐使用github开源的gh-ost来做
重建表因为要创建临时文件(额外空间用于拷贝数据),如果表太大,磁盘太小可能会适得其反
参考
1.mysql实战45讲
2.源码角度分析mysql查询语句
喜欢的朋友记得点赞、收藏、关注哦!!!
相关文章:
mysql重学(一)mysql语句执行流程
思考 一条查询语句如何执行?mysql语句中若列不存在,则在哪个阶段报错一条更新语句如何执行?redolog和binlog的区别?为什么要引入WAL什么是Changbuf?如何工作写缓冲一定好吗?什么情况会引发刷脏页删除语句会…...
国产650V碳化硅MOSFET在通信电源应用中全面取代超结MOSFET
在通信电源应用中,国产650V碳化硅(SiC)MOSFET全面取代超结MOSFET(如硅基CoolMOS),是技术迭代、政策推动、市场需求和国产产业链成熟共同作用的结果。倾佳电子杨茜从以下多个维度解析这一趋势: 倾…...
【ComfyUI专栏】通过软件获取PNG图片中的工作流信息
自从AI生成图片技术发展之后,你会发现很有意思的情况就是所有的图片中开始包含利用AI生成的工作流。有的时候你直接将图片拖到ComfyUI来获取图片中的工作流。下面的图片中的信息很意外的没有包含工作流,但那时我们可以看到的Parameters里面包含了设置之外,也有工作流节点内容…...
大一计算机的自学总结:位运算实现加减乘除
前言 位运算当然可以用来实现加减乘除。 一、加法 #include<bits/stdc.h> using namespace std;int add(int a,int b) {int ansa;while(b!0){ansa^b;b(a&b)<<1;aans;}return ans; }int main() {int a,b;cout<<"a,b:"<<endl;cin>&g…...
《LLM大语言模型+RAG实战+Langchain+ChatGLM-4+Transformer》
文章目录 Langchain的定义Langchain的组成三个核心组件实现整个核心组成部分 为什么要使用LangchainLangchain的底层原理Langchain实战操作LangSmithLangChain调用LLM安装openAI库-国内镜像源代码运行结果小结 使用Langchain的提示模板部署Langchain程序安装langserve代码请求格…...
天融信 NGFW2.3 mibs
1. 新节点 库节点名称含义OID数据类型权限私有库tosRouteEntryrouteNetDst路由目地址1.3.6.1.4.1.14331.5.5.1.8.1.3OCTET STRINGread-only私有库tosRouteEntryrouteWeight路由权重1.3.6.1.4.1.14331.5.5.1.8.1.9Integer32read-only私有库tosRouteEntryrouteProbeID路由探测ID…...
进程控制的学习
目录 1.进程创建 1.1 fork函数 1.2 fork函数返回值 1.3 写时拷贝 1.4 fork 常规用法 1.5 fork 调用失败的原因 2. 进程终止 2.1 进程退出场景 2.2 进程常见退出方法 2.2.1 从main 返回 2.2.2 echo $? 查看进程退出码 2.2.2.1 我们如何得到退出码代表的含…...
rsync安装与使用-linux015
使用 rsync 可以非常高效地将文件或目录从一个服务器传输到另一个服务器。 能力: 支持 64 位文件、64 位 inode、64 位时间戳、64 位长整型支持套接字对、符号链接、符号链接时间、硬链接、硬链接特殊文件、硬链接符号链接支持 IPv6、访问时间(atimes&…...
Formality:时序变换(二)(不可读寄存器移除)
相关阅读 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 一、引言 时序变换在Design Compiler的首次综合和增量综合中都可能发生,它们包括:时钟门控(Clock Gating)、寄存器合并(Register Merging)、…...
前端实战:小程序搭建商品购物全流程
目录 项目概述 开发环境搭建 微信开发者工具下载与安装 项目创建流程 项目目录结构及各文件作用 商品展示页面开发 页面布局(WXML 与 WXSS) 数据获取与绑定(JavaScript) 加入购物车功能实现 购物车功能开发 购物车页面布…...
Python中的函数(下)
函数返回值 返回单个值 函数可以通过 return 语句返回一个值。一旦执行到 return 语句,函数就会停止执行,并将指定的值返回给调用者。例如: 返回多个值 实际上,Python函数只能返回一个值,但可以通过返回一个元组来模…...
探索AI(chatgpt、文心一言、kimi等)提示词的奥秘
大家好,我是老六哥,我正在共享使用AI提高工作效率的技巧。欢迎关注我,共同提高使用AI的技能,让AI成功你的个人助理。 "AI提示词究竟是什么?" 这是许多初学者在接触AI时的共同疑问。 "我阅读了大量关于…...
Linux运维之Linux的安装和配置
目录 Linux的基本概念: 1.为什么要使用Linux? 2.什么是Linux? Linux的安装和配置: 1.下载Linux的虚拟机和镜像文件: 1.1下载虚拟机 1.2下载镜像文件 2.在虚拟机或者物理机中安装Linux操作系统 3.配置虚拟机的…...
【深度分析】微软全球裁员计划不影响印度地区,将继续增加当地就业机会
当微软的裁员刀锋掠过全球办公室时,班加罗尔的键盘声却愈发密集——这场资本迁徙背后,藏着数字殖民时代最锋利的生存法则。 表面是跨国公司的区域战略调整,实则是全球人才市场的地壳运动。微软一边在硅谷裁撤年薪20万美金的高级工程师&#x…...
联想Y7000+RTX4060+i7+Ubuntu22.04运行DeepSeek开源多模态大模型Janus-Pro-1B+本地部署
直接上手搓了: conda create -n myenv python3.10 -ygit clone https://github.com/deepseek-ai/Janus.gitcd Januspip install -e .pip install webencodings beautifulsoup4 tinycss2pip install -e .[gradio]pip install pexpect>4.3python demo/app_januspr…...
Spring Boot 无缝集成SpringAI的函数调用模块
这是一个 完整的 Spring AI 函数调用实例,涵盖从函数定义、注册到实际调用的全流程,以「天气查询」功能为例,结合代码详细说明: 1. 环境准备 1.1 添加依赖 <!-- Spring AI OpenAI --> <dependency><groupId>o…...
【MQ】探索 Kafka
高性能 消息的顺序性、顺序写磁盘 零拷贝 RocketMQ内部主要是使用基于mmap实现的零拷贝,用来读写文件 减少cpu的拷贝次数和上下文切换次数,实现文件的高效读写操作 Kafka 零拷贝 Kafka 使用到了 mmap 和 sendfile 的方式来实现零拷贝。分别对应 Jav…...
指针(C语言)从0到1掌握指针,为后续学习c++打下基础
目录 一,指针 二,内存地址和指针 1,什么是内存地址 2,指针在不同系统下所占内存 三,指针的声明和初始化以及类型 1,指针的声明 2,指针 的初始化 1, 初始化方式优点及适用场景 4,指针的声明初始化类型…...
项目开发实践——基于SpringBoot+Vue3实现的在线考试系统(九)(完结篇)
文章目录 一、成绩查询模块实现1、学生成绩查询功能实现1.1 页面设计1.2 前端页面实现1.3 后端功能实现2、成绩分段查询功能实现2.1 页面设计2.2 前端页面实现2.3 后端功能实现二、试卷练习模块实现三、我的分数模块实现1、 页面设计2、 前端页面实现3、 后端功能实现四、交流区…...
AI DeepSeek-R1 Windos 10 环境搭建
1、安装: 下载 Python |Python.org CUDA Drivers for MAC Archive | NVIDIA pip 和virtualenv Download Ollama on Windows 如下图 2、下载模型 deepseek-r1 ollama run deepseek-r1 或者可以ollama run deepseek-r1:8b 或 3、安装一个可视化对话Chatbox 下载 …...
搜索与图论复习1
1深度优先遍历DFS 2宽度优先遍历BFS 3树与图的存储 4树与图的深度优先遍历 5树与图的宽度优先遍历 6拓扑排序 1DFS: #include<bits/stdc.h> using namespace std; const int N10; int n; int path[N]; bool st[N]; void dfs(int u){if(nu){for(int i0;…...
10.共享内存 信号量集 消息队列
10.共享内存 信号量集 消息队列 **1. IPC对象操作通用框架****2. 共享内存(Shared Memory)****3. 信号量集(Semaphore)****4. 消息队列(Message Queue)****5. 练习与作业****6. 总结** 1. IPC对象操作通用框…...
每日 Java 面试题分享【第 17 天】
欢迎来到每日 Java 面试题分享栏目! 订阅专栏,不错过每一天的练习 今日分享 3 道面试题目! 评论区复述一遍印象更深刻噢~ 目录 问题一:Java 中的访问修饰符有哪些?问题二:Java 中静态方法和实例方法的区…...
【懒删除堆】力扣2349. 设计数字容器系统
设计一个数字容器系统,可以实现以下功能: 在系统中给定下标处 插入 或者 替换 一个数字。 返回 系统中给定数字的最小下标。 请你实现一个 NumberContainers 类: NumberContainers() 初始化数字容器系统。 void change(int index, int numb…...
【Block总结】OutlookAttention注意力,捕捉细节和局部特征|即插即用
论文信息 标题: VOLO: Vision Outlooker for Visual Recognition作者: Li Yuan, Qibin Hou, Zihang Jiang, Jiashi Feng, Shuicheng Yan代码链接: https://github.com/sail-sg/volo论文链接: https://arxiv.org/pdf/2106.13112 创新点 前景注意力机制: VOLO引入了一种称为“…...
有效运作神经网络
内容来自https://www.bilibili.com/video/BV1FT4y1E74V,仅为本人学习所用。 文章目录 训练集、验证集、测试集偏差、方差正则化正则化参数为什么正则化可以减少过拟合Dropout正则化Inverted Dropout其他的正则化方法数据增广Early stopping 归一化梯度消失与梯度爆…...
Vue 组件开发:构建高效可复用的前端界面要素
1 引言 在现代 Web 开发中,构建高效且可复用的前端界面要素是提升开发效率和用户体验的关键。Vue.js 作为一种轻量级且功能强大的前端框架,提供了丰富的工具和机制,帮助开发者快速构建高质量的应用程序。通过合理设计和封装 Vue 组件,我们可以实现组件的高效复用,提高开发…...
【Python】深入探索Python元类:动态生成类与对象的艺术
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 元类是Python中一个高级且强大的特性,允许开发者在类的创建过程中插入自定义逻辑,从而动态生成类和对象。本文将全面介绍Python中的元类概…...
Spring Boot + Facade Pattern : 通过统一接口简化多模块业务
文章目录 Pre概述在编程中,外观模式是如何工作的?外观设计模式 UML 类图外观类和子系统的关系优点案例外观模式在复杂业务中的应用实战运用1. 项目搭建与基础配置2. 构建子系统组件航班服务酒店服务旅游套餐服务 3. 创建外观类4. 在 Controller 中使用外…...
在Linux系统上安装.NET
测试系统:openKylin(开放麒麟) 1.确定系统和架构信息: 打开终端(Ctrl Alt T),输入cat /etc/os-release查看系统版本相关信息。 输入uname -m查看系统架构。确保你的系统和架构符合.NET 的要求,如果架构…...
