MySQL-基础汇总
数据库对于任何一个从事后台开发的人说都是永远躲不掉的,任何系统或程序离开了数据的支持都变的毫无意义。而管理数据的工具——数据库就显得尤为重要。本章节我们的核心就是 MySQL,相信很多小伙伴跟我一样,也沉浸在增、删、改、查的舒适区里沾沾自喜。认为MySQL也不过如此,这不用起来也得心应手的嘛!而当你真正走出这个舒适圈,看看外面的世界(与他人技术交流亦或求职面试)。猛然发现自己真的是井底之蛙,有些概念或许听说过,有些甚至不知所云。所以有必要认真系统的去学习并整理一下 MySQL。
SQL的执行流程
众所周知,我们对 MySQL 数据库中数据的操作是通过执行 sql 语句来实现的。那么一个 sql 语句的执行中间经历了怎样的过程?
建立连接
无论我们的 sql 语句写的再天花乱坠,没有连上 MySQL 就是白费功夫。所以第一步得需要跟 MySQL数据库建立连接,客户端向 MySQL 数据库发送网络请求(TCP/IP、命名管道或共享内存、Unix域套接字这几种方式之一来与服务器进程建立连接。这几种连接方式了解下即可),每当有一个客户端进程连接到 MySQL 时,MySQL 都会创建一个线程来专门处理与这个客户端的交互,当该客户端退出时会与MySQL 断开连接,MySQL 并不会立即把与该客户端交互的线程销毁掉,而是把它缓存起来,在另一个新的客户端再进行连接时,为了节省线程销毁和创建的开销,把这个缓存的线程分配给这个新连接的客户端。那怎么知道相关线程的连接情况呢?(注:本文所有的命令执行都是通过Navicat执行的)
我们可以通过执行命令
show status like 'Threads%';
执行结果如下
- Threads_cached:表示线程缓存中的线程数量。MySQL会缓存一定数量的线程以应对新的连接请求,这样可以避免为每个新连接都创建和销毁线程的开销。如果这个数字接近
Threads_connected
,那么说明线程缓存设置得比较合适。设置缓存大小的配置是thread_cache_size 默认大小为-1 可自动调整。它的最大值是16384。 - Threads_connected:当前打开的连接数。这个数字显示了有多少客户端正在与MySQL服务器进行交互。
- Threads_created:自服务器启动以来创建的线程总数。如果这个值越大,配置项 thread_cache_size 可相应的增大以提升线程的缓存命中率
- Threads_running:当前正在执行查询(运行)的线程数量。
这里提到了 thread_cache_size 我们可以通过命令进行查看
SELECT @@thread_cache_size
执行结果如下
-- 显示用户正在运行的线程
show FULL PROCESSLIST
关于连接其实还有一个需要关注的点——连接数,那要查看最大连接数我们可以通过执行
SELECT @@max_connections
执行结果如下
max_connections 表示最大的连接数,超过该值不允许建立连接 。 默认值是151,最小值是1最大值是100000。
想要查看连接超时时间可以通过执行命令
SELECT @@wait_timeout;
wait_timeout 非交互连接等待的时间 (单位s) 默认值28800 也就是8小 时,空闲的线程如果8小时没动静,则会断开。
在其他的连接的配置中比较重要的就是 Max_used_connections,查看与之相关的信息可以通过执行命令
show status like 'Max_used_connections%';
执行结果如下
Max_used_connections 自服务启动以来最大的连接数,它记录了自MySQL服务器启动以来,同一时刻并行连接数的最大值。这个值并不表示当前的连接数,而是提供了一个历史记录,用于评估MySQL服务器在过去所承受的最大连接压力。
Max_used_connections_time 表示达到这个峰值的时间。
在成功的与 MySQL 建立连接之后接下来就要开始进入查询的阶段了,因为我们发送给MySQL的请求是我们输入的sql语句(本质上是一段文本)所以首先要对sql 语句进行分析
解析sql
解析sql是借用解析器去把sql语句解析出来,看是否正确可分为 词法解析和语法解析
-
词法解析
顾名思义就是将 sql 语句打碎,转化成一个个的单词,判断单词是否正确
-
语法解析
在输入的sql语句中所有单词都正确的基础上检查该sql的语法是否正确。例如,where是不是写成where1,from写成from1. 表名、列名是否存在、用户是否有操作权限等等。 如果出现错误直接抛出错误。
通过解析器的解析,我们写的 sql 词法正确,语法上也没有错误。那接下来 MySQL 还要对 sql 语句进行预处理操作
预处理
在 MySQL 中,预处理(Prepared Statements)是一种通过编译和执行 SQL 语句的分离来优化性能和增强安全性的技术。预处理语句通常用于防止 SQL 注入攻击,并可以通过减少 SQL 语句的编译次数来提高性能。预处理语句是一种在 SQL 语句中包含占位符(通常是问号 ?
)的语句。Mybatis 中拼接 sql 时用的 #{} 和 ${},其中 #{} 就会去解析变成参数,然后进行预处理,能防止sql注入,并且必须传入参数。
查询优化
经过前面几个步骤的洗礼,我们的 sql 语句的所包含的信息完整且语法正确 (所查询的表、列、以及筛选条件都正确),但是我们写的 sql 执行效率不是特别高,需要进行优化。此时优化器就登场了,它会对我们的 sql 语句进行优化(如外连接转化为内连接、表达式简化、子查询转为连接等等)在执行的时候还会判断需不需要走索引,总之优化器会先帮我们做这些工作。优化后会生成一个最终的执行计划,所以这个语句到底如何执行更好,优化器来决定。
到目前为止,我们还未真正的获取到数据。MySQL 把数据的存储和提取操作都封装到了存储引擎中。
存储引擎
执行器去根据表设置的存储引擎,调用不同存储引擎的API接口获取数据。如果要查看当前 MySQL的存储引擎可以通过执行命令
SHOW ENGINES; -- 查询当前服务器支持的存储引擎
执行结果如下
在以上存储引擎中常用的存储引擎就只有 InnoDB 和 MyISAM,其中 InnoDB 为默认的存储引擎。我们通过表格的形式来对比一下这两个存储引擎的区别。
对比 | 事务支持 | 外键支持 | 锁机制 | 计数统计 | 主键要求 |
---|---|---|---|---|---|
InnoDB | 支持 | 支持 | 支持行级锁 | 没有保存表的总行数 | 必须有主键 |
MyISAM | 不支持 | 不支持 | 只支持表锁,不支持行锁和页面锁 | 保存有表的总行数 | 允许没有任何索引和主键的表存在 |
我们都知道数据最终是存到磁盘中的,不同的存储引擎会有自己的存储实现方式。那如何在磁盘里找到数据存储的位置,可以执行如下命令
SHOW VARIABLES LIKE '%datadir%';
结果如下
注:这里是安装的windows 版本的 MySQL, Linux 下的地址会有不同
为了体现不同存储引擎在磁盘中的存储方式不同,我们模拟创建两个存储引擎的表,执行如下命令
CREATE TABLE `t_teacher` (`id` int(10) NOT NULL COMMENT '主键id',`name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '姓名',`age` int(5) DEFAULT NULL COMMENT '年龄',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;CREATE TABLE `t_student` (`id` int(10) NOT NULL COMMENT '主键id',`name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '姓名',`age` int(5) DEFAULT NULL COMMENT '年龄',PRIMARY KEY (`id`)
) ENGINE= MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
在上述 sql 片段中我们创建了两个表教师表和学生表,其中教师表采用默认的存储引擎 InnoDB,学生表用 MyISAM 我们来看这两个表在磁盘中的存储情况
从图上我们可以明显的看出 MyISAM的文件是3个,而InnoDB表数据文件只有1个。
MyISAM磁盘文件
- MYD文件 存储所有的数据记录
- MYI文件 存储索引信息,即表的索引数据
- sdi 系统数据文件,存储一些元数据、表结构,比如行数、空间碎片等
InnoDB磁盘文件
而 InnoDB 的磁盘文件就一个ibd文件存储数据与索引(关于Innodb的具体内容会单独整理一篇文章,此处不做具体的展开)
字符集与比较规则
与 sql执行流程相关的内容梳理完了,我们接下来看一块儿经常容易忽略的内容——字符集和比较规则。众所周知,我们创建数据库的时候都会有这样的选择项
字符集
字符集(Character Set),也被称为编码表,是一个系统支持的所有字符的集合。为了使计算机能够准确地表示、存储和处理各种文本数据,字符集定义了字符与二进制数据之间的映射关系。在计算机内部,所有的信息都是以二进制形式存储和处理的。字符集就是一套规则,它规定了如何将字符转换为二进制数据,以及如何将二进制数据转换回字符。我们看一下常见的字符集
字符集 | 特征 |
---|---|
ASCII | 共收录128个字符,包括空格、标点、数字、大小写字母及一些不可见字符 |
ISO 8859-1 | 共收录256个字符是 ASCII 字符集的扩充,它的别名为 Latin1 |
GB2312 | 收录汉字6763个,其他文字符号682个 |
GBK | 是GB2312字符集的扩充,编码方式兼容GB2312 |
UTF-8(重要) | 几乎收录了所有的字符,编码一个字符需要1~4字节 |
其他的字符集稍微了解下即可,我们还是把目光集中在重点的 UTF-8上
MySQL中的字符集
我们在创建数据库时会发现UTF-8字符集时有两个,utf8 与 utf8mb4。正如前面所说 UTF-8 字符集表示一个字符时需要14个字节,而正常情况下一般常用的字符用13个字节就够了,某些特殊的字符则需要1~4的字节来表示(比如表情 emoji)一个字符所用最大字节长度会在某些方面影响性能以及存储。所以我们在创建数据库时看到的 utf8 其实是一个 “简化” 的 UTF-8,它有一个别名叫 utf8mb3 它只使用1~3个字节。utf8mb4 才是正儿八经的 UTF-8 使用1~4个字节。在 MySQL8.0 以后已经将utf8mb4 认定为默认的字符集。那我们要看一下目前 MySQL中有哪些字符集,执行如下命令
show charset;
执行结果如下
Default collation 代表该字符集默认的比较规则,Maxlen则表示该字符集最多需要几个字节表示一个字符。
字符集比较规则
我们来看一下 UTF-8 系列的字符集有哪些比较规则,执行命令
SHOW COLLATION LIKE'utf8_%';
执行结果如下
那这么多的比较规则该如何进行选择呢,其实 Default 中 包含 yes的都是默认的比较规则。我们可以看到这些比较规则以 utf-8 开头而后面跟着的是比较规则对应的语言,比如 utf8mb4_polish_ci 表示波兰语比较规则,utf8mb4_spanish2_ci 是西班牙语比较规则。这些我们肯定大概率用不到仅作为一个了解,我们只需要知道 general_ci 结尾的是通用的比较规则就可以了。
附:比较规则后缀代表的含义
后缀 | 含义 |
---|---|
_ai | 不区分重音 |
_as | 区分重音 |
_ci | 不区分大小写 |
_cs | 区分大小写 |
_bin | 以二进制方式比较 |
总结
本篇是 MySQL 章节的开篇,内容不是很多。主要从sql执行流程的角度出发,在执行流程中涉及每个阶段一些 mysql 的信息查看,另外就是总结了下字符集和比较规则的一点冷知识,姑且算做一个开胃小菜。