当前位置:首页 » 编程语言 » sql字句执行过程分析思维导图
扩展阅读
webinf下怎么引入js 2023-08-31 21:54:13
堡垒机怎么打开web 2023-08-31 21:54:11

sql字句执行过程分析思维导图

发布时间: 2022-10-30 08:55:28

1. sql存储过程是怎么执行的(是执行过程)谢了

就像你调用方法一样嘛
通过调用
exec 存储过程名 参数名1,参数名2,.....
进入存储过程,根据存储过程编写语句顺序执行

2. 数据库 SQL语句 子查询执行过程

子查询执行过程,可以用以下例子来说明:

语句如下:

select*fromscorewheresidin(selectsidfromstudentwhere班级='一班')

在sql语句中,数据库先执行的是括号中的部分,得出student表中一班学生的sid,然后再在score表中选出sid为一班id的哪些学生的详细内容。

3. sql语句分析

SQL 语句摘要可以用在MySQL的各个方面,比如 性能字典里对语句的分析,查询重写插件规则改写等等。
接下来依次看下语句摘要在这两方面的使用。
1. 性能字典
mysql> call sys.ps_setup_enable_consumer('statements');
+---------------------+
| summary |
+---------------------+
| Enabled 4 consumers |
+---------------------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
开启后,执行几次之前的几条 SQL。
完后可以很方便的从 sys 库里分析这类语句的执行情况,包括执行次数,执行时间,扫描的记录数,锁定的时间,是否用到排序等等。

2. 查询重写插件
比如要阻止对表 p1 通过字段 r1 的删除动作,可以用查询重写插件在 MySQL 语句分析层直接转换,这时候就得用到摘要函数 statement_digest_text。
假设:表 p1 字段 id 值全部为正。
delete from p1 where id = 1000;
要改写为,
delete from p1 where id = -1;
利用函数 statement_digest_text 来定制这条 SQL 的重写规则。
mysql> INSERT INTO query_rewrite.rewrite_rules (pattern, replacement,pattern_database) -> VALUES( -> statement_digest_text('delete from p1 where id = 1000') , -> statement_digest_text('delete from p1 where id = -1'), -> 'ytt' -> );Query OK, 1 row affected (0.01 sec)
语句被查询重写后的效果:
mysql> delete from p1 where id = 20000;Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> show warnings\G*************************** 1. row *************************** Level: Note Code: 1105Message: Query 'delete from p1 where id = 20000' rewritten to 'DELETE FROM `p1` WHERE `id` = - 20000' by a query rewrite plugin1 row in set (0.00 sec)
mysql> select count(*) from p1;+----------+| count(*) |+----------+| 9000001 |+----------+1 row in set (1.59 sec)
总结
MySQL 8.0 新增的语句摘要函数可以很方便的分析 SQL 语句执行的各个方面,比以前分析类似的场景要简单的多。

4. 数据分析过程如果用SQL语句进行统计如何实现

方法和详细的操作步骤如下:

1、第一步,创建一个测试表,详细代码见下图,转到下面的步骤。

5. MySql中Sql的执行过程

如果查询缓存没有命中,那么SQL请求会进入分析器,分析器是用来分辨SQL语句的执行目的,其执行过程大致分为两步:

表1 语法分析关键字然后再通过语法规则解析,判断输入的SQL 语句是否满足MySQL语法,并且生成图5的语法树。由SQL语句生成的四个单词中,识别出两个关键字,分别是select 和from。根据MySQL的语法Select 和 from之间对应的是fields 字段,下面应该挂接username;在from后面跟随的是Tables字段,其下挂接的是userinfo。

优化器的作用是对SQL进行优化,生成最有的执行方案。如图6所示,前面提到的SQL解析器通过语法分析和语法规则生成了SQL语法树。这个语法树作为优化器的输入,而优化器(黄色的部分)包含了逻辑变换和代价优化两部分的内容。在优化完成以后会生成SQL执行计划作为整个优化过程的输出,交给执行器在存储引擎上执行。

所处的位置如上图所示,这节的重点在优化器中的逻辑变换和代价优化上。

逻辑变换也就是在关系代数基础上进行变换,其目的是为了化简,同时保证SQL变化前后的结果一致,也就是逻辑变化并不会带来结果集的变化。其主要包括以下几个方面:

这样讲概念或许有些抽象,通过图7 来看看逻辑变化如何在SQL中执行的吧。

如图7所示,从上往下共有4个步骤:
1. 针对存在的SQL语句,首先通过“否定消除”,去掉条件判断中的“NOT”。语句由原来的“or”转换成“and”,并且大于小于符号进行变号。蓝色部分为修改前的SQL,红色是修改以后的SQL。2. 等值传递,这一步很好理解分别降”t2.a=9” 和”t2.b=5”分别替换掉SQL中对应的值。3. 接下来就是常量表达式计算,将“5+7”计算得到“12”。4. 最后是常量表达式计算后的化简,将”9<=10”化简为”true”带入到最终的SQL表达式中完成优化。

代价优化是用来确定每个表,根据条件是否应用索引,应用哪个索引和确定多表连接的顺序等问题。为了完成代价优化,需要找到一个代价最小的方案。因此,优化器是通过基于代价的计算方法来决定如何执行查询的(Cost-based Optimization)。简化的过程如下:

这里将配置操作的代价分为MySQL 服务层和MySQL 引擎层,MySQL 服务层主要是定义CPU的代价,而MySQL 引擎层主要定义IO代价。MySQL 5.7 引入了两个系统表mysql.server_cost和mysql.engine_cost来分别配置这两个层的代价。如下:MySQL 服务层代价保存在表server_cost中,其具体内容如下:

由上可以看出创建临时表的代价是很高的,尤其是内部的myisam或innodb临时表。MySQL 引擎层代价保存在表engine_cost中,其具体内容如下:

目前io_block_read_cost和memory_block_read_cost默认值均为1,实际生产中建议酌情调大memory_block_read_cost,特别是对普通硬盘的场景。MySQL会根据SQL查询生成的查询计划中对应的操作从上面两张代价表中查找对应的代价值,并且进行累加形成最终执行SQL计划的代价。再将多种可能的执行计划进行比较,选取最小代价的计划执行。

当分析器生成查询计划,并且经过优化器以后,就到了执行器。执行器会选择执行计划开始执行,但在执行之前会校验请求用户是否拥有查询的权限,如果没有权限,就会返回错误信息,否则将会去调用MySQL引擎层的接口,执行对应的SQL语句并且返回结果。例如SQL:“SELECT * FROM userinfo WHERE username = 'Tom';“假设 “username“ 字段没有设置索引,就会调用存储引擎从第一条开始查,如果碰到了用户名字是” Tom“, 就将结果集返回,没有查找到就查看下一行,重复上一步的操作,直到读完整个表或者找到对应的记录。需要注意SQL语句的执行顺序并不是按照书写顺序来的,顺序的定义会在分析器中做好,一般是按照如下顺序:

如果命中的记录比较多,应用会从MySql Server一批批获取数据

本文从MySQL中SQL语句的执行过程作为切入点,首先介绍了查询请求的执行流程,其中将MySQL的处理分为MySQL Server层和MySQL存储引擎层。通过介绍SQL语句的流转,引出了后面要介绍的5大组件,他们分别是:连接器、查询缓存、分析器、优化器、执行器。后面的内容中对每个组件进行了详细的介绍。连接器,负责身份认证和权限鉴别;查询缓存,将查询的结果集进行缓存,提高查询效率;分析器,对SQL语句执行语法分析和语法规则,生成语法树和执行计划;优化器,包括逻辑变换和代价优化;执行器,在检查用户权限以后对数据进行逐条查询,整个过程遵守SQL语句的执行顺序。

6. SQL 语句在数据库中是怎样执行的

第一步:应用程序把查询SQL语句发给服务器端执行

我们在数据层执行SQL语句时,应用程序会连接到相应的数据库服务器,把SQL语句发送给服务器处理。

第二步:服务器解析请求的SQL语句

SQL计划缓存,经常用查询分析器的朋友大概都知道这样一个事实,往往一个查询语句在第一次运行的时候需要执行特别长的时间,但是如果你马上或者在一定时间内运行同样的语句,会在很短的时间内返回查询结果。原因是:

  • 服务器在接收到查询请求后,并不会马上去数据库查询,而是在数据库中的计划缓存中找是否有相对应的执行计划。如果存在,就直接调用已经编译好的执行计划,节省了执行计划的编译时间。

  • 如果所查询的行已经存在于数据缓冲存储区中,就不用查询物理文件了,而是从缓存中取数据,这样从内存中取数据就会比从硬盘上读取数据快很多,提高了查询效率。数据缓冲存储区会在后面提到。

  • 如果在SQL计划缓存中没有对应的执行计划,服务器首先会对用户请求的SQL语句进行语法效验,如果有语法错误,服务器会结束查询操作,并用返回相应的错误信息给调用它的应用程序。

    注意:此时返回的错误信息中,只会包含基本的语法错误信息,例如select写成selec等,错误信息中如果包含一列表中本没有的列,此时服务器是不会检查出来的,因为只是语法验证,语义是否正确放在下一步进行。

    语法符合后,就开始验证它的语义是否正确。例如,表名、列名、存储过程等等数据库对象是否真正存在,如果发现有不存在的,就会报错给应用程序,同时结束查询。

    接下来就是获得对象的解析锁,我们在查询一个表时,首先服务器会对这个对象加锁,这是为了保证数据的统一性,如果不加锁,此时有数据插入,但因为没有加锁的原因,查询已经将这条记录读入,而有的插入会因为事务的失败会回滚,就会形成脏读的现象。

    接下来就是对数据库用户权限的验证。SQL语句语法,语义都正确,此时并不一定能够得到查询结果,如果数据库用户没有相应的访问权限,服务器会报出权限不足的错误给应用程序,在稍大的项目中,往往一个项目里面会包含好几个数据库连接串,这些数据库用户具有不同的权限,有的是只读权限,有的是只写权限,有的是可读可写,根据不同的操作选取不同的用户来执行。稍微不注意,无论你的SQL语句写的多么完善,完美无缺都没用。

    解析的最后一步,就是确定最终的执行计划。当语法、语义、权限都验证后,服务器并不会马上给你返回结果,而是会针对你的SQL进行优化,选择不同的查询算法以最高效的形式返回给应用程序。例如在做表联合查询时,服务器会根据开销成本来最终决定采用hashjoin,mergejoin ,还是loop join,采用哪一个索引会更高效等等。不过它的自动化优化是有限的,要想写出高效的查询SQL还是要优化自己的SQL查询语句。

    当确定好执行计划后,就会把这个执行计划保存到SQL计划缓存中,下次在有相同的执行请求时,就直接从计划缓存中取,避免重新编译执行计划。

    第三步:语句执行

    服务器对SQL语句解析完成后,服务器才会知道这条语句到底表态了什么意思,接下来才会真正的执行SQL语句。

    此时分两种情况:

  • 如果查询语句所包含的数据行已经读取到数据缓冲存储区的话,服务器会直接从数据缓冲存储区中读取数据返回给应用程序,避免了从物理文件中读取,提高查询速度。

  • 如果数据行没有在数据缓冲存储区中,则会从物理文件中读取记录返回给应用程序,同时把数据行写入数据缓冲存储区中,供下次使用。

  • 说明:SQL缓存分好几种,这里有兴趣的朋友可以去搜索一下。有时因为缓存的存在,使得我们很难马上看出优化的结果,因为第二次执行因为有缓存的存在,会特别快速,所以一般都是先消除缓存,然后比较优化前后的性能表现,这里有几个常用的方法:

    1 DBCC DROPCLEANBUFFERS

    2 从缓冲池中删除所有清除缓冲区。

    3 DBCC FREEPROCCACHE

    4 从过程缓存中删除所有元素。

    5 DBCC FREESYSTEMCACHE

    6 从所有缓存中释放所有未使用的缓存条目。

    SQL Server 2005数据库引擎会事先在后台清理未使用的缓存条目,以使内存可用于当前条目。但是,可以使用此命令从所有缓存中手动删除未使用的条目。

    这只能基本消除SQL缓存的影响,目前好像没有完全消除缓存的方案,如果大家有,请指教。

    执行顺序:

  • FROM子句返回初始结果集。

  • WHERE子句排除不满足搜索条件的行。

  • GROUP BY子句将选定的行收集到GROUP BY子句中各个唯一值的组中。

  • 选择列表中指定的聚合函数可以计算各组的汇总值。

  • 此外,HAVING子句排除不满足搜索条件的行。

  • 计算所有的表达式;

  • 使用order by对结果集进行排序。

  • 查找你要搜索的字段。

7. SQL语句执行过程详解

SQL语句执行过程详解
一条sql,plsql的执行到底是怎样执行的呢?
一、SQL语句执行原理:
第一步:客户端把语句发给服务器端执行当我们在客户端执行 select 语句时,客户端会把这条 SQL 语句发送给服务器端,让服务器端的
进程来处理这语句。也就是说,Oracle 客户端是不会做任何的操作,他的主要任务就是把客户端产生
的一些 SQL 语句发送给服务器端。虽然在客户端也有一个数据库进程,但是,这个进程的作用跟服务器
上的进程作用事不相同的。服务器上的数据库进程才会对SQL 语句进行相关的处理。不过,有个问题需
要说明,就是客户端的进程跟服务器的进程是一一对应的。也就是说,在客户端连接上服务器后,在客户
端与服务器端都会形成一个进程,客户端上的我们叫做客户端进程;而服务器上的我们叫做服务器进程。
第二步:语句解析
当客户端把 SQL 语句传送到服务器后,服务器进程会对该语句进行解析。同理,这个解析的工作,
也是在服务器端所进行的。虽然这只是一个解析的动作,但是,其会做很多“小动作”。
1. 查询高速缓存(library cache)。服务器进程在接到客户端传送过来的 SQL 语句时,不
会直接去数据库查询。而是会先在数据库的高速缓存中去查找,是否存在相同语句的执行计划。如果在
数据高速缓存中,则服务器进程就会直接执行这个 SQL 语句,省去后续的工作。所以,采用高速数据缓
存的话,可以提高 SQL 语句的查询效率。一方面是从内存中读取数据要比从硬盘中的数据文件中读取
数据效率要高,另一方面,也是因为这个语句解析的原因。
不过这里要注意一点,这个数据缓存跟有些客户端软件的数据缓存是两码事。有些客户端软件为了
提高查询效率,会在应用软件的客户端设置数据缓存。由于这些数据缓存的存在,可以提高客户端应用软
件的查询效率。但是,若其他人在服务器进行了相关的修改,由于应用软件数据缓存的存在,导致修改的
数据不能及时反映到客户端上。从这也可以看出,应用软件的数据缓存跟数据库服务器的高速数据缓存
不是一码事。
2. 语句合法性检查(data dict cache)。当在高速缓存中找不到对应的 SQL 语句时,则服
务器进程就会开始检查这条语句的合法性。这里主要是对 SQL 语句的语法进行检查,看看其是否合乎
语法规则。如果服务器进程认为这条 SQL 语句不符合语法规则的时候,就会把这个错误信息,反馈给客
户端。在这个语法检查的过程中,不会对 SQL 语句中所包含的表名、列名等等进行 SQL 他只是语法
上的检查。
3. 语言含义检查(data dict cache)。若 SQL 语句符合语法上的定义的话,则服务器进程
接下去会对语句中的字段、表等内容进行检查。看看这些字段、表是否在数据库中。如果表名与列名不
准确的话,则数据库会就会反馈错误信息给客户端。所以,有时候我们写 select 语句的时候,若语法
与表名或者列名同时写错的话,则系统是先提示说语法错误,等到语法完全正确后,再提示说列名或表名
错误。
4. 获得对象解析锁(control structer)。当语法、语义都正确后,系统就会对我们需要查询
的对象加锁。这主要是为了保障数据的一致性,防止我们在查询的过程中,其他用户对这个对象的结构发
生改变。
5. 数据访问权限的核对(data dict cache)。当语法、语义通过检查之后,客户端还不一定
能够取得数据。服务器进程还会检查,你所连接的用户是否有这个数据访问的权限。若你连接上服务器
的用户不具有数据访问权限的话,则客户端就不能够取得这些数据。有时候我们查询数据的时候,辛辛苦
苦地把 SQL 语句写好、编译通过,但是,最后系统返回个 “没有权限访问数据”的错误信息,让我们气
半死。这在前端应用软件开发调试的过程中,可能会碰到。所以,要注意这个问题,数据库服务器进程先
检查语法与语义,然后才会检查访问权限。
6. 确定最佳执行计划 ?。当语句与语法都没有问题,权限也匹配的话,服务器进程还是不会直接对
数据库文件进行查询。服务器进程会根据一定的规则,对这条语句进行优化。不过要注意,这个优化是有
限的。一般在应用软件开发的过程中,需要对数据库的 sql 语言进行优化,这个优化的作用要大大地大
于服务器进程的自我优化。所以,一般在应用软件开发的时候,数据库的优化是少不了的。当服务器进程
的优化器确定这条查询语句的最佳执行计划后,就会将这条 SQL 语句与执行计划保存到数据高速缓存
(library cache)。如此的话,等以后还有这个查询时,就会省略以上的语法、语义与权限检查的步骤,
而直接执行 SQL 语句,提高 SQL 语句处理效率。
第三步:语句执行
语句解析只是对 SQL 语句的语法进行解析,以确保服务器能够知道这条语句到底表达的是什么意
思。等到语句解析完成之后,数据库服务器进程才会真正的执行这条 SQL 语句。这个语句执行也分两
种情况。
一是若被选择行所在的数据块已经被读取到数据缓冲区的话,则服务器进程会直接把这个数据传递
给客户端,而不是从数据库文件中去查询数据。
若数据不在缓冲区中,则服务器进程将从数据库文件中查询相关数据,并把这些数据放入到数据缓冲
区中(buffer cache)。
第四步:提取数据
当语句执行完成之后,查询到的数据还是在服务器进程中,还没有被传送到客户端的用户进程。所以,
在服务器端的进程中,有一个专门负责数据提取的一段代码。他的作用就是把查询到的数据结果返回给
用户端进程,从而完成整个查询动作。从这整个查询处理过程中,我们在数据库开发或者应用软件开发过
程中,需要注意以下几点:
一是要了解数据库缓存跟应用软件缓存是两码事情。数据库缓存只有在数据库服务器端才存在,在
客户端是不存在的。只有如此,才能够保证数据库缓存中的内容跟数据库文件的内容一致。才能够根据
相关的规则,防止数据脏读、错读的发生。而应用软件所涉及的数据缓存,由于跟数据库缓存不是一码事
情,所以,应用软件的数据缓存虽然可以提高数据的查询效率,但是,却打破了数据一致性的要求,有时候
会发生脏读、错读等情况的发生。所以,有时候,在应用软件上有专门一个功能,用来在必要的时候清除
数据缓存。不过,这个数据缓存的清除,也只是清除本机上的数据缓存,或者说,只是清除这个应用程序
的数据缓存,而不会清除数据库的数据缓存。
二是绝大部分 SQL 语句都是按照这个处理过程处理的。我们 DBA 或者基于 Oracle 数据库的
开发人员了解这些语句的处理过程,对于我们进行涉及到 SQL 语句的开发与调试,是非常有帮助的。有
时候,掌握这些处理原则,可以减少我们排错的时间。特别要注意,数据库是把数据查询权限的审查放在
语法语义的后面进行检查的。所以,有时会若光用数据库的权限控制原则,可能还不能满足应用软件权限
控制的需要。此时,就需要应用软件的前台设置,实现权限管理的要求。而且,有时应用数据库的权限管
理,也有点显得繁琐,会增加服务器处理的工作量。因此,对于记录、字段等的查询权限控制,大部分程
序涉及人员喜欢在应用程序中实现,而不是在数据库上实现。
DBCC DROPCLEANBUFFERS
从缓冲池中删除所有清除缓冲区。
DBCC FREEPROCCACHE
从过程缓存中删除所有元素。
DBCC FREESYSTEMCACHE
从所有缓存中释放所有未使用的缓存条目
SQL语句中的函数、关键字、排序等执行顺序:
1. FROM 子句返回初始结果集。
2. WHERE 子句排除不满足搜索条件的行。
3. GROUP BY 子句将选定的行收集到 GROUP BY 子句中各个唯一值的组中。
4. 选择列表中指定的聚合函数可以计算各组的汇总值。
5. 此外,HAVING 子句排除不满足搜索条件的行。
6. 计算所有的表达式;
7. 使用 order by 对结果集进行排序。
8. 查找你要搜索的字段。
二、SQL语句执行完整过程:
1.用户进程提交一个 sql 语句:
update temp set a=a*2,给服务器进程。
2.服务器进程从用户进程把信息接收到后,在 PGA 中就要此进程分配所需内存,存储相关的信息,如在会
话内存存储相关的登录信息等。
3.服务器进程把这个 sql 语句的字符转化为 ASCII 等效数字码,接着这个 ASCII 码被传递给一个
HASH 函数,并返回一个 hash 值,然后服务器进程将到shared pool 中的 library cache 中去查找是否存在相
同的 hash 值,如果存在,服务器进程将使用这条语句已高速缓存在 SHARED POOL 的library cache 中的已
分析过的版本来执行。
4.如果不存在,服务器进程将在 CGA 中,配合 UGA 内容对 sql,进行语法分析,首先检查语法的正确性,接
着对语句中涉及的表,索引,视图等对象进行解析,并对照数据字典检查这些对象的名称以及相关结构,并根据
ORACLE 选用的优化模式以及数据字典中是否存在相应对象的统计数据和是否使用了存储大纲来生成一个
执行计划或从存储大纲中选用一个执行计划,然后再用数据字典核对此用户对相应对象的执行权限,最后生成
一个编译代码。
5.ORACLE 将这条 sql 语句的本身实际文本、HASH 值、编译代码、与此语名相关联的任何统计数据
和该语句的执行计划缓存在 SHARED POOL 的 library cache中。服务器进程通过 SHARED POOL 锁存
器(shared pool latch)来申请可以向哪些共享 PL/SQL 区中缓存这此内容,也就是说被SHARED POOL 锁存
器锁定的 PL/SQL 区中的块不可被覆盖,因为这些块可能被其它进程所使用。
6.在 SQL 分析阶段将用到 LIBRARY
CACHE,从数据字典中核对表、视图等结构的时候,需要将数据
字典从磁盘读入 LIBRARY
CACHE,因此,在读入之前也要使用LIBRARY
CACHE 锁存器(library cache
pin,library cache lock)来申请用于缓存数据字典。 到现在为止,这个 sql 语句已经被编译成可执行的代码了,
但还不知道要操作哪些数据,所以服务器进程还要为这个 sql 准备预处理数据。
7.首先服务器进程要判断所需数据是否在 db buffer 存在,如果存在且可用,则直接获取该数据,同时根据
LRU 算法增加其访问计数;如果 buffer 不存在所需数据,则要从数据文件上读取首先服务器进程将在表头部
请求 TM 锁(保证此事务执行过程其他用户不能修改表的结构),如果成功加 TM 锁,再请求一些行级锁(TX
锁),如果 TM、TX 锁都成功加锁,那么才开始从数据文件读数据,在读数据之前,要先为读取的文件准备好
buffer 空间。服务器进程需要扫面 LRU list 寻找 free db buffer,扫描的过程中,服务器进程会把发现的所有
已经被修改过的 db buffer 注册到 dirty list 中, 这些 dirty buffer 会通过 dbwr 的触发条件,随后会被写出到
数据文件,找到了足够的空闲 buffer,就可以把请求的数据行所在的数据块放入到 db buffer 的空闲区域或者
覆盖已经被挤出 LRU list 的非脏数据块缓冲区,并排列在 LRU list 的头部,也就是在数据块放入 DB
BUFFER 之前也是要先申请 db buffer 中的锁存器,成功加锁后,才能读数据到 db buffer。
8.记日志 现在数据已经被读入到 db buffer 了,现在服务器进程将该语句所影响的并被读
入 db buffer 中的这些行数据的 rowid 及要更新的原值和新值及 scn 等信息从 PGA 逐条的写入 redo log
buffer 中。在写入 redo log buffer 之前也要事先请求 redo log buffer 的锁存器,成功加锁后才开始写入,当
写入达到 redo log buffer 大小的三分之一或写入量达到 1M 或超过三秒后或发生检查点时或者 dbwr 之前
发生,都会触发 lgwr 进程把 redo log buffer 的数据写入磁盘上的 redo file 文件中(这个时候会产生log file
sync 等待事件)
已经被写入 redofile 的 redo log buffer 所持有的锁存器会被释放,并可被后来的写入信息覆盖,
redo log buffer是循环使用的。Redo file 也是循环使用的,当一个 redo file 写满后,lgwr 进程会自动切换到
下一 redo file(这个时候可能出现 log fileswitch(checkpoint complete)等待事件)。如果是归档模式,归档进
程还要将前一个写满的 redo file 文件的内容写到归档日志文件中(这个时候可能出现 log file
switch(archiving needed)。
9.为事务建立回滚段 在完成本事务所有相关的 redo log buffer 之后,服务器进程开始改写这个 db buffer
的块头部事务列表并写入 scn,然后 包含这个块的头部事务列表及 scn 信息的数据副本放入回滚段中,将
这时回滚段中的信息称为数据块的“前映像“,这个”前映像“用于以后的回滚、恢复和一致性读。(回滚段可以
存储在专门的回滚表空间中,这个表空间由一个或多个物理文件组成,并专用于回滚表空间,回滚段也可在其它
表空间中的数据文件中开辟。
10.本事务修改数据块 准备工作都已经做好了,现在可以改写 db buffer 块的数据内容了,并在块的头部写
入回滚段的地址。
11.放入 dirty list 如果一个行数据多次 update 而未 commit,则在回滚段中将会有多个“前映像“,除了第
一个”前映像“含有 scn 信息外,其他每个“前映像“的头部都有 scn 信息和“前前映像”回滚段地址。一个
update 只对应一个 scn,然后服务器进程将在 dirty list 中建立一
条指向此 db buffer 块的指针(方便 dbwr 进程可以找到 dirty list 的 db buffer 数据块并写入数据文件中)。
接着服务器进程会从数据文件中继续读入第二个数据块,重复前一数据块的动作,数据块的读入、记日志、建
立回滚段、修改数据块、放入 dirty list。当 dirty queue 的长度达到阀值(一般是 25%),服务器进程将通知
dbwr 把脏数据写出,就是释放 db buffer 上的锁存器,腾出更多的 free db buffer。前面一直都是在说明
oracle 一次读一个数据块,其实 oracle 可以一次读入多个数据块(db_file_multiblock_read_count 来设置一
次读入块的个数)
说明:
在预处理的数据已经缓存在 db buffer 或刚刚被从数据文件读入到 db buffer 中,就要根据 sql 语句
的类型来决定接下来如何操作。
1>如果是 select 语句,则要查看 db buffer 块的头部是否有事务,如果有事务,则从回滚段中读取数据;如
果没有事务,则比较 select 的 scn 和 db buffer 块头部的 scn,如果前者小于后者,仍然要从回滚段中读取数据;
如果前者大于后者,说明这是一非脏缓存,可以直接读取这个 db buffer 块的中内容。
2>如果是 DML 操作,则即使在 db buffer 中找到一个没有事务,而且 SCN 比自己小的非脏
缓存数据块,服务器进程仍然要到表的头部对这条记录申请加锁,加锁成功才能进行后续动作,如果不成功,则要
等待前面的进程解锁后才能进行动作(这个时候阻塞是 tx 锁阻塞)。
用户 commit 或 rollback 到现在为止,数据已经在 db buffer 或数据文件中修改完
成,但是否要永久写到数文件中,要由用户来决定 commit(保存更改到数据文件) rollback 撤销数据的更改)。
1.用户执行 commit 命令
只有当 sql 语句所影响的所有行所在的最后一个块被读入 db buffer 并且重做信息被写入 redo log
buffer(仅指日志缓冲区,而不包括日志文件)之后,用户才可以发去 commit 命令,commit 触发 lgwr 进程,但不
强制立即 dbwr来释放所有相应 db buffer 块的锁(也就是no-force-at-commit,即提交不强制写),也就是说有
可能虽然已经 commit 了,但在随后的一段时间内 dbwr 还在写这条 sql 语句所涉及的数据块。表头部的行锁
并不在 commit 之后立即释放,而是要等 dbwr 进程完成之后才释放,这就可能会出现一个用户请求另一用户
已经 commit 的资源不成功的现象。
A .从 Commit 和 dbwr 进程结束之间的时间很短,如果恰巧在 commit 之后,dbwr 未结束之前断电,因为
commit 之后的数据已经属于数据文件的内容,但这部分文件没有完全写入到数据文件中。所以需要前滚。由
于 commit 已经触发 lgwr,这些所有未来得及写入数据文件的更改会在实例重启后,由 smon 进程根据重做日
志文件来前滚,完成之前 commit 未完成的工作(即把更改写入数据文件)。
B.如果未 commit 就断电了,因为数据已经在 db buffer 更改了,没有 commit,说明这部分数据不属于数
据文件,由于 dbwr 之前触发 lgwr 也就是只要数据更改,(肯定要先有 log) 所有 DBWR,在数据文件上的修改
都会被先一步记入重做日志文件,实例重启后,SMON 进程再根据重做日志文件来回滚。
其实 smon 的前滚回滚是根据检查点来完成的,当一个全部检查点发生的时候,首先让 LGWR 进程将
redo log buffer 中的所有缓冲(包含未提交的重做信息)写入重做日志文件,然后让 dbwr 进程将 db buffer 已
提交的缓冲写入数据文件(不强制写未提交的)。然后更新控制文件和数据文件头部的 SCN,表明当前数据库
是一致的,在相邻的两个检查点之间有很多事务,有提交和未提交的。
像前面的前滚回滚比较完整的说法是如下的说明:

A.发生检查点之前断电,并且当时有一个未提交的改变正在进行,实例重启之后,SMON 进程将从上一个
检查点开始核对这个检查点之后记录在重做日志文件中已提交的和未提交改变,因为
dbwr 之前会触发 lgwr,所以 dbwr 对数据文件的修改一定会被先记录在重做日志文件中。因此,断电前被
DBWN 写进数据文件的改变将通过重做日志文件中的记录进行还原,叫做回滚,
B. 如果断电时有一个已提交,但 dbwr 动作还没有完全完成的改变存在,因为已经提交,提交会触发 lgwr
进程,所以不管 dbwr 动作是否已完成,该语句将要影响的行及其产生的结果一定已经记录在重做日志文件中
了,则实例重启后,SMON 进程根据重做日志文件进行前滚.
实例失败后用于恢复的时间由两个检查点之间的间隔大小来决定,可以通个四个参数设置检查点执行的频
率:

Log_checkpoint_interval:
决定两个检查点之间写入重做日志文件的系统物理块(redo blocks)
的大小,默认值是 0,无限制。
log_checkpoint_timeout:
两 个 检 查 点 之 间 的 时 间 长 度(秒)默 认 值 1800s。
fast_start_io_target:
决定了用于恢复时需要处理的块的多少,默认值是 0,无限制。
fast_start_mttr_target:
直接决定了用于恢复的时间的长短,默认值是 0,无限制(SMON 进程执行的前滚
和回滚与用户的回滚是不同的,SMON 是根据重做日志文件进行前滚或回滚,而用户的回滚一定是根据回滚段
的内容进行回滚的。
在这里要说一下回滚段存储的数据,假如是 delete 操作,则回滚段将会记录整个行的数据,假如是 update,
则回滚段只记录被修改了的字段的变化前的数据(前映像),也就是没有被修改的字段是不会被记录的,假如是
insert,则回滚段只记录插入记录的 rowid。 这样假如事务提交,那回滚段中简单标记该事务已经提交;假如是
回退,则如果操作是 delete,回退的时候把回滚段中数据重新写回数据块,操作如果是 update,则把变化前数据
修改回去,操作如果是 insert,则根据记录的 rowid 把该记录删除。
2.如果用户 rollback。
则服务器进程会根据数据文件块和 DB BUFFER 中块的头部的事务列表和 SCN 以及回滚段地址找到
回滚段中相应的修改前的副本,并且用这些原值来还原当前数据文件中已修改但未提交的改变。如果有多个
“前映像”,服务器进程会在一个“前映像”的头部找到“前前映像”的回滚段地址,一直找到同一事务下的最早的
一个“前映像”为止。一旦发出了 COMMIT,用户就不能rollback,这使得 COMMIT 后 DBWR 进程还没有
全部完成的后续动作得到了保障。到现在为例一个事务已经结束了。
说明:
TM 锁:
符合 lock 机制的,用于保护对象的定义不被修改。 TX 锁:
这个锁代表一个事务,是行
级锁,用数据块头、数据记录头的一些字段表示,也是符合 lock 机制,有 resource structure、lock
structure、enqueue 算法。

8. oracle怎样跟踪一条sql语句的执行过程

需要建立一张表来记录
explain plan SET statement_id='name' FOR (这里是你要调试的语句 )

SELECT
A.OPERATION,
OPTIONS,
OBJECT_NAME,
OBJECT_TYPE,
ID,
PARENT_ID
FROM
PLAN_TABLE A
WHERE
STATEMENT_ID='name'
ORDER BY
Id;

ID 'name'是一个标识,你可以自己取,字段有很多个,以下是各个字段的解释(可能格式不对,你可以复制后看):

字段名 字段类型 含义
STATEMENT_ID VARCHAR2(30) explain PLAN 语句中所指定的最优STATEMENT_ID 参数值, 如果在EXPLAN PLAN语句中没有使用SET STATEMENT_ID,那么此值会被设为NULL。

REMARKS VARCHAR2(80) 与被解释规划的各步骤相关联的注释最长可达80 字节

OPERATION VARCHAR2(30) 各步骤所执行内部操作的名称在某条语句所产生的第一行中该列的可能取值如下DELETE STATEMENT INSERT STATEMENT SELECT STATEMENT UPDATE STATEMENT

OPTIONS VARCHAR2(30) 对OPERATION 列中所描述操作的变种

OBJECT_NODE VARCHAR2(128) 用于访问对象的数据库链接database link 的名称对于使用并行执行的本地查询该列能够描述操作中输出的次序

OBJECT_OWNER VARCHAR2(30) 对于包含有表或索引的架构schema 给出其所有者的名称

OBJECT_NAME VARCHAR2(30) 表或索引的名称

OBJECT_INSTANCE INTEGER 根据对象出现在原始original 语句中的次序所给出的相应次序编号就原始的语句文本而论其处理顺序为自左至右自外向内景象扩张view

OBJECT_TYPE VARCHAR2(30) 用于提供对象描述性信息的修饰符例如索引的NON-UNIQUE

OPTIMIZER VARCHAR2(255) 当前优化程序的模式

ID INTEGER 分配给执行规划各步骤的编号

PARENT_ID INTEGER 对ID 步骤的输出进行操作的下一个执行步骤的ID

POSITION INTEGER 对于具有相同PARENT_ID 的步骤其相应的处理次序

COST INTEGER 根据优化程序的基于开销的方法所估计出的操作开销值对于使用基于规则方法的语句该列为空该列值没有特定的测量单位它只是一个用于比较执行规划开销大小的权重值

CARDINALITY INTEGER 根据基于开销的方法对操作所访问行数的估计值

BYTES INTEGER 根据基于开销的方法对操作所访问字节的估计
=============================================

你按照我说的做,后面用
SELECT
*
FROM
PLAN_TABLE A
WHERE
STATEMENT_ID='name'

结果已经很清楚了,全部满足你的要求。
各列的具体含义上面已经给出。

9. SQL执行与优化

SQL优化

执行计划,表关联查询顺序,优化策略与思路

下面再向前走一些,容我根据自己的认识说一下查询执行的流程是怎样的:

1.连接

1.1客户端发起一条Query请求,监听客户端的‘连接管理模块’接收请求

1.2将请求转发到‘连接进/线程模块’

1.3调用‘用户模块’来进行授权检查

1.4通过检查后,‘连接进/线程模块’从‘线程连接池’中取出空闲的被缓存的连接线程和客户端请求对接,如果失败则创建一个新的连接请求

2.处理

2.1先查询缓存,检查Query语句是否完全匹配,接着再检查是否具有权限,都成功则直接取数据返回

2.2上一步有失败则转交给‘命令解析器’,经过词法分析,语法分析后生成解析树

2.3接下来是预处理阶段,处理解析器无法解决的语义,检查权限等,生成新的解析树

2.4再转交给对应的模块处理

2.5如果是SELECT查询还会经由‘查询优化器’做大量的优化,生成执行计划

2.6模块收到请求后,通过‘访问控制模块’检查所连接的用户是否有访问目标表和目标字段的权限

2.7有则调用‘表管理模块’,先是查看table cache中是否存在,有则直接对应的表和获取锁,否则重新打开表文件

2.8根据表的meta数据,获取表的存储引擎类型等信息,通过接口调用对应的存储引擎处理

2.9上述过程中产生数据变化的时候,若打开日志功能,则会记录到相应二进制日志文件中

3.结果

3.1Query请求完成后,将结果集返回给‘连接进/线程模块’

3.2返回的也可以是相应的状态标识,如成功或失败等

3.3‘连接进/线程模块’进行后续的清理工作,并继续等待请求或断开与客户端的连接

接下来再走一步,让我们看看一条SQL语句的前世今生。

首先看一下示例语句

示例语句

执行顺序

SQL解析

1. FROM

当涉及多个表的时候,左边表的输出会作为右边表的输入,之后会生成一个虚拟表VT1。

(1-J1)笛卡尔积

计算两个相关联表的笛卡尔积(CROSS JOIN) ,生成虚拟表VT1-J1。

两次全表扫描

哈希索引,查找复杂度都是 O(1)

2. WHERE

对VT1过程中生成的临时表进行过滤,满足WHERE子句的列被插入到VT2表中。

注意:

此时因为分组,不能使用聚合运算;也不能使用SELECT中创建的别名;

与ON的区别:

如果有外部列,ON针对过滤的是关联表,主表(保留表)会返回所有的列;

如果没有添加外部列,两者的效果是一样的;

应用:

对主表的过滤应该放在WHERE;

对于关联表,先条件查询后连接则用ON,先连接后条件查询则用WHERE;

hash join 哈希连接 驱动表和被驱动表都只会访问0次或1次

应用场景:一个大表一个小表/表上没有索引/返回结果集比较大

3. GROUP BY

这个子句会把VT2中生成的表按照GROUP BY中的列进行分组。生成VT3表。

注意:

其后处理过程的语句,如SELECT,HAVING,所用到的列必须包含在GROUP BY中,对于没有出现的,得用聚合函数;

原因:

GROUP BY改变了对表的引用,将其转换为新的引用方式,能够对其进行下一级逻辑操作的列会减少;

原作者的理解是:

根据分组字段,将具有相同分组字段的记录归并成一条记录,因为每一个分组只能返回一条记录,除非是被过滤掉了,而不在分组字段里面的字段可能会有多个值,多个值是无法放进一条记录的,所以必须通过聚合函数将这些具有多值的列转换成单值;

GROUP BY 重新聚合查询

4. HAVING

这个子句对VT3表中的不同的组进行过滤,只作用于分组后的数据,满足HAVING条件的子句被加入到VT4表中。

7.LIMIT

LIMIT子句从上一步得到的VT6虚拟表中选出从指定位置开始的指定行数据。

注意:

offset和rows的正负带来的影响;

当偏移量很大时效率是很低的,可以这么做:

采用子查询的方式优化,在子查询里先从索引获取到最大id,然后倒序排,再取N行结果集

采用INNER JOIN优化,JOIN子句里也优先从索引获取ID列表,然后直接关联查询获得最终结果

当前未用到索引,

三次full scan , table1 AS a / table2 AS b / GROUP BY

尽量少做重复的工作

控制同一语句的多次执/减少多次的数据转换/

杜绝不必要的子查询和连接表,子查询在执行计划一般解释成外连接,多余的连接表带来额外的开销

关于临时表和表变量的选择

临时表产生使用SELECT INTO和CREATE TABLE + INSERT INTO的选择,一般情况下,SELECT INTO会比CREATE TABLE + INSERT INTO的方法快很多,但是SELECT INTO会锁定TEMPDB的系统表SYSOBJECTS、SYSINDEXES、SYSCOLUMNS,在多用户并发环境下,容易阻塞其他进程,所以建议,在并发系统中,尽量使用CREATE TABLE + INSERT INTO,而大数据量的单个语句使用中,使用SELECT INTO。

子查询的用法

相关子查询可以用IN、NOT IN、EXISTS、NOT EXISTS引入

NOT IN、NOT EXISTS的相关子查询可以改用LEFT JOIN代替写法

如果保证子查询没有重复 ,IN、EXISTS的相关子查询可以用INNER JOIN 代替

IN``的相关子查询用EXISTS代替

不要用 COUNT (*)的子查询判断是否存在记录,最好用 LEFT` `JOIN 或者EXISTS,比如有人写这样的语句:

建立索引后,并不是每个查询都会使用索引,在使用索引的情况下,索引的使用效率也会有很大的差别。只要我们在查询语句中没有强制指定索引,

不要对索引字段进行运算,而要想办法做变换

不要对索引字段进行格式转换

不要对索引字段使用函数

不要对索引字段进行多字段连接

join关联查询的计算是很复杂的,特别是数据量比较大的情况下,实际情况还是拆解较快的

Join拆解的核心就是利用In关键字

要么用空间换时间,要么用时间换空间

多表连接的连接条件对索引的选择有着重要的意义,所以我们在写连接条件条件的时候需要特别注意。

A、多表连接的时候,连接条件必须写全,宁可重复,不要缺漏。

B、连接条件尽量使用聚集索引

C、注意ON、WHERE和HAVING部分条件的区别

ON是最先执行, WHERE次之,HAVING最后,因为ON是先把不符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该速度是最快的,WHERE也应该比 HAVING快点的,因为它过滤数据后才进行SUM,在两个表联接时才用ON的,所以在一个表的时候,就剩下WHERE跟HAVING比较了

考虑联接优先顺序:

(1)INNER JOIN

(2)LEFT JOIN (注:RIGHT JOIN 用 LEFT JOIN 替代)

(3)CROSS JOIN

索引并不适用于所有情况:a.少量数据;b.频繁进行改动的字段,不适合做索引;c.很少使用的字段,不需要加索引

索引会提高数据查询效率,但是会降低“增、删、改”的效率。当不使用索引的时候,我们进行数据的增删改,只需要操作源表即可,但是当我们添加索引后,不仅需要修改源表,也需要再次修改索引,很麻烦。

先执行顺序, 是否走索引, 有无类型转换

18000 字的SQL优化大全

步步深入:MySQL架构总览->查询执行流程->SQL解析顺序

MySQL索引总结(4)——btree与hash区别

10. SQL优化万能公式:5 大步骤 + 10 个案例

在应用开发的早期,数据量少,开发人员开发功能时更重视功能上的实现,随着生产数据的增长,很多SQL语句开始暴露出性能问题,对生产的影响也越来越大,有时可能这些有问题的SQL就是整个系统性能的瓶颈。

1、通过慢查日志等定位那些执行效率较低的SQL语句

2、explain 分析SQL的执行计划

type由上至下,效率越来越高

Extra

3、show profile 分析

了解SQL执行的线程的状态及消耗的时间。默认是关闭的,开启语句“set profiling = 1;”

4、trace

trace分析优化器如何选择执行计划,通过trace文件能够进一步了解为什么优惠券选择A执行计划而不选择B执行计划。

5、确定问题并采用相应的措施

案例1、最左匹配

索引

SQL语句

查询匹配从左往右匹配,要使用order_no走索引,必须查询条件携带shop_id或者索引( shop_id , order_no )调换前后顺序

案例2、隐式转换

索引

SQL语句

隐式转换相当于在索引上做运算,会让索引失效。mobile是字符类型,使用了数字,应该使用字符串匹配,否则MySQL会用到隐式替换,导致索引失效。

案例3、大分页

索引

SQL语句

对于大分页的场景,可以优先让产品优化需求,如果没有优化的,有如下两种优化方式, 一种是把上一次的最后一条数据,也即上面的c传过来,然后做“c < xxx”处理,但是这种一般需要改接口协议,并不一定可行。另一种是采用延迟关联的方式进行处理,减少SQL回表,但是要记得索引需要完全覆盖才有效果,SQL改动如下

案例4、in + order by

索引

SQL语句

in查询在MySQL底层是通过n*m的方式去搜索,类似union,但是效率比union高。in查询在进行cost代价计算时(代价 = 元组数 * IO平均值),是通过将in包含的数值,一条条去查询获取元组数的,因此这个计算过程会比较的慢,所以MySQL设置了个临界值(eq_range_index_pe_limit),5.6之后超过这个临界值后该列的cost就不参与计算了。因此会导致执行计划选择不准确。默认是200,即in条件超过了200个数据,会导致in的代价计算存在问题,可能会导致Mysql选择的索引不准确。

处理方式,可以( order_status , created_at )互换前后顺序,并且调整SQL为延迟关联。

案例5、范围查询阻断,后续字段不能走索引

索引

SQL语句

范围查询还有“IN、between”

案例6、不等于、不包含不能用到索引的快速搜索。(可以用到ICP)

在索引上,避免使用NOT、!=、>、!、NOT EXISTS、NOT IN、NOT LIKE等

案例7、优化器选择不使用索引的情况

如果要求访问的数据量很小,则优化器还是会选择辅助索引,但是当访问的数据占整个表中数据的蛮大一部分时(一般是20%左右),优化器会选择通过聚集索引来查找数据。

查询出所有未支付的订单,一般这种订单是很少的,即使建了索引,也没法使用索引。

案例8、复杂查询

如果是统计某些数据,可能改用数仓进行解决;如果是业务上就有那么复杂的查询,可能就不建议继续走SQL了,而是采用其他的方式进行解决,比如使用ES等进行解决。

案例9、asc和desc混用

desc 和asc混用时会导致索引失效

案例10、大数据

对于推送业务的数据存储,可能数据量会很大,如果在方案的选择上,最终选择存储在MySQL上,并且做7天等有效期的保存。那么需要注意,频繁的清理数据,会照成数据碎片,需要联系DBA进行数据碎片处理。