当我们使用客户端进程向服务器进程发送一段 SQL语句,然后服务器进程处理后会向客户端进程发送一段文本(处理结果),最终将处理结果呈现在我们面前;那么问题来了:MySQL 内部是如何处理的呢?
ok,废话不多说,直接来看一个图:
下面从连接管理(上图中的连接/线程处理)、解析与优化(上图中的查询缓存、解析器、优化器)、存储引擎三部分来分别解释。
连接管理
每当有一个客户端进程连接到服务器进程时,服务器进程都会创建一个线程来专门处理与这个客户端的交互,当该客户端退出时会与服务器断开连接,服务器并不会立即把与该客户端交互的线程销毁掉,而是把它缓存起来,在另一个新的客户端再进行连接时,把这个缓存的线程分配给该新客户端。这样就起到了不频繁创建和销毁线程的效果,从而节省开销。从这一点大家也能看出,MySQL服务器会为每一个连接进来的客户端分配一个线程,但是线程分配的太多了会严重影响系统性能,所以我们也需要限制一下可以同时连接到服务器的客户端数量。
- 在客户端程序发起连接的时候,需要携带主机信息、用户名、密码,服务器程序会对客户端程序提供的这些信息进行认证,如果认证失败,服务器程序会拒绝连接。另外,如果客户端程序和服务器程序不运行在一台计算机上,我们还可以采用使用了SSL(安全套接字)的网络连接进行通信,来保证数据传输的安全性。
- 简单理解:该阶段主要完成一些类似于连接处理、授权认证、及相关的安全方案
解析与优化
当连接建立后,与该客户端关联的服务器线程会一直等待客户端发送过来的请求,MySQL服务器接收到的请求只是一个文本消息,该文本消息还要经过各种处理。
查询缓存
MySQL服务器程序处理查询请求的时候,会把刚刚处理过的查询请求和结果缓存起来,如果下一次有一模一样的请求过来,直接从缓存中查找结果就好了。这个查询缓存可以在不同客户端之间共享,也就是说如果客户端A刚刚查询了一个语句,而客户端B之后发送了同样的查询请求,那么客户端B的这次查询就可以直接使用查询缓存中的数据。
另外,需要注意的是:
- 缓存无法命中:如果两个查询请求在任何字符上的不同(例如:空格、注释、大小写),都会导致缓存不会命中。
- 请求不会被缓存的情况:如果查询请求中包含某些系统函数、用户自定义变量和函数、一些系统表,如 mysql 、information_schema、 performance_schema 数据库中的表,那这个请求就不会被缓存。(以某些系统函数举例,可能同样的函数的两次调用会产生不一样的结果,比如函数NOW,每次调用都会产生最新的当前时间。这类请求根本没有缓存的意义,也不会被缓存。)
- 缓存失效:MySQL的缓存系统会监测涉及到的每张表,只要该表的结构或者数据被修改,如对该表使用了INSERT、 UPDATE、DELETE、TRUNCATE TABLE、ALTER TABLE、DROP TABLE或 DROP DATABASE语句,那使用该表的所有高速缓存查询都将变为无效并从高速缓存中删除!
虽然查询缓存有时可以提升系统性能,但也不得不因维护这块缓存而造成一些开销,比如每次都要去查询缓存中检索,查询请求处理完需要更新查询缓存,维护该查询缓存对应的内存区域。从MySQL 5.7.20开始,不推荐使用查询缓存,并在MySQL 8.0中删除。
语法解析
如果查询缓存没有命中,接下来就需要进入正式的查询阶段了。因为客户端程序发送过来的请求只是一段文本而已,所以MySQL服务器程序首先要对这段文本做分析,判断请求的语法是否正确,然后从文本中将要查询的表、各种查询条件都提取出来放到MySQL服务器内部使用的一些数据结构上来。
查询优化
语法解析之后,服务器程序获得到了需要的信息,比如要查询的列是哪些,表是哪个,搜索条件是什么等等,但光有这些是不够的,因为我们写的MySQL语句执行起来效率可能并不是很高,MySQL的优化程序会对我们的语句做一些优化,如外连接转换为内连接、表达式简化、子查询转为连接吧啦吧啦的一堆东西。优化的结果就是生成一个执行计划,这个执行计划表明了应该使用哪些索引进行查询,表之间的连接顺序是啥样的。我们可以使用EXPLAIN语句来查看某个语句的执行计划。
存储引擎
截止到服务器程序完成了查询优化为止,还没有真正的去访问真实的数据表,MySQL服务器把数据的存储和提取操作都封装到了一个叫存储引擎的模块里。我们知道表是由一行一行的记录组成的,但这只是一个逻辑上的概念,物理上如何表示记录,怎么从表中读取数据,怎么把数据写入具体的物理存储器上,这都是存储引擎负责的事情。为了实现不同的功能,MySQL提供了各式各样的存储引擎,不同存储引擎管理的表具体的存储结构可能不同,采用的存取算法也可能不同。
为了管理方便,人们把连接管理、查询缓存、语法解析、查询优化这些并不涉及真实数据存储的功能划分为MySQL server的功能,把真实存取数据的功能划分为存储引擎的功能。各种不同的存储引擎向上边的MySQL server层提供统一的调用接口(也就是存储引擎API),包含了几十个底层函数,像"读取索引第一条内容"、“读取索引下一条内容”、"插入记录"等等。
所以在MySQL server完成了查询优化后,只需按照生成的执行计划调用底层存储引擎提供的API,获取到数据后返回给客户端就好了。
常用存储引擎
MySQL
支持非常多种存储引擎,这里只简单列举常用的:
存储引擎 | 描述 |
---|---|
InnoDB | 具备外键支持功能的事务存储引擎 |
MEMORY | 置于内存的表 |
MyISAM | 主要的非事务处理存储引擎 |
… |
同时,我们最常用的就是InnoDB
和MyISAM
,有时会提一下Memory
。其中InnoDB
是MySQL
默认的存储引擎。
Feature | MyISAM | Memory | InnoDB |
---|---|---|---|
B-tree indexes(B树索引) | yes | yes | yes |
Backup/point-in-time recovery(备份/恢复) | yes | yes | yes |
Cluster database support(支持数据库集群) | no | no | no |
Clustered indexes(聚簇索引) | no | no | yes |
Compressed data(数据压缩) | yes | no | yes |
Data caches(数据缓存) | no | N/A | yes |
Encrypted data(数据加密) | yes | yes | yes |
Foreign key support(支持外键) | no | no | yes |
Full-text search indexes(全文索引) | yes | no | yes |
Geospatial data type support(支持GEO数据类型) | yes | no | yes |
Geospatial indexing support(支持GEO索引) | yes | no | yes |
Hash indexes(哈希索引) | no | yes | no |
Index caches(索引缓存) | yes | N/A | yes |
Locking granularity(锁粒度) | Table | Table | Row |
MVCC | no | no | yes |
Query cache support(支持查询缓存) | yes | yes | yes |
Replication support(支持拷贝) | yes | Limited | yes |
Storage limits(存储容量) | 256TB | RAM | 64TB |
T-tree indexes(T树索引) | no | no | no |
Transactions(事务) | no | no | yes |
Update statistics for data dictionary(更新字典统计数据) | yes | yes | yes |
参考:
https://dev.mysql.com/doc/