MySQL的客户端/服务端架构
以我们使用最多的微信为例,它其实是有两个部分组成的,客户端和服务器端。客户端可以有很多形式,比如:手机APP、PC、网页等;每一个客户端都有一个唯一的用户名,就是你的微信号,另一方面,腾讯在他们的机房里运行这一个服务端程序。我们平时其实就是是有客户端来和这个服务器端打交道。比如给好友发送一条信息的过程其实是这个样的
- 消息被客户端包装好,添加发送者和接收者,然后我们把消息从客户端中发送带服务器端;
- 服务器端接收到消息之后,从中解析出发送者和接收着的信息,根据消息接收者把这条消息送达到好友的微信中。如果好友不在线,服务器会缓存这个条消息,等好友上线之后再将消息推送到好友手机上
MySQL的使用过程和这个是一样的,服务器程序直接和存储的数据打交道,然后可以有好多不同的客户端连接到这个服务器程序,发送增删改查的请求,然后服务器端响应这些请求,从而操作它维护的数据。也需要用户名和密码,客户端才能连接上服务器程序
日常使用MySQL流程
- 启动MySQL服务器程序
- 启动MySQL客户端程序并连接到服务器端
- 再客户端中输入一些命令作为请求发送到服务器端,服务器接收到请求后,会根据内容来操作具体的数据,并向客户端返回操作结果
MySQL的客户端或服务器端就是运行在计算机上的一个进程;MySQL服务器端的进程被称为MySQL数据库实例,简称数据库实例;每个进程都有一个唯一的ID,PID,这个id是在服务器启动时有操作系统随机分配的,每一个进程都一个名称,这个名称是由编写程序的人定义个,MySQL服务器的名称为“mysqld”、MySQL客户端的名称为“mysql”
Centos安装MySQL8
下载MySQL安装文件
选择Linux通用安装
上传到服务器中
- 解压安装文件
解压mysql-8.0.28-linux-glibc2.12-x86_64.tar
tar -xvf mysql-8.0.28-linux-glibc2.12-x86_64.tar
解压mysql-8.0.28-linux-glibc2.12-x86_64.tar.xz
tar xvJf mysql-8.0.28-linux-glibc2.12-x86_64.tar.xz -C /usr/local/
改名
mv mysql-8.0.28-linux-glibc2.12-x86_64 mysql
- 创建/usr/data/mysql/data文件夹保存数据
mkdir -p /usr/data/mysql/data
- 创建用户和组,并改变mysql目录的拥有者
groupadd mysql
useradd -r -g mysql mysql
chown -R mysql:mysql /usr/local/mysql
- 修改/etc/my.cnf文件,若无则手动创建
[mysqld]
basedir=/usr/local/mysql
datadir=/usr/local/mysql/data
socket=/usr/local/mysql/mysql.sock
- 进入/usr/local/mysql/bin,初始化数据库,并保存好临时密码
./mysqld --initialize --user=mysql
- 后台启动MySQL
./mysqld --user=mysql &
- 使用临时密码登录
./mysql -uroot -p
这一步可能会报错:ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)
解决方案1:给自动生成的mysql.sock建立一个软链接
ln -s /usr/local/mysql/mysql.sock /tmp/mysql.sock
解决方案2:修改/etcmy.cnf文件,添加
[client]
port=3306
socket=/usr/local/mysql/mysql.sock
- 登录后,修改密码
alter user
root
@localhost
identified by '123456';
- 创建用户并修改权限,以便在其它机器进行访问
create user
root
@%
identified by '123456';-- 给在localhost登录的root用户开放权限
grant all privileges on . toroot
@localhost
;-- 给在任意机器登录的root用户开放权限
grant all privileges on . toroot
@%
;flush privileges;
到此MySQL8 在liunx上已经安装成功。
MySQL服务启动程序
安装完成之后查看/usr/local/mysql/bin/文件结构
tree /usr/local/mysql/bin/
可以发现在MySQL的安装目录的bin目录下启动MySQL服务器程序的可执行文件很多
挑一些重要的记录
mysqld
mysqld表示MySQL服务器程序,运行这个可执行文件就可以直接启动一个MySQL进程
mysqld_safe
mysqld_safe是一个启动脚步,它会间接的调用mysqld,而且还会顺便启动一个监控进程,这个监控进程在服务器进程挂了的时候,可以帮他重启。另外使用mysqld_safe启动服务器程序时,它会将服务器程序出现的错误信息和其他诊断信息输入到指定文件中,产生出错误日志,这样也方便我们在发生错误之后查找错误原因。
mysql.server
mysql.server是support-files目录下的一个启动脚步,它会间接的调用mysqld_safe,在调用mysql.server时在后面指定start参数就可以启动服务器端程序了
mysql.server start
要关闭服务器端程序只需要将start参数更换为stop参数即可
mysql.server stop
mysqld_multi
mysqld_multi可以在一台计算机中运行多个服务器实例,也就是运行多个MySQL服务器进程,mysql_multi可执行文件可以对每一个服务器进程的启动或停止进行监控。
启动MySQL客户端程序
在成功启动MySQL服务器端程序之后,就可以接着启动客户端程序来连接到这个服务器了,在bin目录下有许多客户端程序,比如:mysqladmin、mysqldump、mysqlcheck等等。单是使用最多的是mysql这个命令,通过这个可执行文件可以让Wimbledon和服务器程序进程进行交互,也就是发送请求,接收服务器的处理结果。使用这个命令时需要指定有一些参数,格式如下
mysql -h主机名 -u用户名 -p密码
各个参数含义
|参数|含义|
|-------|-------|
|-h|表示服务器进程所在计算机的域名或ip地址,如果服务器进行就运行在本机,可以省略这个参数,或者填localhost/127.0.0.1。也可以写作--host=主机名/ip的形式|
|-u|表示用户名。也可写作 --user=用户名 形式|
|-p|小写P表示密码。也可写作 --password=密码 形式|
|-P|大写P表示端口。也可写作 --port=端口号 形式|
像 h、u、p这样名称只有一个单词的参数为短形式参数,在使用时前面只需要添加一个短横线,而host、user、password这种长形式参数在使用时需要在前面添加两个短横线
连接服务器端
./mysql -hlocalhost -uroot -p123456
连接成功界面
最后一行的 mysql> 是一个客户端的提示符,之后客户端要发送给服务器端的命令都写在这个提示符后面。
操作完成之后要断开与服务器端的连接可以再 mysql> 后面输入 quit、exit、\q 三个命令中的任意一个即可
客户端与服务器端的连接过程
MySQL的客户端和服务器端本质上就是计算机操作系统上的一个进程,所以客户端进程向服务器端进程发送请求并得到回复的过程本质上就是进程之间通信的过程;在MySQL中支持有三种客户端进程和服务器端进程的通信方式。
TCP/IP
在真实环境中,数据库服务器端进程和客户端进程可能运行在不同的主机中,它们之间必须通过网络进行通讯。MySQL采用TCP作为服务器端和客户端之间的网络同学协议。在网络环境下每台计算机都一个唯一的ip地址,而且每一个MySQL服务器端进程都会想计算机申请一个唯一的端口号,它是一个整数值,范围在0 ~ 65535。这样在网络通信中就可以通过ip地址找到服务主机,然后通过端口找到主机上的MySQL进程,这样服务器端和客户端就可以通过网络进行通信了。mysql在启动时会默认申请3306端口
命名管道和共享内存
如果服务器端和客户端都在windows中,那么可以考虑使用命名管道或共享内存进行通信。要想使用只需要在服务器启动时添加一些命令即可
- 管道命名:--enable-named-pipe
- 共享内存:--shared-memory
在客户端连接时也需要直接参数 - 管道命名:--pipe 或者 --protocol=pipe
- 共享内存:--protocol=memory
Unix域套接字文件
如果MySQL的服务器端和客户端都运行在Unix中,就可以使用Unix域套接字来进行进程通信。如果在连接服务器端时指定的主机名为localhost或使用了参数 --protocol=socket,那么服务器端进程与客户端进程就可以通过Unix域套接字文件进行通信了。MySQL服务器程序默认监听Unix域套接字文件路径为/tem/mysql.socket,客户端程序也默认连接到这个Unix域套接字文件。如果我想改变这个文件的名称或路径可以在MySQL服务器启动的时候指定socket参数
mysqld --socket=/tem/a.txt
服务器端启动时改变了Unix域套接字文件,那么客户端在连接时也要指定修改后的Unix域套接字文件。
mysql -hlocalhost --socket=/tem/a.txt -uroot -p
服务器端处理客户端请求
不论客户端和服务器端进程采用哪种方式进行通信,最后实现的效果都是:客户端进程向服务器端进程发送一段文本(MySQL语句),服务器端进程处理后在向客户端进程发送一段文本(处理结果)。
处理流程
从图中可以看出,服务器程序处理来自客户端的查询请求大致需要经过三个部分,连接管理、解析与优化、存储引擎。
连接管理
客户端进程可以使用上面介绍的三种方式中任意一种来与服务器端建立连接,每当有一个客户端进程连接到服务器进程时,服务器进程都会创建一个线程来专门处理这个客户端的交互,当客户端断开与服务器进程的连接时,服务器进程并不会立即把与该客户端交互的线程回收,而是缓存起来,当有其他客户端连接到服务器进程时,就把这个缓存的线程分配给新的客户端,这样就避免了频繁的创建和销毁现场,从而节省开销。
在客户端发起连接请求的时候会携带用户名、密码,服务器程序会对客户端提供的信息进行验证,如果验证不通过服务器程序拒绝连接。
用户名、密码都正确,通过验证之后就成功建立连接,当连接建立之后与该客户端关联的线程会一直等待客户端发送请求过来。
解析与优化
客户端发送过来的只是一段文本消息,该文本消息还需进行各种处理,比较重要的是:查询缓存、语法解析、查询优化
查询缓存
MySQL服务器程序处理查询请求时,会把刚刚查询的sql语句和结果缓存起来,如果下一次有一抹一样的请求过来,那么直接从缓存中查找到结果就行了,就不在需要去表中查找了,提高了查询的速度。
如果两个请求之间的sql语句是完全相同的,但是它们之间的某些字符大小不同,或者多了少了一个空格,都会导致无法命中缓存;在查询中包含了系统函数、用户自定义变量和函数、一些系统表,也会导致无法命中缓存。
缓存是针对某一张表的,只要对表使用了INSERT、UPDATE、DELETE、TRUNCATE TABLE、ALTER TABLE、DROP TABLE或DROP DATABASE语句,那么该表的所有缓存都将失效(在MySQL8中已将将缓存删除了)
语法解析
如果查询缓存没有命中,接下来就进入待正式查询阶段。客户端发来的只是一段文本信息,所以MySQL服务器程序首先是对文本做出分析,判断是否符合语法要求,然后从文本中将要查询的表、各种查询条件都提取出来放到服务器程序内部使用的一些数据结构上来。
查询优化
语法解析之后,服务器程序获得到了需要的信息,比如要查询的列是哪些,表是哪个,搜索条件是什么等等,但光有这些是不够的,因为我们写的MySQL语句执行起来效率可能并不是很高,MySQL的优化程序会对我们的语句做一些优化,如外连接转换为内连接、表达式简化、子查询转为连接吧啦吧啦的一堆东西。优化的结果就是生成一个执行计划,这个执行计划表明了应该使用哪些索引进行查询,表之间的连接顺序是啥样的。我们可以使用EXPLAIN语句来查看某个语句的执行计划,关于查询优化这部分的详细内容我们后边会仔细唠叨,现在你只需要知道在MySQL服务器程序处理请求的过程中有这么一个步骤就好了。
存储引擎
截止到服务器程序完成了查询优化为止,还没有真正的去访问真实的数据表,MySQL服务器把数据的存储和提取操作都封装到了一个叫存储引擎的模块里。我们知道表是由一行一行的记录组成的,但这只是一个逻辑上的概念,物理上如何表示记录,怎么从表中读取数据,怎么把数据写入具体的物理存储器上,这都是存储引擎负责的事情。为了实现不同的功能,MySQL提供了各式各样的存储引擎,不同存储引擎管理的表具体的存储结构可能不同,采用的存取算法也可能不同。
为了管理方便,人们把连接管理、查询缓存、语法解析、查询优化这些并不涉及真实数据存储的功能划分为MySQL server的功能,把真实存取数据的功能划分为存储引擎的功能。各种不同的存储引擎向上边的MySQL server层提供统一的调用接口(也就是存储引擎API),包含了几十个底层函数,像"读取索引第一条内容"、"读取索引下一条内容"、"插入记录"等等。
所以在MySQL server完成了查询优化后,只需按照生成的执行计划调用底层存储引擎提供的API,获取到数据后返回给客户端就好了。
常用存储引擎
MySQL支持非常多种存储引擎,我这先列举一些:
存储引擎 | 描述 |
---|---|
ARCHIVE | 用于数据存档(行被插入后不能再修改) |
BLACKHOLE | 丢弃写操作,读操作会返回空内容 |
CSV | 在存储数据时,以逗号分隔各个数据项 |
FEDERATED | 用来访问远程表 |
InnoDB | 具备外键支持功能的事务存储引擎 |
MEMORY | 置于内存的表 |
MERGE | 用来管理多个MyISAM表构成的表集合 |
MyISAM | 主要的非事务处理存储引擎 |
NDB | MySQL集群专用存储引擎 |
在这诸多的存储引擎中,其实我们最常用的就是InnoDB和MyISAM,有时会提一下Memory。其中InnoDB是MySQL默认的存储引擎,我们之后会详细唠叨这个存储引擎的各种功能,现在先看一下一些存储引擎对于某些功能的支持情况:
Feature | MyISAM | Memory | InnoDB | Archive | NDB |
---|---|---|---|---|---|
B-tree indexes(B树索引) | yes | yes | yes | no | no |
Backup/point-in-time recovery(备份/恢复时间点) | yes | yes | yes | yes | yes |
Cluster database support(集群数据库支持) | no | no | no | no | yes |
Clustered indexes(聚集索引) | no | no | yes | no | no |
Compressed data(压缩数据) | yes | no | yes | yes | no |
Data caches(数据缓存) | no | N/A | yes | no | yes |
Encrypted data(加密的数据) | yes | yes | yes | yes | yes |
Foreign key support(外键的支持) | no | no | yes | no | yes |
Full-text search indexes(全文搜索索引) | yes | no | yes | no | no |
Geospatial data type support(地理空间数据类型支持) | yes | no | yes | yes | yes |
Geospatial indexing support(地理空间索引支持) | yes | no | yes | no | no |
Hash indexes(散列索引) | no | yes | no | no | yes |
Index caches(索引缓存) | yes | N/A | yes | no | yes |
Locking granularity(锁的粒度) | Table | Table | Row | Row | Row |
MVCC(断续器) | no | no | yes | no | no |
Query cache support(查询缓存的支持) | yes | yes | yes | yes | yes |
Replication support(复制支持) | yes | Limited | yes | yes | yes |
Storage limits(存储限制) | 256TB | RAM | 64TB | None | 384EB |
T-tree indexes(T树索引) | no | no | no | no | yes |
Transactions(事务) | no | no | yes | no | yes |
Update statistics for data dictionary(更新数据字典的统计信息) | yes | yes | yes | yes | yes |
存储引擎的一些操作
查看当前服务器程序支持的存储引擎
SHOW ENGINES;
其中的Support列表示该存储引擎是否可用,DEFAULT值代表是当前服务器程序的默认存储引擎。Comment列是对存储引擎的一个描述,英文的,将就着看吧。Transactions列代表该存储引擎是否支持事务处理。XA列代表着该存储引擎是否支持分布式事务。Savepoints代表着该存储引擎是否支持部分事务回滚。
创建表时指定存储引擎
我们之前创建表的语句都没有指定表的存储引擎,那就会使用默认的存储引擎InnoDB(当然这个默认的存储引擎也是可以修改的,我们在后边的章节中再说怎么改)。如果我们想显式的指定一下表的存储引擎,那可以这么写:
CREATE TABLE 表名(
建表语句;
) ENGINE = 存储引擎名称;
创建一个存储引擎为MyISAM的表
CREATE TABLE engine_demo_table(
i int
) ENGINE = MyISAM;
修改表的存储引擎
如果表已经建好了,我们也可以使用下边这个语句来修改表的存储引擎:
ALTER TABLE 表名 ENGINE = 存储引擎名称;
比如我们修改一下上面创建的engine_demo_table表的存储引擎:
ALTER TABLE engine_demo_table ENGINE = InnoDB;
查看一下engine_demo_table的表结构
mysql> SHOW CREATE TABLE engine_demo_table\G
*************************** 1. row ***************************
Table: engine_demo_table
Create Table: CREATE TABLE `engine_demo_table` (
`i` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.01 sec)