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