我们平时玩的网络游戏,比如DOTA和LOL等的运行方式和MySQL有相似之处。我们把游戏的客户端下载到电脑上,登录客户端开始游戏,每个人都有自己的账号和密码,通过客户端连接到游戏公司的服务端,服务端负责交换客户端发来的数据,并返回给客户端,从而我们能看到玩游戏的时候是不是能打中技能。
1. MySQL的客户端/服务器架构
MySQL的原理与之类似,它的服务器程序直接负责维护我们的存储数据,而多个客户端可以通过不同的账号和密码与服务端程序建立连接,发送数据修改请求,从而操作和维护数据。
我们日常使用MySQL的流程是这样的:
- 启动MySQL服务器程序。我用的Mac电脑可以在设置中启动MySQL服务。
- 启动
MySQL
客户端程序并连接到服务器程序。在Mac中,有时是使用Terminal通过账号密码登录到客户端,与服务端建立连接。 - 在客户端程序中输入一些命令语句作为请求发送到服务器程序,服务器程序收到这些请求后,会根据请求的内容来操作具体的数据并向客户端返回操作结果。
MySQL服务端程序和客户端程序都是计算机的进程,MySQL服务器程序的进程叫作MySQL数据库实例,简称数据库实例。
2. 客户端与服务器连接的过程
客户端程序和服务端程序本质上都是进程,所以客户端进程向服务端进程发送请求并得到回复的过程本质上是进程间通信的过程。MySQL支持以下三种客户端和服务器进程的通信方式。
2.1 TCP/IP
数据库服务器进程和客户端进程可能运行在不同的主机中,它们之间必须通过网络来进行通讯。MySQL
采用TCP
作为服务器和客户端之间的网络通信协议。在网络环境下,每台计算机都有一个唯一的IP地址
,如果某个进程有需要采用TCP
协议进行网络通信方面的需求,可以向操作系统申请一个端口号
,这是一个整数值,它的取值范围是0~65535
。这样在网络中的其他进程就可以通过IP地址 + 端口号
的方式来与这个进程连接,这样进程之间就可以通过网络进行通信了。
MySQL
服务器启动的时候会默认申请3306
端口号,之后就在这个端口号上等待客户端进程进行连接,用书面一点的话来说,MySQL
服务器会默认监听3306
端口。
如果客户端进程想要使用TCP/IP
网络来连接到服务器进程,比如我们在使用mysql
来启动客户端程序时,在-h
参数后必须跟随IP地址
来作为需要连接的服务器进程所在主机的主机名,如果客户端进程和服务器进程在一台计算机中的话,我们可以使用127.0.0.1
来代表本机的IP地址
。另外,如果服务器进程监听的端口号不是默认的3306
,我们也可以在使用mysql
启动客户端程序时使用-P
参数(大写的P
,小写的p
是用来指定密码的)来指定需要连接到的端口号。比如我们现在已经在本机启动了服务器进程,监听的端口号为3307
,那我们启动客户端程序时可以这样写:
mysql -h127.0.0.1 -uroot -P3307 -p
2.2 命名管道和共享内存
对于Windows
用户,客户端进程和服务器进程之间可以考虑使用命名管道
或共享内存
进行通信。不过启用这些通信方式的时候需要在启动服务器程序和客户端程序时添加一些参数。
2.3 Unix域套接字文件
如果我们的服务器进程和客户端进程都运行在同一台操作系统为类Unix
的机器上的话,我们可以使用Unix域套接字文件
来进行进程间通信。如果我们在启动客户端程序的时候指定的主机名为localhost
,或者指定了--protocol=socket
的启动参数,那服务器程序和客户端程序之间就可以通过Unix
域套接字文件来进行通信了。
3. 服务器处理客户端请求
无论客户端与服务端用何种方式通信,本质都是客户端进程向服务端进程发送一段MySQL
语句,服务器进程处理后再向客户端进程发送处理结果。客户端可以向服务器发送增删改查请求,以查询为例介绍下处理过程:
从图中我们可以看出,服务器程序处理来自客户端的查询请求大致需要经过三部分,分别是连接管理
、解析与优化
、存储引擎
。
3.1 连接管理
每当有一个客户端进程连接到服务器进程时,服务器进程都会创建一个线程来专门处理与这个客户端的交互,当该客户端退出时会与服务器断开连接,服务器并不会立即把与该客户端交互的线程销毁掉,而是把它缓存起来,在另一个新的客户端再进行连接时,把这个缓存的线程分配给该新客户端。这样就起到了不频繁创建和销毁线程的效果,从而节省开销。
在客户端程序发起连接的时候,需要携带主机信息、用户名、密码,服务器程序会对客户端程序提供的这些信息进行认证,如果认证失败,服务器程序会拒绝连接。另外,如果客户端程序和服务器程序不运行在一台计算机上,我们还可以采用使用了SSL
(安全套接字)的网络连接进行通信,来保证数据传输的安全性。
当连接建立后,与该客户端关联的服务器线程会一直等待客户端发送过来的请求,MySQL
服务器接收到的请求只是一个文本消息,该文本消息还要经过各种处理。
3.2 解析与优化
服务器收到了请求之后,还要进行查询缓存
、语法解析
和查询优化
过程。
3.2.1 查询缓存
MySQL会把处理过的查询请求和结果缓存起来,如果下次有请求需要这些数据,就会直接从缓存里拿到结果返回出去,就不需要再重新查找一次了。这个查询缓存可以在不同客户端之间共享,也就是说如果客户端A刚刚查询了一个语句,而客户端B之后发送了同样的查询请求,那么客户端B的这次查询就可以直接使用查询缓存中的数据。
但是,如果两个查询请求有任何字符上的不同,都会导致缓存不会命中。另外,如果查询请求中包含某些系统函数、用户自定义变量和函数、一些系统表,如mysql、information_schema、performance_schema数据库中的表,那这个请求就不会被缓存。以某些系统函数举例,可能同样的函数的两次调用会产生不一样的结果,比如函数NOW
,每次调用都会产生最新的当前时间,如果在一个查询请求中调用了这个函数,那即使查询请求的文本信息都一样,那不同时间的两次查询也应该得到不同的结果,如果在第一次查询时就缓存了,那第二次查询的时候直接使用第一次查询的结果就是错误的。
不过既然是缓存,那就有它缓存失效的时候。MySQL
的缓存系统会监测涉及到的每张表,只要该表的结构或者数据被修改,那使用该表的所有高速缓存查询都将变为无效并从高速缓存中删除。
3.2.2 语法解析
如果查询缓存没有命中,接下来就需要进入正式的查询阶段了。因为客户端程序发送过来的请求只是一段文本而已,所以MySQL
服务器程序首先要对这段文本做分析,判断请求的语法是否正确,然后从文本中将要查询的表、各种查询条件都提取出来放到MySQL
服务器内部使用的一些数据结构上来。
3.2.3 查询优化
语法解析之后,服务器程序获得到了需要的信息,例如表信息,行列信息等等。我们的原始MySQL
语句执行效率可能不高,MySQL
的优化程序会对我们的语句做一些优化。优化的结果就是生成一个执行计划,这个执行计划表明了应该使用的索引,表之间的连接顺序等等。我们可以使用EXPLAIN
语句来查看某个语句的执行计划。
3.3 存储引擎
截止到服务器程序完成了查询优化为止,还没有真正的去访问真实的数据表,MySQL
服务器把数据的存储和提取操作都封装到了一个叫存储引擎
的模块里。我们知道表
是由一行一行的记录组成的,但这只是一个逻辑上的概念,物理上如何表示记录,怎么从表中读取数据,怎么把数据写入具体的物理存储器上,这都是存储引擎
负责的事情。为了实现不同的功能,MySQL
提供了各式各样的存储引擎
,不同存储引擎
管理的表具体的存储结构可能不同,采用的存取算法也可能不同。
为了管理方便,人们把连接管理
、查询缓存
、语法解析
、查询优化
这些并不涉及真实数据存储的功能划分为MySQL server
的功能,把真实存取数据的功能划分为存储引擎
的功能。各种不同的存储引擎向上边的MySQL server
层提供统一的调用接口(也就是存储引擎API),包含了几十个底层函数,像”读取索引第一条内容”、”读取索引下一条内容”、”插入记录”等等。
所以在MySQL server
完成了查询优化后,只需按照生成的执行计划调用底层存储引擎提供的API,获取到数据后返回给客户端就好了。
4. 常用存储引擎
MySQL
支持非常多种存储引擎
存储引擎 | 描述 |
---|---|
ARCHIVE |
用于数据存档(行被插入后不能再修改) |
BLACKHOLE |
丢弃写操作,读操作会返回空内容 |
CSV |
在存储数据时,以逗号分隔各个数据项 |
FEDERATED |
用来访问远程表 |
InnoDB |
具备外键支持功能的事务存储引擎 |
MEMORY |
置于内存的表 |
MERGE |
用来管理多个MyISAM表构成的表集合 |
MyISAM |
主要的非事务处理存储引擎 |
NDB |
MySQL集群专用存储引擎 |
我们常用的引擎只有InnoDB
和MyISAM
参考文章:
文档信息
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
- 本文链接:MySQL运行原理(Principle)