无论你是前端还是后端,只要是一个合格的开发者,对于MySQL
这个名词相信都不陌生,MySQL
逐渐成为了最受欢迎的关系型数据库,无论你是大前端,亦或是Java、Go、Python、C/C++、PHP....
等这些语言的程序员,对于MySQL
是必然要掌握的核心技术之一,程序员不能没有MySQL
,就像西方不能失去耶路撒冷一般。
当然,MySQL
也不仅仅是唯一的数据库,与它类似的关系型数据库竞品还有很多,例如Oracle、SQLServer、PostgreSQL、DB2....
,这其中使用最为广泛的是Oracle
,但Oracle
实际上并不怎么受程序员欢迎,或者说Oracle
并不怎么受中小企业的Boss
欢迎,原因嘛大家都清楚,无非因为它收费罢了。
也正是由于
Oracle
收费的原因,才导致MySQL
像如今这么流行,正所谓时势造英雄,MySQL
作为免费的开源数据库,也正是抓住了这个风口,所以才越发流行。对于MySQL
,用一句话形容很贴切:“天不生我MySQL
,编程万古如长夜”。
一、MySQL概述与系列预告
MySQL
数据库是由瑞典的MySQL AB
公司开发的,后面这家企业被Sun
公司收购,最后Sun
公司又被Oracle
以74
亿美元收购,所以本质上MySQL
现在隶属于Oracle
旗下,因此大家也会发现,MySQL
后面的高版本会有收费版出现。
实际上如果
MySQL
没有并入Oracle
的话,是有很大几率问鼎数据库榜首的,造化弄人。
整个MySQL系列会按上述目录进行全面阐述,但上述目录只是预期规划内容,实际撰写过程中可能会适当调整,但给出的技术点都会事无巨细的讲到,内容只多不少,因此大家感兴趣的话,可以点个关注,由我伴随诸君一同彻底掌握MySQL
数据库。
二、MySQL整体结构浅析
本章作为MySQL
系列的开篇之作,当然也有一定的原因,毕竟只有先对MySQL
的整体架构有了一个宏观的认知,才能更好的理解每个细节点的知识。
MySQL
与我们开发项目时相同,为了能够合理的规划整体架构设计,也会将整个MySQL
服务抽象成几个大的模块,然后在内部进行实现,因此先来看看MySQL
的整体架构,开局先上一张图:
从上往下看,依次会分为网络连接层、系统服务层、存储引擎层、以及文件系统层,往往编写SQL
后,都会遵守着MySQL
的这个架构往下走。
- 连接层:主要是指数据库连接池,会负责处理所有客户端接入的工作。
- 服务层:主要包含
SQL
接口、解析器、优化器以及缓存缓冲区四块区域。 - 存储引擎层:这里是指
MySQL
支持的各大存储引擎,如InnoDB、MyISAM
等。 - 文件系统层:涵盖了所有的日志,以及数据、索引文件,位于系统硬盘上。
OK~,除了上述的四层外,还有客户端,这个客户端可以是各类编程语言,如Java、Go、Python、C/C++、PHP、Node、.Net....
,也可以是一些数据库的可视化软件,例如Navicat、SQLyog
等,也可以是mysql-cli
命令行工具。总之,只要能与MySQL
建立网络连接,都可以被称为是MySQL
的客户端。
MySQL-Server
就是上述图中的那玩意儿,一般来说,客户端负责编写SQL
,而服务端则负责SQL
的执行与数据的存储。
对MySQL
的整体架构有了简单了解后,接下来详细的拆解一下MySQL-Server
的每个层面。
三、网络连接层
在之前的《网络之旅》的文章中,我们提到过一点:当一个客户端尝试与MySQL
建立连接时,MySQL
内部都会派发一条线程负责处理该客户端接下来的所有工作。而数据库的连接层负责的就是所有客户端的接入工作,MySQL
的连接一般都是基于TCP/IP
协议建立网络连接,因此凡是可以支持TCP/IP
的语言,几乎都能与MySQL
建立连接。
其实
MySQL
还支持另一种连接方式,就是Unix
系统下的Socket
直连,但这种方式一般使用的较少。
虽然MySQL
是基于TCP/IP
协议栈实现的连接建立工作,但并非使用HTTP
协议建立连接的,一般建立连接的具体协议,都会根据不同的客户端实现,如jdbc、odbc...
这类的。在这里先暂且不纠结连接MySQL
时的协议类型,先来看看一般是怎么连接MySQL
的?如下:
mysql -h 127.0.0.1 -uroot -p123456
例如上述这条指令,-h
表示MySQL
所在的服务器IP
地址,-u
表示本次连接所使用的用户名,-p
则代表着当前用户的账号密码,当执行这条指令后,会与MySQL-Server
建立网络连接,也就是会经历《TCP的三次握手过程》。当然,MySQL
也支持SSL
加密连接,如果采用这种方式建立连接,那还会经过《SSL多次握手过程》,当握手结束,网络建立成功后,则会开始正式的数据库连接建立工作。
TCP
网络连接建立成功后,MySQL
服务端与客户端之间会建立一个session
会话,紧接着会对登录的用户名和密码进行效验,MySQL
首先会查询自身的用户表信息,判断输入的用户名是否存在,如果存在则会判断输入的密码是否正确,如若密码错误或用户名不存在就会返回1045
的错误码,如下信息:
ERROR 1045 (28000): Access denied for user 'zhuzi'@'localhost' (using password: YES)
如果你在连接数据库的过程中,出现了上述的错误信息,那绝对是你输入的用户名或密码错误导致的,当账号及密码正确时,此时就会进入MySQL
的命令行,接下来可以执行SQL
操作。
但实际上,在用户名和密码都正确的情况下,
MySQL
还会做一些些小动作,也就是会进行授权操作,查询每个用户所拥有的权限,并对其授权,后续SQL
执行时,都会先判断是否具备执行相应SQL
语句的权限,然后再执行。
OK~,经过上述流程后数据库连接就建立成功了,数据库连接建立成功后,MySQL
与客户端之间会采用半双工的通讯机制工作,与之对应的还有“全双工、单工”的工作模式:
- 全双工:代表通讯的双方在同一时间内,即可以发送数据,也可以接收数据。
- 半双工:代表同一时刻内,单方要么只能发送数据,要么只能接受数据。
- 单工:当前连接只能发送数据或只能接收数据,也就是“单向类型的通道”。
到这里,MySQL
也会“安排”一条线程维护当前客户端的连接,这条线程也会时刻标识着当前连接在干什么工作,可以通过show processlist;
命令查询所有正在运行的线程:
执行结果如下(
root
账号可以查询所有线程):
Id
:当前线程的ID
值,可以利用这个ID
,使用kill
强杀线程。User
:当前线程维护的数据库连接,与之对应的用户是谁。Host
:与当前线程保持连接关系的客户端地址(IP+Port
)。db
:目前线程在哪个数据库中执行SQL
。Command
:当前线程正在执行的SQL
类型,如:Create DB
:正在执行创建数据库的操作。Drop DB
:正在执行删除数据库的操作。Execute
:正在执行预编译的SQL
(PreparedStatement
)。Close Stmt
:正在关闭一个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;
命令。
其实从这个结果上来看,我们能够很明显的看到数据库中各个线程的信息,这条指令对于以后做线上排查时有很大的作用,目前先简单了解,接着来看看数据库连接池。
3.1、数据库连接池(Connection Pool)
Connection Pool
翻译过来的意思就是连接池,那为什么需要有这个东西呢?因为前面聊到过,所有的客户端连接都需要一条线程去维护,而线程资源无论在哪里都属于宝贵资源,因此不可能无限量创建,所以这里的连接池就相当于Tomcat
中的线程池,主要是为了复用线程、管理线程以及限制最大连接数的。
连接池的最大线程数可以通过参数max-connections
来控制,如果到来的客户端连接超出该值时,新到来的连接都会被拒绝,关于最大连接数的一些命令主要有两条:
show variables like '%max_connections%';
:查询目前DB
的最大连接数。set GLOBAL max_connections = 200;
:修改数据库的最大连接数为指定值。
对于不同的机器配置,可以适当的调整连接池的最大连接数大小,以此可以在一定程度上提升数据库的性能。除了可以查询最大连接数外,MySQL
本身还会对客户端的连接数进行统计,对于这点可以通过命令show status like "Threads%";
查询:
其中各个字段的释义如下:
Threads_cached
:目前空闲的数据库连接数。Threads_connected
:当前数据库存活的数据库连接数。Threads_created
:MySQL-Server
运行至今,累计创建的连接数。Threads_running
:目前正在执行的数据库连接数。
对于几个字段很容易理解,额外要说明的一点是Threads_cached
这个字段,从名称上来看,似乎跟缓存有关系,其实也没错,因为这里是有一个数据库内部的优化机制。当一个客户端连接断开后,对于数据库连接却不会立马销毁,而是会先放入到一个缓存连接池当中。这样就能在下次新连接到来时,省去了创建线程、分配栈空间等一系列动作,但这个值不会是无限大的,一般都在32
左右。
连接池的优化思想与
Java
线程池相同,会将数据库创建出的连接对象放入到一个池中,一旦出现新的访问请求会复用这些连接,一方面提升了性能,第二方面还节省了一定程度上的资源开销。