0
点赞
收藏
分享

微信扫一扫

MySQL架构 & InnoDB存储引擎

Sikj_6590 2023-10-06 阅读 38

1. 什么是Mysql?

那什么是关系型数据库呢?

那么关系型数据库相比非关系型数据的区别: 

官网介绍: https://www.oracle.com/mysql/what-is-mysql/

MySQL 的优势:

Mysql服务安装
  • 见官网:https://dev.mysql.com/doc/refman/8.0/en/installing.html
Mysql连接或者断开服务器 
  • 官网:https://dev.mysql.com/doc/refman/8.0/en/connecting-disconnecting.html
Mysql里面的基本库表信息
库基本操作
  • 我们叫它数据库,数据库,所以,在表的前面还有库的概念,操作查询库信息,这些基本操作就不演示了。
  • 官网:https://dev.mysql.com/doc/refman/8.0/en/database-use.html

查询所有的库:

 

创建库: 

查询当前选择的库:

 

表基本操作
  • 官网:MySQL :: MySQL 8.0 Reference Manual :: 13.1.20 CREATE TABLE Statement

系统库表 

  • 我们发现除了我们自己创建的库以外,还有很多系统的库、以及表来保证MySQL的系统运行。

官网:MySQL :: MySQL 8.0 参考手册 :: 5.3 mysql 系统架构 

三个系统库: 

mysql系统库下几个重要的表:
  1. 数据字典表(Data Dictionary Tables)
  2. 授权表(Grant System Tables)
  3. 对象信息表(Object Information System Tables):plugin 插件注册表 等待
  4. 日志系统表(Log System Tables):
  • general_log:一般查询日志表。
  • slow_log:慢查询日志表。
日志配置:
show variables like 'general_log'; //一般查询日志,默认关闭
SELECT @@long_query_time;
show global variables like 'long_query_time';
show global variables like 'min_examined_row_limit'; //至少需要检索这么多行
show global variables like 'slow_query_log'; //是否开启慢日志查询 默认关闭
SET GLOBAL slow_query_log=1;
set global long_query_time=0.1; //超过100毫秒
log_output=table |file |none //设置是放在文件中,还是在mysql.slow_log表中

Sql语句的执行流程

  • 客户端发送一条语句给到服务器,然后服务器它能给你一个它的数据。

一. 连接器 - 跟MySQL服务器建立连接

  • 只有建立连接以后,我才能够发送SQL语句给到MySQL服务器,MySQL服务器它才能够去进行接收,这样才能进行网络IO。
  • 连接的过程需要先经过TCP三次握手,因为MySQL是基于TCP协议进行传输的,如果MySQL服务并没有启动,则连接会报错;如果MySQL服务正常运行,完成TCP连接的建立后,此时连接器就要开始验证你的用户名和密码,如果用户名或密码不对,就会收到一个"Access denied for user"的错误,然后客户端程序结束执行。
  • 如果用户名和密码都对了,会读取该用户的权限,然后后面的权限逻辑判断都基于此时读取到的权限;

连接管理
  • 首先,我们得有连接,那么Mysql里面就有一个连接层来管理连接,我们看下跟连接有关的变量/参数
  • 变量:随着我的服务的运行,它会变更的,这些变量会随着我的客户端连接的变多而变多。 

MySQL的四个线程状态变量:MySQL :: MySQL 8.0 参考手册 :: 5.1.6 服务器状态变量参考

-- 查看MySQL的四个Thread线程状态变量
show status like 'Thread%';

-- 查询thread_cache_size系统变量
select @@thread_cache_size;

查看MySQL服务被多少个客户端连接 /  查看当前正在运行的线程:
-- 显示当前正在运行的线程
show full PROCESSLIST;
该查询将返回一个结果集,包含所有当前连接的信息。每一行代表一个连接,每个连接的信息包括

删除阻塞线程

  • 官网:https://dev.mysql.com/doc/refman/8.0/en/kill.html

使用PROCESSLIST表可以查看当前正在运行的连接-线程,假如当前有客户端连接已经阻塞了,那么此时你可以去把它KILL掉:

demo

1. 会话一:

SELECT * FROM product_new --表中有500W数据,查询很慢

2. 会话二:

SHOW PROCESSLIST; -- 查看当前线程

执行结果:

3. 关闭查询query:

​​​​​​​KILL QUERY 9328; -- 终止查询 

查看会话一的结果:[Err] - Query execution was interrupted 请求被中断

4. 杀死 / 关闭连接线程 - Connection Thread  

KILL 2280; -- kill 连接线程

查看会话一的结果:

  • [Err] - Lost connection to MySQL server during query - 丢失连接Connection 

其他连接相关配置参数:
show status like 'Max_used_connections%';
 
  • Max_used_connections 自服务启动以来最大的连接数
  • Max_used_connections_time 达到这个峰值的时间
几个全局系统变量: 
-- 查询最大连接数 默认151
SELECT @@max_connections;
select @@GLOBAL.max_connections;

-- 手动设置最大连接数
SET @@GLOBAL.max_connections = 1000;

-- 查询服务器超时等待时间  默认28800s - 8h
SELECT @@wait_timeout;
select @@GLOBAL.wait_timeout;

-- 手动设置服务器最大等待时间
SET @@GLOBAL.wait_timeout = 1000;

跟服务器建立完连接之后,此时就代表客户端能跟服务端去进行通信了,也就是客户端能向服务端去发送SQL语句的请求了。 

MySQL Server中的第一层 - 网络连接层

-- MySQL 8.0版本以后就已经移除掉了缓存模块:查询缓存 -- 

二. 解析器 - 解析SQL

  • 当建立连接后,客户端向服务端去发送SQL语句请求时需要解析SQL语句,会把一条SQL语句解析生成语法树;因为MySQL不是人,不会一眼看到SQL语句就知道要做什么事情。
  • 所以它会借用解析器去把SQL语句解析出来看是否符合我们的SQL语法,最终生成一个语法树(理解为一个数据结构)。
解析器分为词法解析跟语法解析!
词法解析(器)
  • 将SQL语句打碎,转化成一个一个关键单词 => 然后交给语法解析(器)去构建语法树,判断语法是否正确
语法解析(器)  
  • 语法解析已经知道每个SQL语句的单词了,那么在语法解析的时候,会去判断 / 检查语法是否正确,比如,where是不是写出where1,from写成from1;
  • 表名、字段名是否存在、用户是否有操作权限等等。
  • 如果发现SQL语法错误,则MySQL直接抛出相应的错误信息,并拒绝执行该SQL语句 => You have an error in your SQL syntax;
  • 否则会去解析构建出SQL语法树,以便后面模块获取SQL语句类型、表名、字段名、where条件等等。
总结:
  • 先将输入的SQL语句解析为语法树,然后对语法树进行语法检查,这样可以确保在执行之前先判断SQL语句是否符合MySQL的语法规则,避免执行无效或错误的语句。 
  • 注意:表名不存在或者字段名不存在,并不是在解析器里做的,解析器只负责检查语法和构建语法树,但是不会去查表名或字段名是否存在。

三. 预处理器 / 预编译器(可做可不做)  

预处理 / 预编译的两个作用: 

MyBatis中的SQL注入是MyBatis去做的参数化,而这里的SQL注入是我们的MySQL服务器自己能支持的,预处理器它是我们的MySQL服务能支撑的。 

什么是预处理?
什么是SQL注入?
  • 因为参数是客户端传过来的,所以可以传任何值,那么就有可能传入任何值就有了SQL注入问题。
-- 要执行的SQL语句
select * from emp where password = '';

-- SQL注入演示-客户端传入查询的参数为: ' or '1' = '1
select * from emp where password = '' or '1' = '1';

SQL注入是因为客户端拼接用户传入的参数,然后拼接好语句给到MySQL,这样会导致会安全问题。

那么能不能把这个参数化的事情交给MySQL自己做呢?
  • 当然可以,这个就是预处理
  • 如果你需要参数化,你只要告诉MySQL,传一个预处理语句就行,MySQL会将参数与语句编译分开。
预处理操作解决SQL注入

官网地址: MySQL :: MySQL 8.0 Reference Manual :: 13.5 Prepared Statements

预处理语句的工作流程 / 预处理语句为什么能够防止SQL注入?
预处理Demo,防止SQL注入: 
-- 创建预处理语句
-- PREPARE 预处理名字 from 'SQL语句';
PREPARE select_user from 'select * from emp where password = ?'

-- 绑定参数(设置参数值)
SET @passsword = '123456';

-- 执行预处理语句
-- EXECUTE 预处理名字 USING @绑定参数名;
EXECUTE select_user USING @passsword;

-- 清除预处理语句
-- DEALLOCATE PREPARE 预处理名字;
DEALLOCATE PREPARE select_user;
预处理主要做主要做以下2个事情:
  1. 将语句编译、优化跟参数分开处理当执行SQL语句相同,参数不同的场景,提升性能。
  2. 因为是参数化去执行的,而不是拼接参数,从而解决了SQL注入问题。
比如我们经常被问的Mybatis里面#跟$符号的区别:

四. 优化器 - 决定怎么做 => 生成执行计划,确定执行方案

  • 根据上面的流程,我们知道要去执行什么语句,但是具体怎么执行会有很多的方式,比如走哪个索引,要不要回表,要不要去在内存里面排序,你的语句是不是可以优化等等。

做哪些优化,有哪些优化器,通过全局变量 optimizer_switch 来决定, 控制优化器策略的一种方法是设置 optimizer_switch 系统变量。

具体参数如下:

-- 获取MySQL数据库中的全局优化器开关配置信息
SELECT @@GLOBAL.optimizer_switch;
MySQL  Server中的第二层 - 核心服务层,核心服务层不牵扯到数据的存储与查询。

五. 执行器 - 去操作数据的 - 真正跟存储引擎进行交互

那么官网提供了哪些存储引擎?
  • 官网:MySQL :: MySQL 8.0 Reference Manual :: 16 Alternative Storage Engines

可以通过语句查询当前服务器支持哪些存储引擎:  

SHOW ENGINES; -- 查询当前服务器支持的存储引擎

不同的存储引擎会有自己不同的存储实现方式 / 存储方案不管是什么存储引擎,它一定要做的事情是把这个数据保存起来,是用内存还是磁盘,还是都用,或者磁盘的文件格式等等都会不一样。

  • 既然要保存,那么就一定要有个数据的目录这个目录,也就是我们的一个变量,这个变量就是代表你保存到哪里。
接下来我们看下数据到底存储在哪里,以什么样的方式存储?

六. 存储层 - 数据存储地址 

SQL语句查询:

-- 查询数据库的数据目录
select @@datadir;
show variables like '%datadir%';

该目录就是我们的数据库的数据目录,我们的数据保存在该目录下。  

不同的存储引擎,存储的文件以及格式都不一样,我们今天来重点分析下InnoDB的内存以及磁盘结构。

·································································································································

InnoDB存储引擎

InnoDB 的主要优势

InnoDB的架构 - 数据磁盘结构 

表空间中数据存储 

官网地址:​​​​​​​MySQL :: MySQL 8.0 参考手册 :: 15.11.2 文件空间管理

磁盘结构

内存缓存结构(BufferPool - 缓冲池)

预读机制

-- 获取当前MySQL数据库实例中InnoDB存储引擎的详细状态信息和SQL查询语句
show engine innodb status;
既然有缓存了,那么就一定会有一个问题:内存跟磁盘的一致性问题!你改数据肯定会有个先后顺序,你是改磁盘呢还是改内存呢

数据同步机制 

我们看下我们InnoDB的数据结构 / 架构:

​​​​​​​

缓冲池 LRU-最近最少使用 算法  

Flush链表  

异步刷脏 - 从缓冲池中刷出脏页(缓冲池的冲洗)

1. 自适应刷脏/冲洗 

2. RedoLog自适应 

  • 就是我发现RedoLog保存不了的时候,你要去刷盘。即RedoLog快满的时候,要去刷脏。

3. 空闲时间刷新 - 空闲的时候会去刷脏 

4. 服务正常关闭

  • 肯定会保证我的脏页能够保存到磁盘,因为我要去防止它的数据丢失。​​​​​​​

Doublewrite Buffer - 双写缓存区 - InnoDB的三大特性之一

DoubleWrite双写过程

场景:

  • 既然你现在内存里面有些数据跟磁盘不一致,因此我们的数据是异步刷新到磁盘的,假如:内存里面的脏数据没有同步到磁盘的时候,断电了,此时内存里面的数据会发生丢失,此时我们要去解决,那么InnoDB怎么去保证数据的一致性与持久性呢?

解决办法:RedoLog - 重做日志 - 内存跟磁盘的数据一致性保证

RedoLog格式 

RedoLog File - 重做日志文件存储

RedoLog写入方式  

思考题:为啥不直接实时数据同步到磁盘?

Log - Buffer:日志缓冲区 

既然已经有一个Log buffer空间了,那么它什么时候同步到磁盘呢? 

内存跟磁盘的数据交互 - 刷盘时机

Log Buffer跟磁盘的同步 & RedoLog同步机制 

禁用RedoLog / 禁用重做日志 

补充:

小结: 

  1. 请说一下InnoDB存储引擎的内存以及磁盘结构
  2. 请你说一下你对RedoLog的理解(存储同步等方式)
举报

相关推荐

0 条评论