MySQL整体结构的概述
为了去更好学习MySQL的整体架构,我们先对MySQL内部结构进行一个分析,可以参考下面这个图片:
从上往下看依次是:网络连接层、系统服务层、存储引擎层、以及文件系统层,当我们编写完SQL语句之后都是遵循这个MYSQL这个架构往下走的
- 网络连接层: 数据库连接池这一块,主要处理的是客户端接入的工作,即客户端发送请求去连接数据库连接池
- 系统服务层: 从上图可以发现,主要包括SQL接口组件、解析器、优化器、缓存和缓冲区这四个区域
- 存储引擎层: MySQL支持的各大存储引擎,例如:
InnoDB、MyISAM等等 - 文件系统层: 覆盖了所有的日志,以及数据、索引文件,位于系统硬盘上
那么还有一个最顶部的客户端,客户端可以是各种各样的编程语言java、php、c++、rust、go、node、netty等,也可以是各种可视化工具,例如Datagrip、SQLyog、Navicat等,也可以是mysql命令行工具
总之:只要能连接上MySQL的,即可以和MySQL建立网络连接的都可以被称为是客户端
MySQL-server是MySQL的服务端,就是上面讲述的四个层,主要负责SQL的执行和数据的存储,所以就很好理解为什么需要这四个层,为什么要在这四个区域执行对应的工作
MySQL-client是MySQL的服务端,主要负责编写SQL语句,所以就解释了为什么各种编程语言、各种可视化工具、命令行工具都可以作为客户端
上述就是MySQL的整体架构的简单了解,那么接下来就是对MySQL-server这四个层进行详细的学习
网络连接层
当一个客户端尝试和MySQL建立连接时,MySQL都会派发一条线程负责处理这个客户端接下来要做的所有工作。数据库连接池负责的是所有客户端的接入工作,就是客户端和服务端进行连接的工作,MySQL的连接一般都是基于TCP/IP协议建立网络连接,因此凡是可以支持TCP/IP的语言几乎都可以和MySQL建立连接
MySQL还支持另一种连接方式,就是Unix系统下的Socket直连,但是这种方式一般使用得比较少
虽然MySQL是基于TCP/IP协议栈实现得连接建立工作,但并非使用HTTP协议建立连接的,一般建立连接的具体协议都会根据不同的客户端实现,这里先不要纠结MySQL的具体的协议类型,先看看MySQL怎么连接
打开命令行,切换到MySQL的bin目录下,然后输入指令
# 本地连接 mysql -u 用户名 -p 密码 # 远程连接 mysql -h 主机号或IP地址 -u 用户名 -p 密码连接成功之后就会在命令行中显式
mysql>这样子的提示符,那么在可以对数据库进行一系列操作删库跑路,建表,crud等操作那么执行完指令之后,
mysql客户端和mysql服务端就已经建立网络连接了,这个过程会经历TCP的三次握手此外,
MySQL还支持SSL加密连接,如果采用了SSL加密连接这个操作,那么客户端和服务端的连接过程还会经历SSL的多次握手过程,握手结束,网络连接成功之后,则会开始正式的数据库连接建立工作
TCP网络连接建立成功之后,MySQL的服务端和MySQL的客户端之间会建立一个session会话,紧接着就会对你登录的用户名和密码进行校验,MySQL首先会查询自身的用户表信息,判断输入的用户名是否存在,如果存在则会判断输入的密码是否正确,如果密码错误或你输入的用户名不存在,就会放回1045错误码,如下
ERROR 1045 (28000): Access denied for user 'JunLa'@'localhost' (using password: YES)
所以,如果你在连接数据库的时候,即客户端和服务端建立连接的过程中,如果出现上述错误,一定是输入的用户名和密码错误导致的,如果你输入的用户名和密码都是正确的,那么就会进行到mysql命令行,接下来就可以执行SQL操作,这个在上面也提到过,连接成功就会有mysql>这样子的一个提示符
注意: 在用户名和密码都正确的情况下,
MySQL还会进行授权操作,查询每个用户拥有的权限,并对其进行授权,后续SQL执行的时候,都会先判断你这个用户是否具体执行相应SQL语句的权限,然后才会进行执行
经过上面的操作,客户端和服务端之间就成功建立连接了,它们之间会采用半双工的通讯机制工作,与之对应的还有全双工,单工的工作模式:
- 半双工:同一时刻内,单方要么只能发送数据,要么只能接收数据,即客户端和服务端其中一方,要么只能接收数据,要么只能发送数据
- 全双工:通讯的双方在同一时间内,既可以发送数据,也可以接收数据
- 单工:在当前这个连接中,只能是客户端发送数据,服务端进行接收,或者服务端发送数据,客户端进行接收,也就是单向类型的通道
MySQL也会安排一条线程去维护当前客户端的连接,这条线程也会时刻标识着当前连接在干什么工作,可以通过show processlist:指令查询所有正在运行的线程
mysql> show processlist;
+------+-----------------+-----------------+------+---------+---------+------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+------+-----------------+-----------------+------+---------+---------+------------------------+------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 2014007 | Waiting on empty queue | NULL |
| 2348 | root | localhost:65361 | NULL | Query | 0 | init | show processlist |
+------+-----------------+-----------------+------+---------+---------+------------------------+------------------+
2 rows in set, 1 warning (0.00 sec)
-
Id:当前线程的Id之,可以利用这俄格Id使用kill强杀线程 -
User:当前线程维护的数据库连接,与之对应的用户是谁,就是与服务端连接的客户端是谁 -
Host:与当前线程保持连接关系的客户端地址(IP+Port) -
db:当前线程在哪个数据库中执行SQL -
command:当前线程正在执行的SQL语句的类型:Create DB:正在执行创建数据库的操作Drop DB:正在执行删除数据库的操作Execute:正在执行预编译的SQL(PreparedStatement)Query:正在执行普通的SQL语句Sleep:正在等待客户端发送SQL语句Quit:当前客户端正在退出连接Shutdown:正在关闭MySQL服务端
-
Time:表示当前线程处于目前状态的时间,单位是秒 -
State:表示当前线程的状态:Updating:当前正在执行update语句,匹配数据做修改操作Sleeping:正在等待客户端发送新的SQL语句Starting:目前正在处理客户端的请求Checking table:目前正在标准查询数据Locked:当前线程被阻塞,其他线程获取了执行需要的锁资源Sending Data:目前执行完成了Select语句,正在将结果放回给客户端
-
Info:一般记录当前线程正在执行的SQL,默认显式前一百个字符,查看完整的SQL可以使用show full processlist;命令进行查看
这条指令在线上排查问题的时候作用很大,得学会
数据库连接池
Connection Pool就是连接池的意思,由于每个客户端来连接服务端都需要服务端派发一条线程去进行维护,但是线程资源是宝贵的,不可能无限创建线程,所以这里的线程就相当于tomcat中的线程池,可以达到线程复用、管理线程、限制最大连接数的作用
数据库连接池的最大线程数可以通过参数max-connections来控制,如果来建立连接的客户端数量超过了该值,那么新到来的客户端就会被拒绝建立连接
-
show variables like '%max_connections%';:查询目前数据库的最大连接数mysql> show variables like '%max_connections%'; +------------------------+-------+ | Variable_name | Value | +------------------------+-------+ | max_connections | 151 | | mysqlx_max_connections | 100 | +------------------------+-------+ 2 rows in set, 1 warning (0.01 sec) -
set GLOBAL max_connections = 200;:修改数据库的最大连接数为指定值 -
show status like "Threads%";:对客户端的连接数进行统计mysql> show status like "Threads%"; +-------------------+-------+ | Variable_name | Value | +-------------------+-------+ | Threads_cached | 8 | | Threads_connected | 1 | | Threads_created | 479 | | Threads_running | 2 | +-------------------+-------+ 4 rows in set (0.02 sec)
每个字段的含义:
Threads_cached:目前空闲的数据库连接数Threads_connected:当前数据库存活的数据库连接数Threads_created:mysql服务端运行至今,累计创建的所有连接数,即累计创建了多少个connectionThreads_running:目前正在执行的数据库连接数
Threads_cached:这个字段就是很缓存相关的,因为有一个数据库内部优化机制,当一个客户端断开连接之后,这个链接不会立马销毁,会放入一个缓存连接池中,那么就省去了创建新线程、分配栈空间等操作,但是存放到缓存中的数量是有限的,一般是32个左右
链接池的优化和java线程池相同,会将数据库新建出来的链接对象放到一个吃着,如果有新的客户端请求连接就直接从这个池中进行获取,就一定程度节省了新建线程的资源消耗
系统服务层
mysql的大多数核心功能都位于这一层,包括客户端SQL的请求解析、语义分析、查询优化、缓存以及所有的内置函数(例如:日期、时间、统计、加密函数等),所有跨引擎的功能都在这一层实现,譬如存储过程、触发器和视图等一系列服务
mysql的管理服务&工具组件这块属于全局的,属于mysql的基础设施服务
SQL接口组件
主要就是从负责处理客户端的SQL语句,当客户端连接建立成功之后,它就会接收客户端的SQL命令,例如DDL、DML语句以及存储过程、触发器等,当它接收到SQL语句时,SQL接口组件会将这些语句分发给其他的组件进行对应的操作,然后等待接收执行结果的放回数据,最后接口组件就会把这些放回结果放回给客户端
简单点说:就是SQL接口会作为客户端连接成功之后传递SQL语句的入口,并且还是数据库放回执行结果的出口
SQL语句的类型:
DML:数据库操作语句,例如insert、update、delete语句DDL:数据库定义语句,create、alter、drop语句DQL:数据库查询语句,最常见的selectDCL:数据库控制语句,grant、revoke控制权限的语句TCL:事务控制语句,例如commit、rollback、setpoint等语句
存储过程 存储过程的本质就是:一段预先写好并且编译好的SQL
指的是提前写好一段较为常用或复杂的
SQL语句,然后指定一个名称存储起来,然后先经过编译、优化,完成之后这个过程会被嵌入到MySQL中
MySQL的触发器 是一种特殊的存储过程,但是它们之间又有一点区别
区别:
存储过程需要手动去调用后才能执行,而触发器可以由某个事件主动去触发执行在
MySQL中支持insert、update、delete三种事件的触发,同时也可以通过alter、before语句声明触发的时机,是在操作执行之前还是执行之后
简单点说就是MySQL触发器就类似于Spring框架的AOP切面
解析器
客户端连接服务端后发送SQL语句,经过SQL接口组件进行分发,分发到解析器,解析器在所有的编程语言中都是存在的,它的主要作用就是做词法分析、语义分析、语法树的生成,详细可以看JVM中的javac编译过程这篇文章,后续会发布的,因为java源码在编写后,会经历这个过程,SQL语言也是一样的
解析器这一步的作用主要是:验证SQL语句是否正确,以及将SQL语句解析成MySQL可以看懂的机器码指令
例如我现在写一条SQL语句
select * form user;运行就会得到如下错误信息:
ERROR 1064 (42000): You have an error in your SQL syntax; check....因为上面的
sql语句我们把from写成了form,MySQL是在词法分析阶段检测到存在语法错误,所有就抛出了对应的错误码及信息,如果你的SQL正确,那自然就会进行下一步工作,生成MySQL能看懂得执行命令
优化器
解析器完成它的工作之后,就会来到优化器,优化器的主要职责就是:生成执行计划,例如选择最合适的索引,选择最合适的join方式等,最终会选择出一套最优的执行计划
那么优化器生成执行计划后,维护当前连接的线程会负责根据计划去执行SQL,这个执行的过程实际是在调用存储引擎所提供的API
缓存&缓冲
主要分为读取缓存和写入缓存的两个操作
读取缓存主要指的是select语句的数据缓存,因为不可能说每次进行select操作都要去数据库中进行获取,会导致性能极差,当然还包含一些权限缓存、引擎缓存等信息
MySQL会对一些经常需要进行的查询SQL语句,将其查询出来的结果存入到cache中,如果下次还是出现相同的sql语句直接从缓存中进行获取数据即可,就不需要去查询数据库来进行获取了,因为走内存是比走磁盘效率要高很多的
# 查询缓存是否开启
show global variables like "%query_cache_type%";
# 查询缓存的空间大小
show global variables like "%query_cache_size%";
# 查询缓存相关的统计信息
show status like'%Qcache%';
如果没有开启就是empty的
字段解析:
Qcache_free_blocks:查询缓存中目前还有多少剩余的blocks。Qcache_free_memory:查询缓存的内存大小。Qcache_hits:表示有多少次查询SQL命中了缓存。Qcache_inserts:表示有多少次查询SQL未命中缓存然后走了磁盘。Qcache_lowmem_prunes:这个值表示有多少条缓存数据从内存中被淘汰。Qcache_not_cached:表示由于自己设置了缓存规则后,有多少条数据不符合缓存条件。Qcache_queries_in_cache:表示当前缓存中缓存的数据数量。Qcache_total_blocks:当前缓存区中blocks的数量。
缓存区的设计主要是:通过内存的速度来弥补磁盘速度较慢对数据库造成的性能影响
当我们在数据库中读取某页数据的时候,会先从磁盘中读取这页的数据,然后把这页的数据存放到缓存区中,后续如果还查询这个页的数据,就可以基于内存来进行操作
一般来说 当你对数据库进行写操作时,都会先从缓冲区中查询是否有你要操作的页,如果有,就直接对内存中的数据页进行操作,例如修改、插入、删除操作,对缓冲区中的数据操作完成之后,会直接给客户端放回操作完成之后的数据,然后MySQL会在后台利用checkpoint机制将内存中更新的数据刷新到磁盘中
MySQL在设计师时,通过缓冲区可以减少大量的磁盘IO,进一步提高数据库的整体性能,如果每次操作都走磁盘,性能就更不上了,电脑都废了
注意 后续高版本的
MySQL移除了查询缓冲区,但是没有移除缓存区缓冲区是跟存储引擎相关的,不同的存储引擎实现也不同,比如
InnoDB的缓冲区叫innodb_buffer_poolMyISAM则叫key_buffer
存储引擎层
存储引擎层主要负责具体的数据操作以及执行工作
Oracle、SQLServer等数据库的实现,这些数据库只有一个存储引擎,因为它们是闭源的,所以它们仅有官方自己提供的一种存储引擎,就像苹果还没有改成type-c的时候,它只适用于他自己官方提供的充电头,但是MySQL是开源的,所以它支持不同的存储引擎实现,MySQL为了能够正常搭载不同的存储引擎运行,因此引擎层是被设计成可拨插式的,也就是根据不同的业务特性,为自己的数据库选择不同的存储引擎
存储引擎在
MySQL中相关的规范标准被定义成了一系列的接口,所以如果你自己想开发属于你自己的存储引擎,你就要根据MySQL AB公司定义的准则编写对应的引擎实现即可
MySQL有很多存储引擎可以选择,最常用的是InnoDB和MyISAM引擎,可以通过指令show variables like '%storage_engine%';来查看当前使用的存储引擎
mysql> show variables like '%storage_engine%';
+---------------------------------+-----------+
| Variable_name | Value |
+---------------------------------+-----------+
| default_storage_engine | InnoDB |
| default_tmp_storage_engine | InnoDB |
| disabled_storage_engines | |
| internal_tmp_mem_storage_engine | TempTable |
+---------------------------------+-----------+
4 rows in set, 1 warning (0.01 sec)
后续会专门讲解MySQL的存储引擎
文件系统层
文件系统层是MySQL数据库的基础,本质上就是基于机器物理磁盘的一个文件系统,其中包含了配置文件、库表结构文件、数据文件、索引文件、日志我呢见等各类MySQL运行时所需的文件,这一层的功能比较简单,主要是和上层的存储引擎层做交互,负责数据的最终存储和持久化工作
文件系统层分为两个模块:日志模块和数据模块
日志模块
在MySQL中主要存在七种常用的日志类型:
-
binlog:二进制文件,主要记录MySQL数据库的所有写操作,即增删改操作 -
redo-log:重写/重做日志,MySLQ崩溃时,对于未落盘的操作会记录在这里,用于重启时重新落盘(InnoDB专有的) -
undo-logs:撤销/回滚日志,记录事务开始前【修改数据】的备份,用于事务回滚 -
error-log:错误日志,记录MySQL启动、运行 、停止时的错误信息 -
general-log:常规日志,主要记录MySQL收到的每一个查询或SQL命令 -
slow-log:慢查询日志,主要记录执行时间较长的SQL -
relay-log:中继日志,主要用于主从数据做数据拷贝后续会在
MySLQ日志篇中进行全面的学习
数据模块
MySQL的所有数据都会写入磁盘,而不同的数据在磁盘空间中,存储的格式是不相同的,所以再列举出一些MySQL中常见的数据文件类型
db.opt文件:主要记录当前数据库使用的字符集和验证规则等信息。.frm文件:存储表结构的元数据信息文件,每张表都会有一个这样的文件。.MYD文件:用于存储表中所有数据的文件(MyISAM引擎独有的)。.MYI文件:用于存储表中索引信息的文件(MyISAM引擎独有的)。.ibd文件:用于存储表数据和索引信息的文件(InnoDB引擎独有的)。.ibdata文件:用于存储共享表空间的数据和索引的文件(InnoDB引擎独有)。.ibdata1文件:这个主要是用于存储MySQL系统(自带)表数据及结构的文件。.ib_logfile0/.ib_logfile1文件:用于故障数据恢复时的日志文件。.cnf/.ini:MySQL的配置文件,Windows下是.ini,其他系统大多为.cnf。
目前只需要了解一下即可
MySQL架构篇总结
目前只是对MySQL的整体架构有一个全局的认知,了解具体的架构分层,后续会再对每一个层的细节点进行深入的分析,学习底层遵循:先看主干,再学细节
参考学习自竹子爱熊猫