作为专栏文章《MySQL 性能优化》的第一篇,本文介绍 MySQL 的服务器体系结构,包括物理结构、逻辑结构以及插件式存储引擎。
我们通常所说的 MySQL 数据库服务器由一个实例(instance)以及一个数据库(database)组成。实例包括一组后台进程/线程和许多内存结构,用于管理数据库;数据库由一组磁盘文件组成,用于存储数据和日志等信息。MySQL 使用典型的客户端/服务器(Client/Server)结构,下图显示了一个简单的 MySQL 体系结构:
客户端通过通过实例中的后台进程访问 MySQL 数据库。MySQL 采用单进程多线程架构,也就是说一个 MySQL 实例在操作系统中就是一个进程(mysqld)。在 Linux 系统中使用ps
命令进行查看:
[root@sqlhost ~]# ps -ef|grep mysql|grep -v grep
mysql 7898 1 1 Feb03 ? 00:12:12 /usr/sbin/mysqld
严格来说,一个 MySQL 实例管理的是多个数据库(也叫模式,Schema)包括系统数据库 mysql、information_schema、performance_schema、sys 以及用户创建的数据库等。使用SHOW DATABASES
或者SHOW SCHEMAS
命令查看当前实例中的数据库:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydb |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.14 sec)
数据库就像是一个容器,存储了各种对象。例如,数据表(Table)、视图(View)、存储过程(Stored Procedure)以及触发器(Trigger)等。其中,表是存储数据的主要对象。它们之间的关系如下图所示:
MySQL 的物理结构主要包括两个目录:软件的安装目录和数据目录,以及配置文件和日志文件等。
不同平台、不同安装方式(源码安装、二进制解压)的目录结构有所不同,具体可以参考 MySQL 官方文档。
安装目录(Base Directory)是 MySQL 服务器的安装路径,Linux 上使用 RPM 包安装的默认位置为 /usr/。安装目录中主要包含以下内容:
文件或目录 | 描述 |
---|---|
bin/ | mysql 客户端和实用程序目录 |
sbin/ | mysqld 服务器程序目录 |
share/man/ | Unix 帮助手册页目录 |
include/mysql/ | 头文件目录 |
lib/mysql/ | 库文件目录 |
share/mysql/ | 各种字符集、语言相关的错误信息目录 |
我们可以使用下面的命令查看安装目录:
mysql> show global variables like "%basedir%";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| basedir | /usr/ |
+---------------+-------+
1 row in set (0.00 sec)
数据目录(Data Directory)是 MySQL 存储数据库文件的位置,Linux 上使用 RPM 包安装的默认位置为 /var/lib/mysql/。数据目录中主要包含以下内容:
文件或目录 | 描述 |
---|---|
#innodb_temp/ | InnoDB 会话临时表空间目录 |
mysql/ | 系统数据库 mysql 文件目录 |
performance_schema/ | 性能数据库 performance_schema 文件目录 |
sys/ | sys 数据库文件目录 |
其他子目录 | 每个数据库对应一个文件目录,存储该数据库中的文件 |
auto.cnf | 当前服务器实例的 UUID,用于主从复制 |
binlog.* | 二进制日志 binary log 相关文件 |
*.pem | SSL 连接相关的证书和密钥文件 |
ib_buffer_pool | 缓冲区 buffer pool 中数据页的页号转储文件 |
ibdata1 | InnoDB 表空间文件 |
ib_logfile0 ib_logfile1 |
InnoDB 事务日志(REDO)文件 |
ibtmp1 | InnoDB 临时表空间文件 |
mysql.ibd | 系统数据库 mysql 数据文件 |
mysql.sock | Unix 套接字文件 |
undo_001 undo_002 |
InnoDB UNDO 表空间文件 |
我们可以使用以下命令查看数据目录:
mysql> show global variables like "%datadir%";
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| datadir | /var/lib/mysql/ |
+---------------+-----------------+
1 row in set (0.01 sec)
MySQL 服务器进程和各种工具程序启动时,需要通过配置文件(my.cnf 或者 my.ini)读取各种参数。Linux 上使用 RPM 包安装的默认配置文件为 /etc/my.cnf,可以使用mysqld --verbose --help
查看读取配置文件的顺序:
[root@sqlhost ~]# mysqld --verbose --help | grep -A 1 'Default options'
Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf
服务器运行使用的所有配置选项都可以使用SHOW VARIABLES
命令查看,例如以下命令可以查看 InnoDB 缓冲池相关的配置:
mysql> show variables like '%buffer_pool%';
+-------------------------------------+----------------+
| Variable_name | Value |
+-------------------------------------+----------------+
| innodb_buffer_pool_chunk_size | 8388608 |
| innodb_buffer_pool_dump_at_shutdown | ON |
| innodb_buffer_pool_dump_now | OFF |
| innodb_buffer_pool_dump_pct | 25 |
| innodb_buffer_pool_filename | ib_buffer_pool |
| innodb_buffer_pool_in_core_file | ON |
| innodb_buffer_pool_instances | 1 |
| innodb_buffer_pool_load_abort | OFF |
| innodb_buffer_pool_load_at_startup | ON |
| innodb_buffer_pool_load_now | OFF |
| innodb_buffer_pool_size | 8388608 |
+-------------------------------------+----------------+
11 rows in set (0.00 sec)
除此之外,MySQL 还使用一些其他的文件:
MySQL 使用典型的客户端/服务器(Client/Server)结构,逻辑结构图如下所示:
MySQL 体系结构大体可以分为三层:客户端、服务器层以及存储引擎层。其中,服务器层又包括了连接管理、查询缓存 、SQL 接口、解析器、优化器、缓冲与缓存以及各种管理工具与服务等。
具体来说,每个组件的作用如下:
我们使用 mysql 客户端工具连接到 MySQL 服务器:
[root@sqlhost ~]# mysql -h 192.168.56.104 -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 18
Server version: 8.0.19 MySQL Community Server - GPL
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
以上方式是通过 TCP/IP 网络协议连接到 MySQL 服务器,需要提供服务器的 IP、端口以及用户名和密码等信息。如果提供的信息不正确,将会返回错误消息。另外,我们也可以通过命名管道或者 UNIX 套接字进行连接。
连接成功之后就可以执行各种语句和命令,我们以一个查询语句为例:
SEELCT * FROM employees;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SEELCT * FROM employees' at line 1
以上语句通过 SQL 查询接口提交给服务器的处理线程,解析器进行语法检查时发现 SELECT 写成了 SEELCT,提示的是语法错误。示例表来自《SQL 入门教程》示例数据库。
如果语句没有问题,通过解析器之后生成解析树,然后发送给优化器;优化器判断是否进行查询重写,并且根据统计信息决定访问表的方式和顺序等。我们可以通过 EXPLAIN 命名了解优化器选择的执行计划:
mysql> EXPLAIN SELECT * FROM employees;
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | employees | NULL | ALL | NULL | NULL | NULL | NULL | 107 | 100.00 | NULL |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
这里我们查询的是整个表的数据,因此 MySQL 采用全表扫描的方式(type = ALL)获取数据。关于执行计划的解释,可以参考这篇文章。
最后,由存储引擎获取表中的数据;如果数据已经被缓存,可以直接从缓冲区获取。
插件式存储引擎是 MySQL 的一大特点体系结构,每个存储引擎都提供了各自的功能,用户可以根据业务或者应用场景为数据表选择不同的存储引擎。也就是说,存储引擎的设置是在表级别的;因此也被称为表类型(table type)。
从 MySQL 5.5 之后,默认的存储引擎是 InnoDB。InnoDB 是一个通用的存储引擎,除非有特殊需求,推荐使用 InnoDB。
MySQL 插件式存储引擎结构允许在 MySQL 服务器运行时装载和卸载一个存储引擎,使用SHOW ENGINES
语句可以查看当前服务器支持的存储引擎:
mysql> SHOW ENGINES;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine | Support | Comment | Transactions | XA | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL |
| MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO |
| InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES |
| PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO |
| MyISAM | YES | MyISAM storage engine | NO | NO | NO |
| MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO |
| BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO |
| CSV | YES | CSV storage engine | NO | NO | NO |
| ARCHIVE | YES | Archive storage engine | NO | NO | NO |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
9 rows in set (0.00 sec)
其中,Support 列显示了是否支持该存储引擎以及默认的存储引擎。MySQL 8.0 支持的存储引擎如下:
存储引擎的设置不在服务器级别,也不在数据库(模式)级别;用户可以为不同的表指定不同的存储引擎。例如,一个应用程序大多数的表使用 InnoDB 存储引擎;同时创建一个 CSV 表,用于将数据导出到电子表格文件中;另外创建一些 MEMORY 表作为临时存储区。
下表描述了 MySQL 中常见存储引擎支持的功能特性。
功能特性 | MyISAM | Memory | InnoDB | Archive | NDB |
---|---|---|---|---|---|
B-树索引 | ✔️ | ✔️ | ✔️ | ❌ | ❌ |
备份/时间点恢复(1) | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
集群数据库 | ❌ | ❌ | ❌ | ❌ | ✔️ |
聚集索引 | ❌ | ❌ | ✔️ | ❌ | ❌ |
压缩数据 | ✔️ (2) | ❌ | ✔️ | ✔️ | ❌ |
数据缓存 | ❌ | N/A | ✔️ | ❌ | ✔️ |
加密数据 | ✔️ (3) | ✔️ (3) | ✔️ (4) | ✔️ (3) | ✔️ (3) |
外键 | ❌ | ❌ | ✔️ | ❌ | ✔️ (5) |
全文搜索索引 | ✔️ | ❌ | ✔️ (6) | ❌ | ❌ |
空间数据类型 | ✔️ | ❌ | ✔️ | ✔️ | ✔️ |
空间数据索引 | ✔️ | ❌ | ✔️ (7) | ❌ | ❌ |
哈希索引 | ❌ | ✔️ | ❌ (8) | ❌ | ✔️ |
索引缓存 | ✔️ | N/A | ✔️ | ❌ | ✔️ |
锁的粒度 | 表级 | 表级 | 行级 | 行级 | 行级 |
MVCC | ❌ | ❌ | ✔️ | ❌ | ❌ |
复制 (1) | ✔️ | 有限(9) | ✔️ | ✔️ | ✔️ |
存储限制 | 256TB | RAM | 64TB | 无 | 384EB |
T-树索引 | ❌ | ❌ | ❌ | ❌ | ✔️ |
数据库事务 | ❌ | ❌ | ✔️ | ❌ | ✔️ |
更新数据字典统计信息 | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
注释:
下一篇我们来具体谈谈 InnoDB 存储引擎的体系结构。欢迎关注❤️、点赞、转发!