本人淘天面试快两小时,前面很顺但最后暴露不知道给哪些列加索引给面试官整懵逼了,遂痛改前非,打算通过系统阅读底层原理的技术书籍从根儿上理解MySQL,望周知
第一章主要是宏观上的架构和对连接过程的介绍,安装和运行MySQL我就给跳过了,看着对新手很友好^ ^
MySQL的C/S架构
在MySQL的C/S架构中,"C" 代表客户端 (Client), Spring Boot 中"C" 可以指代使用 JDBC 或 JPA 连接 MySQL 数据库的 Java 代码。而"S" 代表服务器 (Server),就是我们下载安装的那个MySQL。
MySQL 服务器程序和客户端程序本质上都算是计算机上的一个进程 ,这个代表着 MySQL 服务器程序的进程也被称为 MySQL数据库实例 ,简称数据库实例 。
每个进程都有一个唯一的编号,称为进程ID ,英文名叫 PID ,这个编号是在我们启动程序的时候由操作系统随机分配的,操作系统会保证在某一时刻同一台机器上的进程号不重复。每个进程都有一个 名称,这个名称是编写程序的人自己定义的,比如我们启动的 MySQL 服务器进程的默认名称为 mysqld , 而我们常用的MySQL客户端进程的默认名称为 mysql 。
MySQL启动!
服务器
首先要把MySQL服务器启动了才可以让客户端连接
在 MySQL 的安装目录下有一个重要的 bin 目录,这个目录下存放着许多可执行文件
macOS中./bin/mysqld就可以启动MySQL服务器了,还可以用脚本mysql.server start间接的调用 mysqld_safe,它会间接的调用 mysqld启动。而我在Homebrew里安了,就可以直接拿brew启动
客户端
在我们成功启动 MySQL 服务器程序后,就可以接着启动客户端程序来连接到这个服务器,命令为:mysql -h主机名 -u用户名 -p密码
成功后就会有以下界面,mysql>就说明客户端进程的默认名称为mysql
客户端与服务器连接的过程
运行着的服务器程序和客户端程序本质上都是计算机上的一个进程,所以客户端进程向服务器进程发送请求并得到回复的过程本质上是一个进程间通信的过程。
MySQL 采用 TCP 作为服务器和客户端之间的网络通信协议。在网络环境下,每台计算机都有一个唯一的 IP地址 ,如果某个进程有需要采用 TCP 协议进行网络通信方面的需求,可以向操作系统申请一个端口号 ,这是一个整数值,它的取值范围是 0~65535 。这样在网络中的其他进程就可以通过 IP地址 + 端口号的方式来与这个进程连接,这样进程之间就可以通过网络进行通信了。
MySQL 服务器启动的时候会默认申请 3306 端口号,之后就在这个端口号上等待客户端进程进行连接,用书面一点的话来说, MySQL 服务器会默认监听 3306 端口。
PS.
端口号的取值范围是 0 到 65535 是因为在网络通信中,端口号是用来标识不同的网络服务或应用程序的。端口号是一个无符号的 16 位整数,范围是从 0 到 65535,因为 16 位整数的最大值是 (2^{16} - 1),即 65535。
这个范围的分配是为了确保足够的端口号可供网络应用使用,同时也避免了冲突。通常,0 到 1023 被称为系统端口或者保留端口,用于一些常见的服务,如 HTTP、FTP、SSH 等。而 1024 到 49151 被称为注册端口,一些常见的应用程序使用这个范围内的端口。而 49152 到 65535 被称为动态或私有端口,一般由操作系统动态分配给客户端程序。
如果客户端进程想要使用 TCP/IP 网络来连接到服务器进程,比如我们在使用 mysql 来启动客户端程序时,在 - h 参数后必须跟随 IP地址 来作为需要连接的服务器进程所在主机的主机名,如果客户端进程和服务器进程在一台计算机中的话,我们可以使用 127.0.0.1 来代表本机的 IP地址 。
服务器处理客户端请求
本质是客户端进程向服务器进程发送一段文本(MySQL语句),服务器进程处理后再向客户端进程发送一段文本(处理结果)。
服务器程序处理来自客户端的查询请求大致需要经过三个部分,分别是连接管理 、解析与优化 、存储引擎三部分,如下图所示
连接管理
客户端进程可以采用上边介绍的 TCP/IP (或者命名管道或共享内存 、 Unix域套接字)这几种方式之一来与服务器进程建立连接
每当有一个客户端进程连接到服务器进程时,服务器进程都会创建一个线程来专门处理与这个客户端的交互
当该客户端退出时会与服务器断开连接,服务器并不会立即把与该客户端交互的线程销毁掉,而是把它缓存起来,在另一个新的客户端再进行连接时,把这个缓存的线程分配给该新客户端。这样就起到了不频繁创建和销毁线程的效果,从而节省开销。
在客户端程序发起连接的时候,需要携带主机信息、用户名、密码,服务器程序会对客户端程序提供的这些信息进行认证,如果认证失败,服务器程序会拒绝连接。另外,如果客户端程序和服务器程序不运行在一台计算机 上,我们还可以采用使用了 SSL (安全套接字)的网络连接进行通信,来保证数据传输的安全性。
当连接建立后,与该客户端关联的服务器线程会一直等待客户端发送过来的请求, MySQL 服务器接收到的请求只是一个文本消息,该文本消息还要经过下面的处理
解析与优化
到现在为止, MySQL 服务器已经获得了文本形式的请求,
接下来解析与优化中重要的部分分别是查询缓存 、语法解析和查询优化
查询缓存
MySQL 服务器程序处理查询请求会把刚刚处理过的查询请求和结果缓存起来,如果下一次有一模一样的请求过来,直接从缓存中查找结果就好了。这个查询缓存可以在不同客户端之间共享,也就是说如果客户端A刚刚查询了一个语句,而客户端B之后发送了同样的查询请求,那么客户端B的这次查询就可以直接使用查询缓存中的数据。
但如果两个查询请求在任何字符上的不同(例如:空格、注释、大小写),都会导致缓存不会命中。另外,如果查询请求中包含某些系统函数、用户自定义变量和函数、一些系统表,如 mysql 、information_schema、 performance_schema 数据库中的表,那这个请求就不会被缓存。
不过既然是缓存,那就有它缓存失效的时候。MySQL的缓存系统会监测涉及到的每张表,只要该表的结构或者数 据被修改,如对该表使用了 INSERT 、 UPDATE 、 DELETE 、TRUNCATE TABLE 、 ALTER TABLE 、 DROP TABLE 或DROP DATABASE 语句,那使用该表的所有高速缓存查询都将变为无效并从高速缓存中删除。
查询优化
语法解析之后,服务器程序获得到了需要的信息,比如要查询的列是哪些,表是哪个,搜索条件是什么等等,但光有这些是不够的,因为我们写的 MySQL 语句执行起来效率可能并不是很高, MySQL 的优化程序会对我们的语句 做一些优化,如外连接转换为内连接、表达式简化、子查询转为连接的一堆东西。优化的结果就是生成一个执行计划,这个执行计划表明了应该使用哪些索引进行查询,表之间的连接顺序是啥样的。我们可以使用EXPLAIN 语句来查看某个语句的执行计划。
存储引擎
截止到服务器程序完成了查询优化为止,还没有真正的去访问真实的数据表, MySQL 服务器把数据的存储和提取操作都封装到了一个叫 存储引擎 的模块里。我们知道 表 是由一行一行的记录组成的,但这只是一个逻辑上的概念,物理上如何表示记录,怎么从表中读取数据,怎么把数据写入具体的物理存储器上,这都是 存储引擎 负责的事情。为了实现不同的功能, MySQL 提供了各式各样的 存储引擎 ,不同 存储引擎 管理的表具体的存储结构 可能不同,采用的存取算法也可能不同。
为了管理方便,人们把连接管理 、查询缓存 、语法解析 、查询优化这些并不涉及真实数据存储的功能划分为 MySQL server 的功能,把真实存取数据的功能划分为 存储引擎 的功能。各种不同的存储引擎向上边的 MySQL server 层提供统一的调用接口(也就是存储引擎API),包含了几十个底层函数,像"读取索引第一条内容"、"读取索引下一条内容"、"插入记录"等等。
存储引擎最常用的就是 InnoDB 和 MyISAM ,有时会提一下 Memory 。其中 InnoDB 是 MySQL 默认的存储引擎。(下面的表看了很多次记不住一点
在客户端可以查看引擎是啥,鉴定为InnoDB
我们之前创建表的语句都没有指定表的存储引擎,那就会使用默认的存储引擎 InnoDB (当然这个默认的存储引擎也是可以修改的,我们在后边的章节中再说怎么改)。如果我们想显式的指定一下表的存储引擎,那可以这么写:
CREATE TABLE 表名( 建表语句;
) ENGINE = 存储引擎名称;