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系统库下几个重要的表:
- 数据字典表(Data Dictionary Tables)
- 授权表(Grant System Tables)
- 对象信息表(Object Information System Tables):plugin 插件注册表 等待
- 日志系统表(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个事情:
- 将语句编译、优化跟参数分开处理,当执行SQL语句相同,参数不同的场景,提升性能。
- 因为是参数化去执行的,而不是拼接参数,从而解决了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 / 禁用重做日志
补充:
小结:
- 请说一下InnoDB存储引擎的内存以及磁盘结构
- 请你说一下你对RedoLog的理解(存储同步等方式)