connectionstring属性尚未初始化

connectionstring属性尚未初始化

作者|丁奇

来源|极客时间《MySQL实战45讲》专栏

我们常说,看待一个事物,不要直接纠结于细节。你应该先对它有个鸟瞰,可以帮助你从高维度理解问题。同样,MySQL学习也是如此。通常当我们使用数据库时,我们通常会看到一个整体。例如,最简单的表只有一个ID字段。当执行以下查询语句时:

mysql & gtselect * from T其中ID = 10我们看到的只是输入一个语句,返回一个结果,但是我们不知道这个语句在MySQL内部的执行过程。

所以今天我想和大家一起拆解一下MySQL,看看里面都有哪些“零件”。希望你能通过这个反汇编过程对MySQL有更深入的了解。这样,当我们在MySQL中遇到一些异常或问题时,可以直接戳中本质,更快地定位和解决问题。

下面我给出一个MySQL的基本架构示意图,从中可以清晰的看到MySQL各个功能模块中SQL语句的执行过程。

MySQL的逻辑架构图

一般来说,MySQL可以分为两个部分:服务器层和存储引擎层。

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

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

换句话说,当你创建表时,如果你不指定引擎类型,默认情况下将使用InnoDB。但是,您也可以通过指定存储引擎的类型来选择另一个引擎,例如在create table语句中使用engine=memory来指定使用内存引擎创建表。不同的存储引擎有不同的访问表数据的方式,并支持不同的功能。在后面的文章中,我们将讨论引擎的选择。

从图中不难看出,不同的存储引擎共享一个服务器层,也就是从连接器到执行器的部分。你可以先对每个组件的名称有个印象,然后我用开头提到的SQL语句带你看一下整个执行过程,依次看看每个组件的功能。

连接器

在第一步中,您将首先连接到这个数据库,连接器将在此时接收您。连接器负责与客户端建立连接、获取权限、维护和管理连接。连接命令一般是这样写的:

mysql -h$ip -P$port -u$user -p输入命令后,需要在交互对话中输入密码。虽然密码也可以直接写在命令行的-p后面,但是可能会导致你的密码被泄露。如果您连接到生产服务器,强烈建议您不要这样做。

connection命令中的Mysql是一个客户端工具,用来建立与服务器的连接。完成经典的TCP握手后,连接器将开始验证您的身份。此时,将使用您输入的用户名和密码。

如果用户名或密码不对,你就会收到一个\”Access denied for user\”的错误,然后客户端程序结束执行。如果用户名密码认证通过,连接器会到权限表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。

这意味着用户成功建立连接后,即使您使用管理员帐户修改了该用户的权限,也不会影响已有连接的权限。修改后,只有新创建的连接将使用新的权限设置。

连接完成后,如果您没有后续操作,连接将处于空空闲状态,您可以在show processlist命令中看到它。文中这个图是show processlist的结果,其中命令栏显示为“Sleep”,表示现在系统中有空空闲连接。

如果客户端长时间不移动,连接器将自动断开连接。这个时间由参数wait_timeout控制,默认值是8小时。

如果客户端在连接断开后再次发送请求,将会收到错误提示:查询过程中失去与MySQL服务器的连接。如果此时要继续,您需要重新连接,然后执行请求。

在数据库中,长连接意味着连接成功后,如果客户端继续发出请求,它将始终使用同一个连接。短连接是指每次执行几个查询后断开连接,下次重新建立一个。

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

但是在所有长连接都用上之后,你可能会发现有时候MySQL占用的内存上升的特别快,因为MySQL在执行过程中临时使用的内存是在connection对象中管理的。当连接断开时,这些资源将被释放。所以如果长连接积累,可能会导致内存占用过多,被系统强行杀死(OOM)。从现象上看,MySQL重启异常。

如何解决这个问题?可以考虑以下两种选择。

定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。如果你用的是 MySQL 5.7 或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection 来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。查询缓存

建立连接后,您可以执行select语句。执行逻辑将进入第二步:查询缓存。

MySQL得到查询请求后,会先去查询缓存,看看这条语句之前是否被执行过。先前执行的语句及其结果可以以键值对的形式直接缓存在内存中。键是查询的语句,值是查询的结果。如果您的查询可以直接在这个缓存中找到这个键,那么这个值将直接返回给客户端。

如果该语句不在查询缓存中,它将继续到后面的执行阶段。执行完成后,执行结果将存储在查询缓存中。可以看到,如果查询命中缓存,MySQL可以直接返回结果,不需要执行复杂的操作,效率会非常高。

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

查询缓存失效非常频繁。只要有一个表的更新,这个表上的所有查询缓存都将被清除空。所以很有可能是你花了很大力气保存的结果,还没用就被一个update 空清空了。对于更新压力大的数据库,查询缓存的命中率会很低。除非你的业务只是有一个静态表,很久才会更新一次。例如一个系统配置表,那么这个表上的查询适合使用查询缓存。

幸运的是,MySQL也提供了这种“按需”方式。您可以将参数query_cache_type设置为DEMAND,这样查询缓存就不会用于默认的SQL语句。对于确定要使用查询缓存的语句,可以用SQL_CACHE显式指定,如下语句所示:

mysql & gtselect SQL_CACHE * from T其中ID = 10需要注意的是,MySQL版直接删除了整个查询缓存的功能,也就是说8.0开始完全没有这个功能了。

分析器

如果您没有命中查询缓存,您将开始实际执行该语句。首先,MySQL需要知道你想做什么,所以需要解析SQL语句。

分析器将首先进行词法分析。您输入了一个由多个字符串和空单元格组成的SQL语句,MySQL需要识别这些字符串是什么以及它们代表什么。

MySQL是从你输入的关键字“select”中识别出来的,这个关键字是查询语句。它还将字符串“T”识别为“表名T”,将字符串“ID”识别为“列ID”。

这些认识之后,就要做“语法分析”了。根据词法分析的结果,解析器会根据语法规则判断你输入的SQL语句是否符合MySQL语法。

如果您的语句不正确,您将收到一个错误提示“您的SQL语法中有一个错误”。例如,以下语句select遗漏了首字母“S”。

mysql & gtelect * from t其中ID = 1;错误1064 (42000):您的SQL语法有错误;查看MySQL服务器版本的对应手册,了解在第1行使用near\’ elect * from where id = 1 \’的正确语法。一般语法错误会提示错误发生的第一个位置,因此您应该注意紧跟在“使用near”后面的内容。

优化器

通过分析器后,MySQL就会知道你想做什么。在开始执行之前,它必须由优化器处理。

当表中有多个索引时,优化器决定使用哪个索引;或者当一个语句有多个表连接时,确定每个表的连接顺序。例如,您执行以下语句,该语句执行两个表的联接:

mysql & gtselect * from t1 join t2 using(ID)其中t1.c=10,t2.d = 20可以从表t1中取出c=10的记录的ID值,然后根据ID值与表t2关联,再判断表t2中d的值是否等于20。或者,可以先从表t2中取出d=20的记录的ID值,然后根据ID值与t1相关联,再判断t1中c的值是否等于10。这两种执行方式的逻辑结果是一样的,但是执行的效率会有所不同,优化器的作用就是决定使用哪种方案。

优化器阶段完成后,确定了这条语句的执行方案,然后进入执行程序阶段。如果您还有一些问题,比如优化器如何选择索引,是否有可能选择错误的索引等。,没关系,我会在后面的文章中单独讲解优化器的内容。

执行器

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

在开始执行的时候,首先要判断自己是否有权限查询这个表t,如果没有,就会返回一个没有权限的错误,如下图。

mysql & gtselect * from T其中ID = 10错误1142 (42000): select命令拒绝用户\’ b\’ @\’ localhost \’对表\’ t \’执行。如果您有权限,请打开该表继续执行。当表格打开时,执行器将根据表格的引擎定义使用引擎提供的接口。

例如,在我们示例中的表T中,ID字段没有索引,因此执行器的执行流程如下:

调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过,如果是则将这行存在结果集中;调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。

至此,该语句的执行完成。

对于索引表,逻辑是类似的。第一次调用“取符合条件的第一行”的接口,然后回收“取符合条件的下一行”的接口。这些接口已经在引擎中定义。

您将在数据库的慢速查询日志中看到一个rows_examined字段,指示在执行该语句期间扫描了多少行。每次执行程序调用引擎来获取数据行时,都会累积该值。

在某些场景中,执行器被调用一次,引擎内部会扫描很多行,所以引擎扫描的行数与rows_examined并不完全相同。我们后面会有一篇关于存储引擎内部机制的专门文章,会详细讲解。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。

发表回复

登录后才能评论