“这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战”mysql
MySQL 是一种典型的 C/S 结构,C/S 结构即 客户端/服务端 模型。git
服务端程序:mysqld MySQL 自带的客户端:mysql、mysqladmin、mysqldump 等程序员
经过网络链接串:TCP/IP
# mysql -uroot -p -h 127.0.0.1 -P3306
经过套接字文件,socket
# mysql -uroot -p -S /tmp/mysql.sock
复制代码
首先来讲说,数据库是作什么的。github
数据库是用来长久存储数据的,而咱们你们都知道内存只能临时存储,磁盘等才能真正存储数据。那么数据库会放在哪里呢?确定是存放在磁盘上,因此数据库其实就是磁盘上的一个文件。算法
简单的理解就是:数据库 = 磁盘上的文件。sql
既然数据库能够当作是磁盘上文件,那么咱们怎么使用数据库呢?shell
若是说咱们能够直接使用数据库,那就等价于直接使用磁盘上的文件。咱们还知道,咱们只有把磁盘上的文件读入内存中才可使用。这即是正确的数据库的使用流程。数据库
那么数据库如何把数据读入内存中呢?缓存
这个时候就须要咱们将要介绍的实例(instance)了,实例能够理解为内存结构和一组后台进程。bash
实例是用来将磁盘中的数据读入内存中,并使用数据。
MySQL 在启动过程当中,会:
因此实例是:MySQL 的后台进程 + 线程 + 预分配的内存结构
而 MySQL 是单进程多线程,也就是说 MySQL 实际在系统中表现的就是一个服务进程,即进程(经过多种方法能够建立多实例,再安装一个端口号不一样的 MySQL,或者经过 workbench 来新建一个端口号不一样的服务实例等)
咱们平时在 Linux 常常会使用一些专业的命令来管理咱们操做系统中的对象,好比 touch
,mkidr
。这些命令是 Linux 操做系统 bash shell 支持的一些功能。
对于 MySQL 也同样,MySQL 可能不会用 ls
这些命令,MySQL 也有一些专用的内置命令,用来管理数据库中的数据,咱们把这种命令称为 SQL(Structured Query Language,结构化查询语言)。
为了更方便的学习记忆,咱们将 SQL 语句分为以下经常使用的几类:
select
、insert
、update
、delete
、merge
、explain plan
、call
、lock table
等语句。create
、alter
、drop
、truncate
、comment
、replace(rename)
等语句,通常不须要 commit 等事务操做。grant
、revoke
等语句。savepoint
、rollback
,commit
,set transaction
等语句。好比咱们熟悉的这条语句,在 Linux 中没法执行。
# select user,host from mysql.user;
-bash: 未预期的符号 `from' 附近有语法错误
复制代码
咱们得进入到 mysql 中才能运行。
mysql> select user,host from mysql.user;
+---------------+-----------+
| user | host |
+---------------+-----------+
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
+---------------+-----------+
3 rows in set (0.00 sec)
复制代码
整个过程看起来很是简单,输入语句,点击回车,而后就显示结果了。
可是咱们有没有想过,为何咱们在平时的业务开发中,好比要实现一个功能,有的人写的语句执行就快,有的人写的语句执行就慢,结果都是同样的,那么为何一个快一个慢呢?
咱们可能会想到有可能执行的慢的语句很是长、写的很复杂,是的,有多是这个缘由。
那么咱们再深刻想想,有没有多是语句在内部执行过程当中,发生了什么不可预料的操做,或者是一些操做是代价较高的操做。咱们可能不能从表面上看出问题,另外呢,也不必定 SQL 长就慢,反而有多是越长的 SQL 执行越快,越简易的语句执行的越慢。
所以,咱们不该该仅仅关注结果,而不去关注过程,就没法找到根本的缘由,接下来,咱们来学习一下,从链接数据库开始,到输入 SQL 语句,而后点击回车,而后显示结果,这个过程当中间到底发生了什么。
接下来,咱们就经过这一条查询语句,来学习一下 MySQL 详细的工做流程。
mysqld 结构,咱们分红了三层:链接层、SQL 层、存储引擎层。
首先一条语句多是一个用户发起的,好比 Navicat,Navicat 执行以下一条语句:
select user,host from mysql.user;
复制代码
那么首先 Navicat 得链接上 MySQL,那么它怎么链接呢?咱们以前有说过,经过 tcp/ip 或者 socket 方式进行链接。这说明链接层得支持这两种协议,并支持使用这两种协议进行链接。
而后,好比咱们使用 tcp/ip 方式链接,咱们还要输入用户名、密码、IP、端口号。假设我输入的端口号是 3307,那么可以登录吗,确定是不能够的,由于咱们配置的端口号是 3306,那说明链接层的做用还有对用户名、密码、IP、端口号等进行校验,验证合法性。
连上了以后,咱们也说了,在 MySQL 里面全部要实现的功能都须要工做的线程来提供,好比说,接收请求语句,返回结果。因此,在链接层会自动开启一个链接线程,接收语句、查看结果。咱们能够经过 show processlist
来查看链接线程状况。
mysql> show processlist;
+-----+------+-----------+------+---------+------+----------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+------+-----------+------+---------+------+----------+------------------+
| 282 | root | localhost | NULL | Query | 0 | starting | show processlist |
+-----+------+-----------+------+---------+------+----------+------------------+
1 row in set (0.01 sec)
复制代码
而后我在另外一个终端上再启动一个 MySQL,再执行一下,发现链接线程变成了两个。
mysql> show processlist;
+-----+------+-----------+------+---------+------+----------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+------+-----------+------+---------+------+----------+------------------+
| 282 | root | localhost | NULL | Sleep | 20 | | NULL |
| 286 | root | localhost | NULL | Query | 0 | starting | show processlist |
+-----+------+-----------+------+---------+------+----------+------------------+
2 rows in set (0.00 sec)
复制代码
默认链接线程最多有 151 个,固然这个数字能够调整。没有用户请求进来都会开辟一个会话,若是这个会话 8 个小时没有动做的话,就会断开这个链接。
到这里,链接层的工做就作完了,接着把请求传递给下一层。
总结一下,链接层做用:
show processlist
命令能够查看用户链接的线程状况)接收上层传送的 SQL 语句。
SQL 层确定首先将 SQL 接收到,而后才能执行后面的操做。
语法检查:验证语句语法,判断是否知足 SQL_MODE。
语法检查确定是必要的,若是语法检查都没用过,那确定也无法执行了。
SQL_MODE 经常使用来解决下面几类问题:
语义检查。判断 SQL 语句的类型。
在 Linux 上,咱们以某个用户登陆,好比 work 用户,有些文件是不能删除的,必须切换到 root 用户才能够操做。对于 MySQL 也是如此。可是在进行权限以前,咱们能够先对 SQL 语句的类型作一下判断,而后再来判断相应的权限。
权限检查,检查用户对库、表是否有相应权限。
解析器:进行 SQL 语句的预处理,生成解析器(explain desc),生成多种执行方案。
MySQL 执行语句是否能够直接执行?执行完以后是否会产生很差的效果?咱们是否须要评估一下语句的代价有多高?找到一个最快的、最合适的执行方式。
优化器:根据解析器得出的多种执行方案,进行判断,选出最优的执行计划。
代价模型:之前 MySQL 是按照时间去衡量 SQL 语句的优劣,如今按(CPU/IO/MEM)的损耗评估性能的好坏(基于代价)。
各个版本的优化器算法是不一样的。
执行器,选择最优的执行计划去执行 SQL 语句,产生执行的结果。
真正运行 SQL。
执行结果:会提供给存储引擎层一个结果说明这个查询的结果在磁盘的哪一个位置。
提供查询缓存(默认不开)。
若是某些 SQL 一直执行,好比执行 1000 万次,那么咱们就没有必要一直执行了,咱们能够提供一个查询缓存,将请求结果放到缓存中。
提供日志记录(binlog),记录二进制日志,默认不开启。
包括审计日志、通用日志、binlog。
在 SQL 层中,执行器执行完,会得出一个结果,来展现给咱们,可是咱们的数据在哪呢,其实还在磁盘上。
Linux 上对于磁盘使用是文件系统,不能直接对磁盘进行读写。MySQL 也是如此。MySQL 将这个专门负责特殊数据读写的文件系统叫作存储引擎。(相似于 FS)。
存储引擎层的做用:
根据 SQL 的执行结果,去磁盘上找到相应数据。
找到磁盘上的 16 进制数据,再次返回 SQL 层,结构化成二维表的方式,再由链接层的专用线程返回用户,最终展示出来。
咱们能够把 MySQL 理解成一个文件系统,所以不少概念、命令能够对比 Linux 学习。下标中总结了 MySQL 和 Linux 中相似的概念。
MySQL 中概念 | Linux 中概念 |
---|---|
库 | 目录 |
create database account charset utf8mb4; | mkdir /account |
show databases; | ls / |
use account; | cd /account |
表 | 文件 |
列(字段) | |
数据行(记录) | 数据行 |
表属性 | 文件属性 |
列属性 |
MySQL 中的库在文件系统上是使用目录来表示。咱们 MySQL 的数据存储在 /data/mysql/data
下。
首先咱们查看一下咱们的数据库。
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)
复制代码
而后我在 /data/mysql/data
下建立个 account
目录。
# mkdir account
复制代码
咱们再来看一下数据库。
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| account |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
复制代码
能够看到多了一个 account
数据库。
咱们如今来看一下常见的 MyISAM 存储引擎和 InnoDB 存储引擎中数据是如何存储的。以 mysql.user
表为例。
# cd /data/mysql/data/mysql
# ls
......
-rw-r----- 1 mysql mysql 10816 1月 13 22:25 user.frm
-rw-r----- 1 mysql mysql 396 1月 14 17:21 user.MYD
-rw-r----- 1 mysql mysql 4096 1月 14 17:21 user.MYI
复制代码
对于 MyISAM 存储引擎,MySQL 用 user.frm
、user.MYD
、user.MYI
三张表来存储数据。
user.frm
:存储表结构(列、属性)user.MYD
:存储的数据记录user.MYI
:存储索引-rw-r----- 1 mysql mysql 8636 1月 13 22:25 time_zone.frm
-rw-r----- 1 mysql mysql 98304 1月 13 22:25 time_zone.ibd
复制代码
对于 InnoDB 存储引擎,MySQL 用 time_zone.frm
、time_zone.ibd
两张表来存储数据。
time_zone.frm
:存储表结构(列、属性)time_zone.ibd
:存储的数据记录和索引其实对于 InnoDB 存储引擎,MySQL 还有一个文件用来存储元数据,也就是数据字典。该文件名叫 ibdata1
。咱们在 /data/mysql/data
目录下。
通常状况下(非分区表):
用户名@'白名单'
,例如:root@'localhost'
mysql> create user user2@'localhost';
mysql> exit
复制代码
# mysql -uuser2
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 307
......
复制代码
mysql> create user user3@'localhost' identified by '123';
Query OK, 0 rows affected (0.00 sec)
复制代码
mysql> select user,host from mysql.user;
+---------------+-----------+
| user | host |
+---------------+-----------+
| user1 | 10.0.0.% |
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
| user2 | localhost |
| user3 | localhost |
+---------------+-----------+
6 rows in set (0.00 sec)
复制代码
mysql> alter user user3@'localhost' identified by '123456';
Query OK, 0 rows affected (0.00 sec)
复制代码
mysql> drop user user3@'localhost';
Query OK, 0 rows affected (0.01 sec)
mysql> select user,host from mysql.user;
+---------------+-----------+
| user | host |
+---------------+-----------+
| user1 | 10.0.0.% |
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
| user2 | localhost |
+---------------+-----------+
5 rows in set (0.00 sec)
复制代码
ALL:全部权限
select、insert、update、delete ......
with grant option:受权权限(能够给其余用户受权)
grant 权限 on 做用目标 to 用户 identified 密码 with grant option;
做用目标:
*.*
:MySQL 下全部数据库全部表。account.*
:account 数据库下面的全部表account.t1
:account 数据库下面的 t1 表mysql> grant all on *.* to root@'10.0.0.*' identified by '123' with grant option;
复制代码
mysql> grant select,insert,delete,update on account.* to user4@'10.0.0.*' identified by '123';
复制代码
mysql> show grants for user4@'10.0.0.*';
+---------------------------------------------------------------------------+
| Grants for user4@10.0.0.* |
+---------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'user4'@'10.0.0.*' |
| GRANT SELECT, INSERT, UPDATE, DELETE ON `account`.* TO 'user4'@'10.0.0.*' |
+---------------------------------------------------------------------------+
2 rows in set (0.00 sec)
复制代码
mysql> revoke delete on account.* from user4@'10.0.0.*';
Query OK, 0 rows affected (0.00 sec)
mysql> show grants for user4@'10.0.0.*';
+-------------------------------------------------------------------+
| Grants for user4@10.0.0.* |
+-------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'user4'@'10.0.0.*' |
| GRANT SELECT, INSERT, UPDATE ON `account`.* TO 'user4'@'10.0.0.*' |
+-------------------------------------------------------------------+
2 rows in set (0.00 sec)
复制代码