3.MySQL组成部分

MySQL组成部分

image.png 我们一般可以将 MySQL 的结构分为四层,最上层为客户端连接器,主要包括了数据库连接、授权认证、安全管理等,该层引用了线程池,为接入的连接请求提高线程处理效率。

Server层主要实现 SQL 的一些基础功能,包括 SQL 解析、优化、执行以及缓存等。包括连接器、查询缓存、分析器、优化器、执行器等,涵盖MySQL的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。现在最常用的存储引擎是InnoDB,它从MySQL 5.5.5版本开始成为了默认存储引擎。

也就是说,你执行create table建表的时候,如果不指定引擎类型,默认使用的就是InnoDB。不过,你也可以通过指定存储引擎的类型来选择别的引擎,比如在create table语句中使用engine=memory, 来指定使用内存引擎创建表。

最下面一层是数据存储层,主要负责将数据存储在文件系统中,并完成与存储引擎的交互。

从图中不难看出,不同的存储引擎共用一个Server层,也就是从连接器到执行器的部分。你可以先对每个组件的名字有个印象,接下来我会结合开头提到的那条SQL语句,带你走一遍整个执行流程,依次看下每个组件的作用。

image.png

服务器程序处理来自客户端的查询请求大致需要经过三个部分,分别是连接管理、解析与优化、存储引擎。

一.客户端连接器:连接管理

​ 客户端进程可以采用我们上边介绍的TCP/IP、命名管道或共享内存、Unix域套接字这几种方式之一来与服务器进程建立连接,每当有一个客户端进程连接到服务器进程时,服务器进程都会创建一个线程来专门处理与这个客户端的交互,当该客户端退出时会与服务器断开连接,服务器并不会立即把与该客户端交互的线程销毁掉,而是把它缓存起来,在另一个新的客户端再进行连接时,把这个缓存的线程分配给该新客户端。这样就起到了不频繁创建和销毁线程的效果,从而节省开销。

​ 从这一点大家也能看出,MySQL服务器会为每一个连接进来的客户端分配一个线程,但是线程分配的太多了会严重影响系统性能,所以我们也需要限制一下可以同时连接到服务器的客户端数量,至于怎么限制我们后边再说哈~

​ 在客户端程序发起连接的时候,需要携带主机信息、用户名、密码,服务器程序会对客户端程序提供的这些信息进行认证,如果认证失败,服务器程序会拒绝连接。

​ 另外,如果客户端程序和服务器程序不运行在一台计算机上,我们还可以采用使用了SSL(安全套接字)的网络连接进行通信,来保证数据传输的安全性。

第一步,你会先连接到这个数据库上,这时候接待你的就是连接器。连接器负责跟客户端建立连接、获取权限、维持和管理连接。连接命令一般是这么写的:

cmd mysql -h$ip -P$port -u$username -p

​ 输完命令之后,你就需要在交互对话里面输入密码。虽然密码也可以直接跟在-p后面写在命令行中,但这样可能会导致你的密码泄露。如果你连的是生产服务器,强烈建议你不要这么做。

​ 连接命令中的mysql是客户端工具,用来跟服务端建立连接。在完成经典的TCP握手后,连接器就要开始认证你的身份,这个时候用的就是你输入的用户名和密码。

· 如果用户名或密码不对,你就会收到一个"Access denied for user"的错误,然后客户端程序结束执行。

· 如果用户名密码认证通过,连接器会到权限表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。

​ 当连接建立后,与该客户端关联的服务器线程会一直等待客户端发送过来的请求,MySQL服务器接收到的请求只是一个文本消息,该文本消息还要经过各种处理。

建立连接

​ 一个用户成功建立连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。

连接完成后,如果你没有后续的动作,这个连接就处于空闲状态,你可以在show processlist命令中看到它。文本中这个图是show processlist的结果,其中的Command列显示为“Sleep”的这一行,就表示现在系统里面有一个空闲连接。

image.png

连接等待断开时长8小时

客户端如果太长时间没动静,连接器就会自动将它断开。这个时间是由参数wait_timeout控制的,默认值是8小时即28800秒。如果8小时内执行过sql,则会重置等待时长为8小时。

SHOW VARIABLES LIKE 'wait_timeout'

image.png

​ 如果在连接被断开之后,客户端再次发送请求的话,就会收到一个错误提醒: Lost connection to MySQL server during query。这时候如果你要继续,就需要重连,然后再执行请求了。

短连接和长连接

​ 数据库里面,长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。

建立连接的过程通常是比较复杂的,所以我建议你在使用中要尽量减少建立连接的动作,也就是尽量使用长连接。

​ 但是全部使用长连接后,你可能会发现,有些时候MySQL占用内存涨得特别快,这是因为MySQL在执行过程中临时使用的内存是管理在连接对象里面的。这些资源会在连接断开的时候才释放。

​ 所以如果长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是MySQL异常重启了。

怎么解决这个问题呢?你可以考虑以下两种方案。

1.定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。

2.如果你用的是MySQL 5.7或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysqlresetconnection来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。

连接池

第一、连接池的建立。一般在系统初始化时,连接池会根据系统配置建立,并在池中创建几个连接对象,以便使用时能从连接池中获取。连接池中的连接不能随意创建与关闭。这样避免了连接随意建立和关闭造成的系统开销。Java中提供了很多容器类可以方便的构建连接池,如vector、stack。

第二、连接池的管理。 连接池管理策略施连接池机制的核心,连接池内连接的分配和释放都对系统性能有很大的影响。其管理策略是:

- 当用户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没有达到就重新创建一个连接给请求的用户;如果达到就设定最大等待时间进行等待,如果超出最大的等待时间,则抛出异常给用户。

-当用户释放数据库连接时,先判断该连接的引用次数是否超出规定值,如果超出就从连接池中删除该连接,否则保留为其他客户服务。

该策略保证了数据库连接的有效复用,避免频繁的建立,释放连接所带来的系统资源开销。

第三、连接池关闭。当应用程序退出时,关闭连接池中的所有连接,释放连接池相关的资源,该过程正好与创建相反。

​ 首先,每一次web请求都要建立一次数据库连接,建立连接是一个费时的操作,每次都得花费0.05s~1s的时间,而且系统还要分配内存资源。这个时间对于一次或几次数据库操作,或许感觉不出系统有多大的开销。可是对于现在的web应用,尤其是大型电子商务站,同时有几百人甚至几千人在线是很正常的,在这种情况下,频繁的进行数据库连接势必占用很大的系统资源,网站响应速度势必下降,严重的会导致服务器的崩溃。

​ 其次,对每一次数据库连接,使用完后都得断开,否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将不得不重启数据库。

​ 从上面可以看出,数据库连接是一种稀缺资源,我们应对其妥善管理。其实我们查询完数据库后,如果不关闭连接,而是暂时存放起来,当别人使用时把这个连接给他们使用,就避免了一次建立数据库的连接和断开的操作的时间消耗。数据库连接池的基本思想是:为数据库连接建立一个缓冲池,预先在缓冲池中放入一定数量的连接,当需要建立连接的时候,只需从缓存池中取出一个,使用完后在放回去。

二.解析优化之查询缓存

连接建立完成后,你就可以执行select语句了。执行逻辑就会来到第二步:查询缓存。

MySQL拿到一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果可能会以key-value对的形式,被直接缓存在内存中。key是查询的语句,value是查询的结果。如果你的查询能够直接在这个缓存中找到key,那么这个value就会被直接返回给客户端。

如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。你可以看到,如果查询命中缓存,MySQL不需要执行后面的复杂操作,就可以直接返回结果,这个效率会很高。如果命中查询缓存,会在查询缓存放回结果的时候,做权限验证。查询也会在优化器之前调用precheck验证权限。

使用查询缓存场景:静态配置表

但是大多数情况下我会建议你不要使用查询缓存,为什么呢?因为查询缓存往往弊大于利。

查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。因此很可能你费劲地把结果存起来,还没使用呢,就被一个更新全清空了。对于更新压力大的数据库来说,查询缓存的命中率会非常低。

​ 如果你的业务就是有一张静态表,很长时间才会更新一次。

​ 比如,一个系统配置表,那这张表上的查询才适合使用查询缓存。(此处支付中心使用redis,如果使用查询缓存更好)

查询缓存配置

image.png

havequerycache标识,如果是yes 代表MySQL支持Query Cache,查看该参数命令。

sql SHOW VARIABLES LIKE 'have_query_cache';

querycachetype是否打开查询缓存。可以设置为OFF,ON,DEMAND。当设置为DEMAND时表示只有查询语句中明确写明SQL_CACHE的语句时才会写入查询缓存,这样的好处是可以灵活的控制需要缓存的数据。

sql SHOW VARIABLES LIKE 'query_cache_type'

好在MySQL也提供了这种“按需使用”的方式。你可以将参数querycachetype设置成DEMAND,这样对于默认的SQL语句都不使用查询缓存。而对于你确定要使用查询缓存的语句,可以用SQL_CACHE显式指定,像下面这个语句一样:

sql mysql> select SQL_CACHE * from T where ID=10;

缓存命中与失效

​ 当然,MySQL服务器并没有人聪明,如果两个查询请求在任何字符上的不同(例如:空格、注释、大小写),都会导致缓存不会命中。另外,如果查询请求中包含某些系统函数、用户自定义变量和函数、一些系统表,如 mysql 、informationschema、 performanceschema 数据库中的表,那这个请求就不会被缓存。以某些系统函数举例,可能同样的函数的两次调用会产生不一样的结果,比如函数NOW,每次调用都会产生最新的当前时间,如果在一个查询请求中调用了这个函数,那即使查询请求的文本信息都一样,那不同时间的两次查询也应该得到不同的结果,如果在第一次查询时就缓存了,那第二次查询的时候直接使用第一次查询的结果就是错误的!

不过既然是缓存,那就有它缓存失效的时候。MySQL的缓存系统会监测涉及到的每张表,只要该表的结构或者数据被修改,如对该表使用了INSERT、 UPDATE、DELETE、TRUNCATE TABLE、ALTER TABLE、DROP TABLE或 DROP DATABASE语句,那使用该表的所有高速缓存查询都将变为无效并从高速缓存中删除!

! 虽然查询缓存有时可以提升系统性能,但也不得不因维护这块缓存而造成一些开销,比如每次都要去查询缓存中检索,查询请求处理完需要更新查询缓存,维护该查询缓存对应的内存区域。从MySQL 5.7.20开始,不推荐使用查询缓存,并在MySQL 8.0中删除。

三.解析优化之解析器

解析器也叫分析器,如果没有命中查询缓存,就要开始真正执行语句了。首先,MySQL需要知道你要做什么,因此需要对SQL语句做解析。

词法分析

扫描字符流,根据构词规则识别单个单词。MySQL使用Flex来生成词法扫描程序在sql/lex.h中定义了MySQL关键字和函数关键字,用两个数组存储。

你输入的是由多个字符串和空格组成的一条SQL语句,MySQL需要识别出里面的字符串分别是什么,代表什么。MySQL从你输入的"select"这个关键字识别出来,这是一个查询语句。它也要把字符串“T”识别成“表名T”,把字符串“ID”识别成“列ID”。

语法分析

在词法分析的基础上将单词序列组成语法短语,最后生成语法树,提交给优化器

语法分析器使用Bison,在sql/sql_yacc.yy中定义了语法规则。根据关系代数理论生成语法树

语法树

在sql目录下,有许多以sql开头命名的文件,用于接受语法树,执行不同的查询,如sqlselect.cc用于select查询,做完了这些识别以后,就要做“语法分析”。根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个SQL语句是否满足MySQL语法。如果你的语句不对,就会收到“You have an error in your SQL syntax”的错误提醒,比如下面这个语句select少打了开头的字母“s”。

mysql> elect * from t where ID=1;

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 'elect * from t where ID=1' at line 1

一般语法错误会提示第一个出现错误的位置,所以你要关注的是紧接“use near”的内容。

这个从指定的文本中提取出我们需要的信息本质上算是一个编译过程,涉及词法解析、语法分析、语义分析等阶段,这些问题不属于我们讨论的范畴,大家只要了解在处理请求的过程中需要这个步骤就好了。

四.解析优化之优化器

经过了解析器,MySQL就知道你要做什么了。在开始执行之前,还要先经过优化器的处理。

优化器指的是生成的执行计划。

优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。比如你执行下面这样的语句,这个语句是执行两个表的join:

经过了解析器,MySQL就知道你要做什么了。在开始执行之前,还要先经过优化器的处理。

优化器是在表里面有多个索引的时候,决定使用哪个索引。

或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序

比如你执行下面这样的语句,这个语句是执行两个表的join:

mysql> select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;

``` 既可以先从表t1里面取出c=10的记录的ID值,再根据ID值关联到表t2,再判断t2里面d的值是否等于20。

也可以先从表t2里面取出d=20的记录的ID值,再根据ID值关联到t1,再判断t1里面c的值是否等于10。

这两种执行方法的逻辑结果是一样的,但是执行的效率会有不同,而优化器的作用就是决定选择使用哪一个方案。

优化器阶段完成后,这个语句的执行方案就确定下来了,然后进入执行器阶段。 ```

五.执行器:权限验证 + 返回数据

​ MySQL通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。

​ 执行器开始执行的时候,要先判断一下你对这个表T有没有执行查询的权限,如果没有,就会返回没有权限的错误,在工程实现上,如果命中查询缓存,会在查询缓存放回结果的时候,做权限验证。

​ 查询也会在优化器之前调用precheck验证权限,sql执行过程中可能会有触发器这种在运行时才能确定的过程,分析器工作结束后的precheck是不能对这种运行时涉及到的表进行权限校验的,所以需要在执行器阶段进行权限检查。

mysql> select * from T where ID=10;

ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'

如果有权限,就打开表继续执行。

执行器会根据优化器生成的一套执行计划,不停的调用存储引擎的接口去完成SQL语句的执行计划调用存储引擎,执行SQL语句 存储引擎就是执行SQL语句,会按照一定的步骤去查询内存缓存数据,更新磁盘数据,查询磁盘数据等等。

六.★select执行顺序

(1) FROM

(2) ON

(3) JOIN

(4) WHERE

(5) GROUP BY

(6) HAVING

(7) SELECT

(8) DISTINCT

(9) ORDER BY

(10) LIMIT

java SELECT a.customer_id, COUNT( b.order_id ) AS total_orders FROM table1 AS a LEFT JOIN table2 AS b ON a.customer_id = b.customer_id WHERE a.city = 'hangzhou' GROUP BY a.customer_id HAVING count( b.order_id ) < 2 ORDER BY total_orders DESC LIMIT 1;

1、FORM: 对FROM左边的表和右边的表计算笛卡尔积,产生虚表VT1。 2、ON: 对虚表VT1进行ON过滤,只有那些符合 的行才会被记录在虚表VT2中。 3、JOIN: 如果指定了OUTER JOIN(比如left join、 right join),那么保留表中未匹配的行就会作为外部行添加到虚拟表VT2中,产生虚拟表VT3。 4、WHERE: 对虚拟表VT3进行WHERE条件过滤。只有符合 的记录才会被插入到虚拟表VT4中。 5、GROUP BY: 根据group by子句中的列,对VT4中的记录进行分组操作,产生VT5。 6、HAVING: 对虚拟表VT5应用having过滤,只有符合 的记录才会被 插入到虚拟表VT6中。 7、SELECT: 执行select操作,选择指定的列,插入到虚拟表VT7中。 8、DISTINCT: 对VT7中的记录进行去重。产生虚拟表VT8. 9、ORDER BY: 将虚拟表VT8中的记录按照 进行排序操作,产生虚拟表VT9. 10、LIMIT:取出指定行的记录,产生虚拟表VT10, 并将结果返回。

比如我们这个例子中的表T中,ID字段没有索引,那么执行器的执行流程是这样的:

1.调用InnoDB引擎接口取这个表的第一行,判断ID值是不是10,如果不是则跳过,如果是则将这行存在结果集中。

2.调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。

3.执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。

注意:如果id是主键(默认就是唯一索引列)或者是唯一索引列,那么在找到第一行id等于10的记录以后就会直接返回,不再查找。

对于有索引的表,执行的逻辑也差不多。第一次调用的是“取满足条件的第一行”这个接口,之后循环取“满足条件的下一行”这个接口,这些接口都是引擎中已经定义好的。

你会在数据库的慢查询日志中看到一个rowsexamined的字段,表示这个语句执行过程中扫描了多少行。这个值就是在执行器每次调用存储引擎获取数据行的时候累加得来的。在有些场景下,执行器调用一次,在引擎内部则扫描了多行,因此引擎扫描行数跟rowsexamined并不是完全相同的。

七.存储引擎

​ 截止到服务器程序完成了查询优化为止,还没有真正的去访问真实的数据表,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 | 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 | no | no | no | no | yes | | 支持事务处理 | no | no | yes | no | yes | | Update statistics for data dictionary | yes | yes | yes | yes | yes |

密密麻麻列了这么多,看的头皮都发麻了,达到的效果就是告诉你:这玩意儿很复杂。其实这些东西大家没必要立即就给记住,我列出来的目的就是想让大家明白不同的存储引擎支持不同的功能,有些重要的功能我们会在后边的唠叨中慢慢让大家理解的~

关于存储引擎的一些操作

查看服务器程序支持的存储引擎:SHOW ENGINES;

我们可以用下边这个命令来查看当前服务器程序支持的存储引擎:

java SHOW ENGINES;

来看一下调用效果:

```sql mysql> SHOW ENGINES; +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ | Engine | Support | Comment | Transactions | XA | Savepoints | +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ | InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES | | MRGMYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO | | MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO | | BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO | | MyISAM | YES | MyISAM storage engine | NO | NO | NO | | CSV | YES | CSV storage engine | NO | NO | NO | | ARCHIVE | YES | Archive storage engine | NO | NO | NO | | PERFORMANCESCHEMA | YES | Performance Schema | NO | NO | NO | | FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL | +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ 9 rows in set (0.00 sec)

mysql> ```

其中的Support列表示该存储引擎是否可用,DEFAULT值代表是当前服务器程序的默认存储引擎。

Comment列是对存储引擎的一个描述,英文的,将就着看吧。

Transactions列代表该存储引擎是否支持事务处理。

XA列代表着该存储引擎是否支持分布式事务。

Savepoints代表着该列是否支持部分事务回滚。

设置表的存储引擎

​ 我们前边说过,存储引擎是负责对表中的数据进行提取和写入工作的,我们可以为不同的表设置不同的存储引擎,也就是说不同的表可以有不同的物理存储结构,不同的提取和写入方式。

创建表时指定存储引擎

我们之前创建表的语句都没有指定表的存储引擎,那就会使用默认的存储引擎InnoDB(当然这个默认的存储引擎也是可以修改的,我们在后边的章节中再说怎么改)。如果我们想显式的指定一下表的存储引擎,那可以这么写:

sql CREATE TABLE 表名( 建表语句; ) ENGINE = 存储引擎名称;

比如我们想创建一个存储引擎为MyISAM的表可以这么写:

``` mysql> CREATE TABLE enginedemotable( -> id int -> ) ENGINE = MyISAM;

Query OK, 0 rows affected (0.02 sec) mysql> ```

修改表的存储引擎

如果表已经建好了,我们也可以使用下边这个语句来修改表的存储引擎:

java ALTER TABLE 表名 ENGINE = 存储引擎名称;

该命令可以重建索引,会释放空洞,这是由于在转换数据引擎(即使没有真正转换)的时候,会将表中的所有数据读取,再重新写入,这个过程中,会释放空洞(效率慢)。

比如我们修改一下enginedemotable表的存储引擎:

``` mysql> ALTER TABLE enginedemotable ENGINE = InnoDB;

Query OK, 0 rows affected (0.05 sec) Records: 0 Duplicates: 0 Warnings: 0

```

小结

如果表T中没有字段k,而你执行了这个语句 select * from T where k=1, 那肯定是会报“不存在这个列”的错误: “Unknown column ‘k’ in ‘where clause’”。你觉得这个错误是在我们上面提到的哪个阶段报出来的呢?

答案:分析器, 分析器主要做两个事情,先做词法分析后做语法分析,词法分析主要做的是根据mysql的关键字进行验证和解析,而语法分析会在词法解析的基础上进一步做表名和字段名称的验证和解析。

执行过程: 1,连接 连接管理模块,接收请求;连接进程和用户模块,通过,连接线程和客户端对接 2,查询 查询缓存 Query Cache 分析器,内建解析树,对其语法检查,先from,再on,再join,再where......;检查权限,生成新的解析树,语义检查(没有字段k在这里)等 优化器,将前面解析树转换成执行计划,并进行评估最优 执行器,获取锁,打开表,通过meta数据,获取数据 3,返回结果 返回给连接进程和用户模块,然后清理,等待新的请求

创建了一个没有select权限的用户,执行select * from T where k=1,报错“select command denied”,并没有报错“unknown column”,是不是可以说明是在打开表之后才判断读取的列不存在?

答案:一个用户如果没有查看这个表的权限,你是会告诉他字段不对还是没权限?如果告诉他字段不对,其实给的信息太多了,因为没权限的意思还包含了:没权限知道字段是否存在。

参考:https://time.geekbang.org/column/intro/139

参考:https://juejin.cn/book/6844733769996304392?enterfrom=searchresult&utm_source=search

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值