一条 SQL 语句在 MySQL 中的执行过程是怎样的?
在 MySQL 中,一条 SQL 语句的执行过程通常可以分为以下几个步骤:
词法分析和语法分析:MySQL 的 SQL 解析器会对输入的 SQL 语句进行词法分析和语法分析,以确定语句的结构和语法是否正确。
查询优化:MySQL 会对 SQL 语句进行优化,以确定最优的执行计划。在这个过程中,MySQL 会考虑许多因素,例如索引、表连接、统计信息等,以找到执行查询的最有效方式。
查询执行:在查询优化后,MySQL 开始执行查询,读取和处理数据。在执行过程中,MySQL 会根据查询中所涉及的表和列等信息,从磁盘中读取相应的数据,并进行计算和过滤操作。
结果返回:最后,MySQL 会将查询结果返回给客户端,完成整个查询过程。
需要注意的是,实际的执行过程可能会因为多种因素而不同,例如数据量、硬件配置等。
另外,在并发环境下,多个查询可能会同时进行,需要使用锁和事务等机制来保证数据的一致性和正确性。
过程
建立连接:与客户端进行 TCP 三次握手建立连接;
校验密码:校验客户端的用户名和密码,如果用户名或密码不对,则会报错;
权限判断:如果用户名和密码都对了,会读取该用户的权限,然后后面的权限逻辑判断都基于此时读取到的权限;
其他相关问题
如何查看 MySQL 服务被多少个客户端连接了?
mysql> show processlist;
空闲连接会一直占用着吗?
空闲连接的最大空闲时长,由 wait_timeout 参数控制的
查询命令
mysql> show variables like 'wait_timeout';
手动断开
mysql> kill connection +6;
MySQL 的连接数有限制吗?
MySQL 服务支持的最大连接数由 max_connections 参数控制。
MySQL 的连接也跟 HTTP 一样,有短连接和长连接的概念。
怎么解决长连接占用内存的问题?
定期断开长连接
客户端主动重置连接
过程
解析出 SQL 语句的第一个字段,看看是什么类型的语句。
如果 SQL 是查询语句(select 语句),MySQL 就会先去查询缓存( Query Cache )里查找缓存数据。
如果查询的语句命中查询缓存,那么就会直接返回 value 给客户端。
如果查询的语句没有命中查询缓存中,那么就要往下继续执行,等执行完后,查询的结果就会被存入查询缓存中。
缺点
更新比较频繁的表,查询缓存的命中率很低
版本变动
MySQL 8.0 版本直接将查询缓存删掉了,也就是说 MySQL 8.0 开始,执行一条 SQL 查询语句,不会再走到查询缓存这个阶段了。对于 MySQL 8.0 之前的版本,如果想关闭查询缓存,我们可以通过将参数 query_cache_type 设置成 DEMAND。
这里说的查询缓存是 server 层的,也就是 MySQL 8.0 版本移除的是 server 层的查询缓存,并不是 Innodb 存储引擎中的 buffer pool。
过程
词法分析
语法分析
语法不对,解析器就会给报错
注意:表不存在或者字段不存在,并不是在解析器里做的,解析器只负责构建语法树和检查语法,但是不会去查表或者字段存不存在。
过程
prepare 阶段,也就是预处理阶段;
optimize 阶段,也就是优化阶段;
execute 阶段,也就是执行阶段;
1 预处理器
检查 SQL 查询语句中的表或者字段是否存在;
将 select * 中的 * 符号,扩展为表上的所有列;
2 优化器
优化器主要负责将 SQL 查询语句的执行方案确定下来
比如在表里面有多个索引的时候,优化器会基于查询成本的考虑,来决定选择使用哪个索引。
要想知道优化器选择了哪个索引,我们可以在查询语句最前面加个 explain 命令
3 执行器
执行器就会和存储引擎交互了,交互是以记录为单位的。
三种方式执行过程
主键索引查询
全表扫描
索引下推(MySQL 5.6 推出的查询优化策略)
特点
执行器查询的过程是一个 while 循环
Server 层每从存储引擎读到一条记录就会发送给客户端,之所以客户端显示的时候是直接显示所有记录的,是因为客户端是等查询语句查询完成后,才会显示出所有的记录
总结
执行一条 SQL 查询语句,期间发生了什么?
连接器:建立连接,管理连接、校验用户身份;
查询缓存:查询语句如果命中查询缓存则直接返回,否则继续往下执行。MySQL 8.0 已删除该模块;
解析 SQL,通过解析器对 SQL 查询语句进行词法分析、语法分析,然后构建语法树,方便后续模块读取表名、字段、语句类型;
执行 SQL:执行 SQL 共有三个阶段:
预处理阶段:检查表或字段是否存在;将 select * 中的 * 符号扩展为表上的所有列。
优化阶段:基于查询成本的考虑,选择查询成本最小的执行计划;
执行阶段:根据执行计划执行 SQL 查询语句,从存储引擎读取记录,返回给客户端;
连接器:客户端首先进行身份验证,如果没通过直接返回。
查询缓存:如果身份验证不通过则查询缓存。缓存这个原理很容易理解,就是把常用的数据放到更高效的地方便于查询。查询缓存也有缺点,就是每当数据更改的时候就要重新设置缓存,在 MySQL8.0 已经将查询缓存去掉。
分析器:分析器先会做“词法分析”。把你输入的内容进行识别,知道字符分别代表什么,有什么含义 然后进行语法分析,如果你的 SQL 语句不符合 MySQL 语法就会收到错误提醒。
优化器:优化器作用就是决定使用哪个索引,决定 join 表的连接顺序。优化器会选择它自己认为最高效的方案,(也代表它不一定能选择出最优的方案)。
执行器:执行器还是先会判断有没有执行的权限,如果有权限的话才会执行下一步。遍历满足条件的行,并把组成的记录集作为结果集返回给客户端。
什么是 IOC,简单讲一下 Spring IOC 的实现机制?
IOC (Inversion of Control),中文翻译为控制反转,是一种编程思想,它将程序中对象的创建、组装、管理等控制权从代码中转移到框架中,实现了松耦合和可重用性的设计。
Spring IOC 是 Spring 框架的一个核心特性,它的实现机制主要包括以下几个步骤:
定义 Bean:在 Spring IOC 中,所有的对象都被看作是 Bean,需要在配置文件或者使用注解的方式中进行定义和配置。
创建 Bean 工厂:在 Spring 中,Bean 工厂负责管理 Bean 的创建、组装和销毁等任务。Spring IOC 容器就是 Bean 工厂的一种实现。
读取配置文件:Spring IOC 容器会读取配置文件或者使用注解的方式来获取 Bean 的定义和配置信息。
创建 Bean 实例:Spring IOC 容器根据配置文件中的信息,使用反射技术来创建 Bean 实例,并将其保存在容器中。
组装 Bean:Spring IOC 容器根据配置文件中的信息,将不同的 Bean 实例组装起来,形成一个完整的应用程序。
注入依赖:Spring IOC 容器根据配置文件中的信息,自动为 Bean 注入依赖的对象或者值。
提供 Bean 实例:应用程序通过 Spring IOC 容器获取需要的 Bean 实例,从而使用其中的方法和属性等。
需要注意的是,Spring IOC 还提供了多种作用域,例如单例、原型、会话、请求等作用域,可以根据具体的需求来选择。
同时,Spring IOC 容器也支持 AOP、事务管理等功能,可以为应用程序提供更完整的服务。
什么是IOC容器,以及IOC的创建过程
基本概念
IOC(Inverse Of Controll,控制反转):就是原来代码里面需要自己手动创建的对象,依赖,反转给 Spring 来帮忙实现。我们需要创建一个容器,同时需要一种描述来让容器知道要创建的对象与对象之间的关系。
在 Spring 中 BeanFactory 就是 IOC 容器,在 Spring 初始化的时候,创建容器,并将需要创建对象和对象的关系(xml,注解)通过 BeanDefinitionReader 加载到 BeanDefinition 中并保存在 BeanDefinitionMap 中,然后再由 IOC 容器创建 bean 对象.
两种 bean 的注册方式
方法1:通过 @Bean + @Configuration 的方式直接定义要创建的对象与对象的关系
方式2:通过 @Component 定义类,这种方式必须使用 @ComponetScan 定位 Bean 扫描路径
IOC的创建
在 Spring 中 BeanFactory 就是 IOC 容器,在 Spring 初始化的时候,创建容器,并将需要创建对象和对象的关系(xml,注解)通过 BeanDefinitionReader 加载到 BeanDefinition 中并保存在 BeanDefinitionMap 中,在这个过程中会让 BeanDefinitionProcesser(Bean 的定义信息的后置处理器)进行增强,然后再由 IOC 容器创建 bean 对象.
Bean的生命周期(面试官顺着问题往下问的拓展)
bean 的实例化:spring 启动后,会查找和加载需要被 spring 管理的 Bean,并且实例化
bean 的属性注入(bean 的初始化):bean 被实例化后将 Bean 的引用和值注入到 bean 的属性中
查看是否调用一些 aware 接口,比如 BeanFactoryAware,BeanFactoryAware,ApplicationContextAware 接口,分别会将 Bean 的名字,BeanFactory 容器实例,以及 Bean 所在的上下文引用传入给 Bean
在初始化之前,会查看是否调用了 BeanPostProcessor 的预初始化方法,可以对 bean 进行扩展
调用 InitializingBean 的 afterPropertiesSet()方法:如果 Bean 实现了 InitializingBean 接口,spring 将调用他们的afterPropertiesSet()方法,类似的,如果 Bean 使用 init-method 生命了初始化方法的话,这个方法也会被调用。
初始化成功之后,会查看是否调用 BeanPostProcessor 的初始化后的方法:如果 Bean 实现了 BeanPostProcessor 接口,spring 就将调用他们的 postprocessAfterInitialization()方法。可以对 bean 进行扩展
bean的正常使用:可以被应用程序正常使用了,他们将驻留在上下文中,直到应用的上下文被销毁
bean的销毁:调用 DisposableBean 的destory()方法:如果 Bean 实现 DisposableBean 接口,spring 将用他的 destory()方法,相同的,如果 Bean 使用了 destory-method 生命销毁方法,该方法也会被调用。(但由于 bean 也分为单例和多例,单例 bean 会随着 IOC 容器的销毁而销毁,多例的 bean 不会随着 IOC 容器的销毁而销毁,他是通过 JVM 里面的垃圾回收器负责回收)
并发和并行有什么区别?同步和异步有什么区别?
并发和并行是两个计算机领域中经常被提到的概念,它们的含义有所不同。
并发(Concurrency):指的是系统中同时存在多个正在执行的任务,并且这些任务之间可能会相互影响。并发通常用来处理多个任务共享资源的情况。在单核 CPU 上,多个任务会轮流使用 CPU 时间片,表现为看似同时执行的情况,但实际上只有一个任务正在执行。
并行(Parallelism):指的是系统中同时存在多个并且相互独立的任务,并且这些任务可以在多个处理器上同时执行,真正意义上的同时处理多个任务。
同步(Synchronous):指的是程序按照代码的顺序执行,一行一行地执行,直到当前行执行完成后才能继续执行下一行。同步通常会阻塞调用者,直到任务完成才能返回。
异步(Asynchronous):指的是程序在执行某个任务时,不会一直等待任务完成,而是继续执行下一行代码,当任务完成后再进行相应的处理。异步通常不会阻塞调用者,可以提高系统的并发性能。
总的来说,"并发"和"并行"是针对多个任务的执行方式,"同步"和"异步"是针对任务执行的阻塞方式和返回方式。在实际应用中,可以根据不同的需求来选择合适的并发和同步方式,以提高系统的性能和可靠性。
先介绍并发和并行的区别:
并发:指多个任务同时在执行,但是它们并不是在同一时刻执行,而是通过快速切换上下文来模拟同时执行,优秀的并发可以无限逼近并行,但无法完全做到并行。
例如,在一个 Web 服务器上,多个用户访问同一个网站,服务器会并发地处理这些请求,同时响应每个请求,但是在某一时刻只有一个请求被处理;
并行:指多个任务同时在执行,并且它们真正地同时执行,通常需要多个 CPU 或者多台计算机协同工作。
例如,在一个分布式计算系统中,不同的计算节点可以同时执行不同的任务,并在完成任务后将结果汇总,以加速计算的过程。
接下来是同步和异步:
同步:指调用某个函数或方法时,程序必须等待函数或方法执行完毕才能继续往下执行。
最熟知的便是使用同步机制来控制多个线程之间的访问,如在 Java 中的 synchronized 关键字可以确保同一时间只有一个线程可以访问某个对象的临界区,避免了多个线程同时修改同一个对象导致的数据不一致问题;
异步:指调用某个函数或方法时,程序可以继续往下执行,不必等待函数或方法执行完毕。当函数或方法执行完毕后,程序会得到一个通知或回调来处理结果。在 Java 中可以使用 CompletableFuture 类来异步执行某个任务,从而提高程序的性能。
虽然并发和并行、同步和异步都是计算机领域中常用的概念,但是它们的区别还是很明显的。并发和并行关注的是任务的执行方式,同步和异步则关注的是数据的处理方式。
用 CSS 和 JS 来实现动画分别有哪些优缺点?
用 CSS 和 JS 来实现动画各有其优缺点,具体如下:
使用 CSS 实现动画的优缺点:
优点:
硬件加速:CSS 动画会使用浏览器的 GPU 来进行硬件加速,能够更加流畅和高效地运行。
简单易用:CSS 动画通常只需要几行代码就能实现基本的动画效果,不需要使用 JavaScript 来控制动画。
低资源占用:CSS 动画通常比 JavaScript 动画使用更少的 CPU 和内存资源,因此更适合用于简单的动画效果。
缺点:
限制较大:CSS 动画在实现复杂的动画效果时,受到限制较大,不能像 JavaScript 动画那样自由控制动画的速度、方向等。
兼容性问题:由于不同浏览器对 CSS 动画支持程度不同,因此在实现时需要考虑浏览器兼容性问题。
可维护性差:当动画效果较为复杂时,使用 CSS 实现的代码会变得冗长和难以维护,因此需要进行代码优化和结构设计。
使用 JavaScript 实现动画的优缺点:
优点:
自由控制:JavaScript 动画能够更加自由地控制动画的速度、方向等,可以实现更加复杂的动画效果。
兼容性好:由于 JavaScript 是浏览器通用的语言,因此在实现动画效果时,能够更好地兼容不同的浏览器。
可维护性强:使用 JavaScript 实现动画时,代码结构更加灵活,能够更好地维护和扩展。
缺点:
资源占用高:JavaScript 动画通常需要更多的 CPU 和内存资源,因此在实现动画效果时需要考虑系统资源的消耗问题。
性能问题:JavaScript 动画性能受 JavaScript 引擎的影响,而不是浏览器引擎,因此需要对代码进行优化以提高动画性能。
复杂度高:JavaScript 动画的实现复杂度通常比 CSS 动画高,因此需要对动画效果进行设计和规划。
JS 中怎么阻止事件冒泡和事件默认行为?
在 JavaScript 中,可以通过以下方式阻止事件的冒泡和默认行为:
阻止事件冒泡:事件冒泡是指当一个子元素触发了某个事件后,事件会一直冒泡到它的父元素,直到到达文档根节点。为了阻止事件冒泡,可以使用事件对象的 stopPropagation() 方法。例如:
document.getElementById("child").addEventListener("click", function(event) {
// 阻止事件冒泡
event.stopPropagation();
});
上面的代码中,当子元素被点击时,事件不会继续冒泡到父元素。
阻止事件默认行为:
事件的默认行为是指事件发生时,浏览器会默认执行的一些操作,例如提交表单、打开链接等。为了阻止事件的默认行为,可以使用事件对象的 preventDefault() 方法。例如:
document.getElementById("link").addEventListener("click", function(event) {
// 阻止链接的默认跳转行为
event.preventDefault();
});
上面的代码中,当链接被点击时,链接不会跳转到指定的地址。
需要注意的是,阻止事件的冒泡和默认行为可能会影响用户体验,因此需要谨慎使用。在一些场景下,可以使用阻止事件传播的方式来实现事件委托、事件代理等功能。
阻止事件冒泡:事件冒泡是指当一个子元素触发了某个事件后,事件会一直冒泡到它的父元素,直到到达文档根节点。为了阻止事件冒泡,可以使用事件对象的 stopPropagation() 方法。
document.getElementById("child").addEventListener("click", function(event) {
// 阻止事件冒泡
event.stopPropagation();
//IE浏览器(IE11以下)
event.cancelBubble = true;
});
阻止事件默认行为:
事件的默认行为是指事件发生时,浏览器会默认执行的一些操作,例如提交表单、打开链接等。为了阻止事件的默认行为,可以使用事件对象的 preventDefault() 方法。
document.getElementById("link").addEventListener("click", function(event) {
// 阻止链接的默认跳转行为
event.preventDefault();
//IE8及以下
window.event.returnValue = false;
//无兼容问题(但不能用于节点直接onclick绑定函数)
return false;
});
但是请注意:
return false不能适用于直接用onclick绑定的事件,所以当我们使用这种绑定事件方式时,我们还是需要采用e.preventDefault()这个函数。
注:在jQuery中使用return false时,相当于同时使用event.preventDefault和event.stopPropagation,它会阻止冒泡也会阻止默认行为。但是使用原生js写时,return false只会阻止默认行为。
什么是 webpack?它有什么作用?
Webpack 是一个开源的前端打包工具,它主要用于将多个 JavaScript 文件打包成一个或多个文件,以便在浏览器中加载。Webpack 的核心功能是对模块进行打包,并支持多种资源的加载和打包,如 JavaScript、CSS、图片、字体等。
Webpack 的主要作用包括:
模块化:Webpack 支持各种模块化规范,包括 ES6、CommonJS、AMD 等,可以将应用程序拆分为多个模块,方便管理和维护。
资源加载:Webpack 可以处理各种类型的文件,包括 JavaScript、CSS、图片、字体等,可以将这些文件作为模块进行加载和打包。
代码压缩:Webpack 可以对代码进行压缩和混淆,减小文件体积,提高页面加载速度。
代码分割:Webpack 可以将应用程序拆分成多个 chunk,从而实现按需加载,减小首屏加载时间。
模块热替换:Webpack 支持模块热替换,可以在开发过程中快速预览应用程序的变化,提高开发效率。
优化打包速度:Webpack 可以使用多线程打包、缓存等技术,优化打包速度。
需要注意的是,Webpack 本身只是一个打包工具,对于项目的构建和管理,通常需要结合其他工具和插件一起使用,如 Babel、ESLint、PostCSS、Vue Loader 等。同时,Webpack 的配置也比较复杂,需要一定的学习成本。但是一旦熟练掌握,Webpack 可以大大提高项目的可维护性和开发效率。
Webpack 是一个开源的静态模块打包工具,它可以将多个模块组合成一个文件,同时可以将 Less、Scss、TypeScript、ES6 等代码转换为浏览器可以识别的 JavaScript 代码。
Webpack 最重要的特性之一就是模块化。它允许使用模块化的方式来管理前端代码,可以将应用程序分成多个模块,每个模块包含自己的 JavaScript、CSS、图片等资源。Webpack 会自动解析模块之间的依赖关系,并将它们打包成一个或多个文件,这样可以减少 HTTP 请求的次数,提高页面加载速度。Webpack 还有以下几个重要的作用:
支持代码拆分和懒加载,可以将代码拆分成多个块,按需加载,提高页面加载速度和性能。
支持插件和 Loader,可以通过插件和 Loader 实现各种各样的自动化任务,如压缩代码、生成 HTML 文件、图片压缩、样式预处理等。
支持开发和生产环境的不同配置,可以通过不同的配置文件来满足不同环境下的需求,如开发环境下需要支持热更新,生产环境下需要压缩代码等。
提供了开发服务器,可以在开发过程中快速构建和预览应用程序,提高开发效率。
Webpack 的工作原理可以概括为以下几个步骤:
读取入口文件:Webpack 会读取入口文件,根据入口文件和配置信息,构建出一个或多个代码块(Chunk)。
解析模块依赖:Webpack 会递归解析入口文件和其它模块之间的依赖关系,直到所有依赖关系都被解析出来。
加载和转换模块:Webpack 会根据不同的 Loader,将模块中的不同类型的文件转换为 JavaScript 模块,如将 Less、Scss、TypeScript、ES6 等代码转换为浏览器可以识别的 JavaScript 代码。
合并模块:Webpack 会将所有模块合并到一个或多个 Chunk 中,每个 Chunk 中包含了所有相关的模块和代码。
输出打包结果:Webpack 会根据配置信息,将打包后的代码生成一个或多个文件,如输出到 dist 目录中,生成多个 JavaScript 和 CSS 文件。在实际开发的时候,Webpack 的配置非常灵活,可以根据项目的需求来进行配置,包括入口文件、输出文件、Loader、插件、代码拆分、懒加载、开发服务器等。因此,熟练掌握Webpack可以更好地进行项目开发和维护。