MySQL架构
1. mysql语句的执行流程
整体流程:
(1)、客户端与服务端连接(mysql的端口号:3306)
可选择的通信类型:同步/异步
可选择的连接方式:长连接/短连接
可选择的协议:TCP/Unix Socket
tips:一些小技巧
(1)、服务端查看连接了多少客户端
show global status like ‘Thread%’;
Threads_cached:缓存的线程数
Threads_connected:正在使用的连接数
Threads_created:为了连接创建的线程数
Threads_running:当前并发的连接数
show global VARIABLES like ‘wait_timeout’; // 非交互式的超时时间,如jdbc,单位秒
show global VARIABLES like ‘interactive_timeout’; // 交互式的超时时间,如navicat应用程序
服务端:默认允许的连接数,是151,最大是10000
(2)、尝试命中缓存
mysql是有缓存机制的,但默认是关闭的,且mysql是不推荐使用的。缓存比较鸡肋。
(3)、解析器(parser)
词法解析:把一个完整的sql语句打碎成一个个的单词
语法解析:语法检查
得到一个解析树
(4)、预处理器(pre processor)
语义解析,检查表是否存在等
(5)、优化器(query optimizer)
生成不同的执行路径,比如选择索引、表关联选择基准表、语法优化等
子查询优化、条件化简等等
得到执行计划
explain select * from table;
explain format=json select * from table;
使用步骤:
set optimizer_trace = ‘enabled=on’;
select * from information_schema.optimizer_trace;
trace如下:
{
"steps": [
{
"creating_tmp_table": {
"tmp_table_info": {
"row_length": 308,
"key_length": 0,
"unique_constraint": false,
"location": "memory (heap)",
"row_limit_estimate": 54471
}
}
},
{
"join_preparation": {
"select#": 1,
"steps": [
{
"expanded_query": "/* select#1 */ select `information_schema`.`PROFILING`.`STATE` AS `状态`,round(sum(`information_schema`.`PROFILING`.`DURATION`),7) AS `期间`,concat(round(((sum(`information_schema`.`PROFILING`.`DURATION`) / 0.001131) * 100),3),'%') AS `百分比` from `INFORMATION_SCHEMA`.`PROFILING` where (`information_schema`.`PROFILING`.`QUERY_ID` = 3) group by `information_schema`.`PROFILING`.`STATE` order by `information_schema`.`PROFILING`.`SEQ`"
}
]
}
},
{
"join_optimization": {
"select#": 1,
"steps": [
{
"condition_processing": {
"condition": "WHERE",
"original_condition": "(`information_schema`.`PROFILING`.`QUERY_ID` = 3)",
"steps": [
{
"transformation": "equality_propagation",
"resulting_condition": "multiple equal(3, `information_schema`.`PROFILING`.`QUERY_ID`)"
},
{
"transformation": "constant_propagation",
"resulting_condition": "multiple equal(3, `information_schema`.`PROFILING`.`QUERY_ID`)"
},
{
"transformation": "trivial_condition_removal",
"resulting_condition": "multiple equal(3, `information_schema`.`PROFILING`.`QUERY_ID`)"
}
]
}
},
{
"substitute_generated_columns": {
}
},
{
"table_dependencies": [
{
"table": "`INFORMATION_SCHEMA`.`PROFILING`",
"row_may_be_null": false,
"map_bit": 0,
"depends_on_map_bits": [
]
}
]
},
{
"ref_optimizer_key_uses": [
]
},
{
"rows_estimation": [
{
"table": "`INFORMATION_SCHEMA`.`PROFILING`",
"table_scan": {
"rows": 2,
"cost": 10
}
}
]
},
{
"considered_execution_plans": [
{
"plan_prefix": [
],
"table": "`INFORMATION_SCHEMA`.`PROFILING`",
"best_access_path": {
"considered_access_paths": [
{
"rows_to_scan": 2,
"access_type": "scan",
"resulting_rows": 2,
"cost": 10.5,
"chosen": true
}
]
},
"condition_filtering_pct": 100,
"rows_for_plan": 2,
"cost_for_plan": 10.5,
"chosen": true
}
]
},
{
"attaching_conditions_to_tables": {
"original_condition": "(`information_schema`.`PROFILING`.`QUERY_ID` = 3)",
"attached_conditions_computation": [
],
"attached_conditions_summary": [
{
"table": "`INFORMATION_SCHEMA`.`PROFILING`",
"attached": "(`information_schema`.`PROFILING`.`QUERY_ID` = 3)"
}
]
}
},
{
"clause_processing": {
"clause": "ORDER BY",
"original_clause": "`information_schema`.`PROFILING`.`SEQ`",
"items": [
{
"item": "`information_schema`.`PROFILING`.`SEQ`"
}
],
"resulting_clause_is_simple": true,
"resulting_clause": "`information_schema`.`PROFILING`.`SEQ`"
}
},
{
"clause_processing": {
"clause": "GROUP BY",
"original_clause": "`information_schema`.`PROFILING`.`STATE`",
"items": [
{
"item": "`information_schema`.`PROFILING`.`STATE`"
}
],
"resulting_clause_is_simple": true,
"resulting_clause": "`information_schema`.`PROFILING`.`STATE`"
}
},
{
"refine_plan": [
{
"table": "`INFORMATION_SCHEMA`.`PROFILING`"
}
]
}
]
}
},
{
"join_execution": {
"select#": 1,
"steps": [
{
"creating_tmp_table": {
"tmp_table_info": {
"table": "intermediate_tmp_table",
"row_length": 126,
"key_length": 92,
"unique_constraint": false,
"location": "memory (heap)",
"row_limit_estimate": 133152
}
}
},
{
"filesort_information": [
{
"direction": "asc",
"table": "intermediate_tmp_table",
"field": "SEQ"
}
],
"filesort_priority_queue_optimization": {
"usable": false,
"cause": "not applicable (no LIMIT)"
},
"filesort_execution": [
],
"filesort_summary": {
"rows": 6,
"examined_rows": 6,
"number_of_tmp_files": 0,
"sort_buffer_size": 320,
"sort_mode": "<sort_key, rowid>"
}
}
]
}
}
]
}
steps:
sql准备阶段 join_preparation、
sql优化阶段 join_optimization、
sql执行阶段 join_execution
(6)、执行器 executor
执行器调用存储引擎 storage engine
表类型,即存储引擎
innoDB
memory
myisam
表类型可以修改。-- 规范都是一样的,所以可以换的。(自己也可以根据规范创建索引)
当然,也可以通过c语言按照规范自己写索引。
更新(更新/插入/删除)语句如何执行?
(1)、预读取:局部性原理(默认认为取得这个部分周围的部分也是马上要用的)
page的默认值:16kb
(2)、redo log(innoDB独有)
作用:保证了内存数据的安全性,延迟刷盘时机,提升系统的吞吐量
1、为innoDB提供了崩溃恢复的可能性,实现持久化
2、redo log 记录的是在某个数据页上做了什么样的修改,属于物理日志
3、redo log 的大小是固定的,前面的内容会被覆盖,一旦写满,就会触发buffer pool到磁盘的同步,以便腾出空间记录后面的修改
(3)、undo log
记录事务发生之前的数据状态,发生异常回滚,保证原子
(4)、具体步骤
事务开始,从内存/磁盘里取到包含这条数据的数据页,并返回给执行器。
执行器修改数据页的这一行数据。
将旧的数据记录到undo log
将新的数据记录到redo log
调用存储引擎接口,记录数据页到内存
事务提交
2.InnoDB存储引擎的磁盘和内存结构
(1)、内存空间解析
内存淘汰机制:LRU - 冷热比例
新数据先放入到冷数据区,然后被访问了才放入到热数据区。
Change Buffer :存放非唯一索引的一些数据(update insert delete)。
log buffer 刷盘时机,通过参数控制刷盘频率,默认是1
0 - 》 每秒刷一次
1 - 》只要事务提交,立即刷
2 -》 只要有事务提交,先写到操作系统的缓冲区,操作系统每秒刷一次到磁盘
(2)、磁盘空间解析
System Tablespace 系统表空间
DoubleWrite Buffer 双写缓冲区 防止页部分写失败
File-Per-Table Tablespace 独占表空间,每个表有自己的空间
(3)、mysql - binlog
binlog:所有的存储引擎都可以使用
binlog以事件的形式记录了所有的DDL和DML语句,可以用来做主从复制和数据恢复。
update语句的执行过程
在崩溃时,恢复的原则如下:
1、binlog无记录,redolog无记录 --》回滚事务
2、binlog无记录,redolog状态为prepare --》回滚事务
3、binlog有记录,redolog状态为prepare - 》提交事务
3、binlog有记录,redolog状态为commit - 》提交事务