目录
MySQL结构图
连接层
服务层
引擎层
存储层
查看SQL语句的执行周期
SQL的执行顺序
MyISAM 和 InnoDB
插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。
本图的最上层并不是属于Mysql Server ,是属于客户端和Mysql服务器建立连接的编程语言。
最上层是一些客户端和连接服务,包含本地sock通信和大多数基于客户端/服务端工具实现的类似于tcp/ip的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通务器也会为安全接入的每个客户端验证它所具有的操作权限。
主要用来认证安全接入的客户端提供线程,同样在该层可以实现基于SSL的安全连接。
select * from user
Sql语句的执行数据进行解析
Management Serveices & Utilities: 系统管理和控制工具
SQL Interface : SQL接口,接受用户的SQL命令,并且返回用户需要查询的结果。比如select from就是调用SQL Interface
Parser: 解析器, SQL命令传递到解析器的时候会被解析器验证和解析。
Optimizer: 查询优化器。 SQL语句在查询之前会使用查询优化器对查询进行优化。
用一个例子就可以理解: select uid,name from user where gender= 1, 优化器来决定先投影还是先过滤。
Cache和Buffer: 查询缓存。如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据。 这个缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,key缓存,权限缓存等
存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信。不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取。后面介绍MyISAM和InnoDB
数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。
当客户端提交一条SQL语句的时候,会先到缓存中进行相查询,如果没有命中,才会走SQL Interface(DML,DCL等),然后到解析器,然后到优化器, 然后在交给存储引擎,最后存储引擎在和计算机的文件系统进行交互。
如果我们想要看到SQL语句的执行周期,需要开启查看的相关设置:
mysql> show variables like '%profiling%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| have_profiling | YES |
| profiling | OFF |
| profiling_history_size | 15 |
+------------------------+-------+
3 rows in set (0.01 sec)
mysql> set profiling = 1;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like '%profiling%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| have_profiling | YES |
| profiling | ON |
| profiling_history_size | 15 |
+------------------------+-------+
3 rows in set (0.00 sec)
然后执行查询user表,然后执行下面的语句:
mysql> show profiles;
+----------+------------+-----------------------------------+
| Query_ID | Duration | Query |
+----------+------------+-----------------------------------+
| 1 | 0.00038800 | show variables like '%profiling%' |
| 2 | 0.00011100 | select * from user |
| 3 | 0.00026025 | select * from mysql.user |
+----------+------------+-----------------------------------+
3 rows in set (0.00 sec)
对于第三条语句,在执行这条语句的内部,究竟做了什么?我们也可以查看:
mysql> show profile cpu for query 3;
+--------------------------------+----------+----------+------------+
| Status | Duration | CPU_user | CPU_system |
+--------------------------------+----------+----------+------------+
| starting | 0.000010 | 0.000000 | 0.000000 |
| Waiting for query cache lock | 0.000002 | 0.000000 | 0.000000 |
| checking query cache for query | 0.000026 | 0.000000 | 0.000000 |
| checking permissions | 0.000016 | 0.000000 | 0.000000 |
| Opening tables | 0.000019 | 0.000000 | 0.000000 |
| System lock | 0.000006 | 0.000000 | 0.000000 |
| init | 0.000030 | 0.000000 | 0.000000 |
| optimizing | 0.000002 | 0.000000 | 0.000000 |
| statistics | 0.000005 | 0.000000 | 0.000000 |
| preparing | 0.000005 | 0.000000 | 0.000000 |
| executing | 0.000001 | 0.000000 | 0.000000 |
| Sending data | 0.000096 | 0.000000 | 0.000000 |
| end | 0.000003 | 0.000000 | 0.000000 |
| query end | 0.000001 | 0.000000 | 0.000000 |
| closing tables | 0.000004 | 0.000000 | 0.000000 |
| freeing items | 0.000033 | 0.000000 | 0.000000 |
| logging slow query | 0.000002 | 0.000000 | 0.000000 |
| cleaning up | 0.000002 | 0.000000 | 0.000000 |
+--------------------------------+----------+----------+------------+
18 rows in set (0.00 sec)
接收到SQL语句之后,首先在缓存中进行查询,在查询缓存的时候,需要加上锁,这个时候发现缓存里面没有,去表里查询,在查询之前,首先检查查询的权限,打开表,然后加上锁,初始化,对SQL语句进行优化,然后分析,准备,执行,发送数据到缓存中去,结束,结束查询,关闭表,,,,
在第二次查询的时候,会直接去缓存中查询,我们接着验证这个结论是否正确:我们会发现结果和上次查询的结果一致,注意,我们这里不能使用系统的表,只能使用我们自己创建的表:
mysql> select * from user;
+------+--------+
| id | name |
+------+--------+
| 1 | ?? |
| 2 | ?? |
| 3 | 刘备 |
+------+--------+
3 rows in set (0.04 sec)
mysql> show profile cpu for query 5;
+--------------------------------+----------+----------+------------+
| Status | Duration | CPU_user | CPU_system |
+--------------------------------+----------+----------+------------+
| starting | 0.000010 | 0.000000 | 0.000000 |
| Waiting for query cache lock | 0.000015 | 0.000000 | 0.000000 |
| checking query cache for query | 0.000038 | 0.000000 | 0.000000 |
| checking permissions | 0.000003 | 0.000000 | 0.000000 |
| Opening tables | 0.000010 | 0.000000 | 0.000000 |
| System lock | 0.000007 | 0.000000 | 0.000000 |
| init | 0.000030 | 0.000000 | 0.000000 |
| optimizing | 0.000016 | 0.000000 | 0.000000 |
| statistics | 0.000005 | 0.000000 | 0.000000 |
| preparing | 0.000006 | 0.000000 | 0.000000 |
| executing | 0.000002 | 0.000000 | 0.000000 |
| Sending data | 0.000097 | 0.000000 | 0.000000 |
| end | 0.000004 | 0.000000 | 0.000000 |
| query end | 0.000001 | 0.000000 | 0.000000 |
| closing tables | 0.000004 | 0.000000 | 0.000000 |
| freeing items | 0.000022 | 0.000000 | 0.000000 |
| logging slow query | 0.000002 | 0.000000 | 0.000000 |
| cleaning up | 0.000002 | 0.000000 | 0.000000 |
+--------------------------------+----------+----------+------------+
18 rows in set (0.00 sec)
#在执行一遍select * from user之后,
mysql> show profile cpu for query 10;
+--------------------------------+----------+----------+------------+
| Status | Duration | CPU_user | CPU_system |
+--------------------------------+----------+----------+------------+
| starting | 0.000030 | 0.000000 | 0.000000 |
| Waiting for query cache lock | 0.000002 | 0.000000 | 0.000000 |
| checking query cache for query | 0.000003 | 0.000000 | 0.000000 |
| checking privileges on cached | 0.000001 | 0.000000 | 0.000000 |
| checking permissions | 0.000004 | 0.000000 | 0.000000 |
| sending cached result to clien | 0.000031 | 0.000000 | 0.000000 |
| logging slow query | 0.000001 | 0.000000 | 0.000000 |
| cleaning up | 0.000001 | 0.000000 | 0.000000 |
+--------------------------------+----------+----------+------------+
8 rows in set (0.00 sec)
开始,加锁,在缓存中查询,检查对缓存的权限,允许之后,得到结果,将从缓存查到的结果发送给客户端。此时,我们再一次执行SQL查询,但是这次试用的select * from user表的时候,在 * 和 from 之间加上一个空格,然后在查看查询的过程是怎么样的:
mysql> show profile cpu for query 11;
+--------------------------------+----------+----------+------------+
| Status | Duration | CPU_user | CPU_system |
+--------------------------------+----------+----------+------------+
| starting | 0.000011 | 0.000000 | 0.000000 |
| Waiting for query cache lock | 0.000002 | 0.000000 | 0.000000 |
| checking query cache for query | 0.000037 | 0.000000 | 0.000000 |
| checking permissions | 0.000017 | 0.000000 | 0.000000 |
| Opening tables | 0.000009 | 0.000000 | 0.000000 |
| System lock | 0.000015 | 0.000000 | 0.000000 |
| Waiting for query cache lock | 0.000029 | 0.000000 | 0.000000 |
| init | 0.000009 | 0.000000 | 0.000000 |
| optimizing | 0.000016 | 0.000000 | 0.000000 |
| statistics | 0.000005 | 0.000000 | 0.000000 |
| preparing | 0.000022 | 0.000000 | 0.000000 |
| executing | 0.000001 | 0.000000 | 0.000000 |
| Sending data | 0.000072 | 0.000000 | 0.000000 |
| end | 0.000005 | 0.000000 | 0.000000 |
| query end | 0.000003 | 0.000000 | 0.000000 |
| closing tables | 0.000004 | 0.000000 | 0.000000 |
| freeing items | 0.000004 | 0.000000 | 0.000000 |
| Waiting for query cache lock | 0.000001 | 0.000000 | 0.000000 |
| freeing items | 0.000018 | 0.000000 | 0.000000 |
| Waiting for query cache lock | 0.000002 | 0.000000 | 0.000000 |
| freeing items | 0.000001 | 0.000000 | 0.000000 |
| storing result in query cache | 0.000002 | 0.000000 | 0.000000 |
| logging slow query | 0.000001 | 0.000000 | 0.000000 |
| cleaning up | 0.000001 | 0.000000 | 0.000000 |
+--------------------------------+----------+----------+------------+
24 rows in set (0.01 sec)
同样的,如果第一查询和第二次查询使用的SQL语句是完全一模一样的,但在第一条SQL和第二条SQL之间有插入的语句的话,第二次依然无法使用缓存。
手写的SQL语句如下:
这个时候我们平时在进行SQL查询的时候,手敲出来的SQL语句,但是机器在处理的时候并不是这个执行顺序,Mysql版本更新换代,优化器也在不断的升级,优化器会分析不同的执行顺序从而进行查询:
中文语义:按照连接条件,对左表和右表进行连接,选出where后的条件,分组,对分组后的条件,select出来,最后去重,排序。
输入以下的命令查看,mysql的存储引擎都有哪些:
mysql> show engines;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine | Support | Comment | Transactions | XA | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| MyISAM | YES | MyISAM storage engine | NO | NO | NO |
| MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO |
| CSV | YES | CSV storage engine | NO | NO | NO |
| BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO |
| MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO |
| FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL |
| ARCHIVE | YES | Archive storage engine | NO | NO | NO |
| InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES |
| PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
9 rows in set (0.00 sec)
由上面可以看出,MySQL已经默认安装了多种存储引擎,但是默认使用的是InnoDB的存储引擎,该引擎相对于其他引擎的特点在该表中已经列出。支持事务,使用表级锁(操作是只是锁住改行,不会影响其他行,但是MyISAM会锁住整张表),缓存索引,也缓存数据(MyISAM只缓存索引,不缓存数据),支持外键(MyISAM不支持外键,也不支持事务)等等。MySQL关注并发和事务,但是MyISAM关注的是读性能。
查看表默认使用的是什么存储引擎,可以使用
mysql> show create table user;
+-------+---------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+---------------------------------------------------------------------------------------------------------------------------+
| user | CREATE TABLE `user` (
`id` int(11) DEFAULT NULL,
`name` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+---------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
这里面需要指出,我们自己手动创建的表都是使用的是InnoDB的存储引擎,但是系统自带的表使用的是MyISAM的存储引擎。
举例:
mysql> show create table mysql.user;
ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges' |