逻辑IO
逻辑读:就是服务器进程从SGA上的buffercache(高速缓存)区域(先)根据SQL语句解析过程所获得的要操作的数据块的地址找到相关的数据块(后,再)读取这些相关的数据块上的相关数据行,到该服务器进程的PGA内存上,这就是一次逻辑读。简单地说,就是一次从buffer cache读取内容到PGA内存上的过程。
注释:
首先,这些数据块指的是在buffer cache上的数据块,也就是在内存上的数据块。若是服务器进程发现在buffercache上找不到服务器进程想要的数据块,服务器进程就会根据该数据块的地址从磁盘的数据文件上将之读取到buffercache上来,这就是一次物理读,然后再从buffercache上将该数据块读取到该服务器进程的PGA内存上,这就是一次逻辑读(这个不算做一次逻辑读,一次逻辑读特指一次从buffercache读取内容到PGA内存上的过程)。
接着,逻辑读过程中,如果服务器进程从SGA上的buffer cache(高速缓存)区域上找到的某一个数据块上有很多行要读取时,可能就会一次读取不完,要多次逻辑读,这个取决于参数arrysize(即一次逻辑读读取几行数据行)。物理读不知道有这个限制否?
最后一点是,select操作要使用逻辑读,它从buffer cache读取内容到PGA内存上自己的结果集中,再发给客户端。
(姑且认为有一种逻辑写的概念,那么)逻辑写:就是服务器进程从SGA上的buffercache(高速缓存)区域(先)根据SQL语句解析过程所获得的要操作的数据块的地址找到相关的数据块(后,再)修改这些相关的数据块上的相关数据行的内容,这就是一次逻辑写。
注释:
DML操作要使用逻辑写,它在buffer cache上修改相关的数据块上的相关数据行的内容。
逻辑读和逻辑写共同一步骤:
都是要根据SQL语句解析过程所获得的要操作的数据块的地址找到相关的数据块。
逻辑读和逻辑写不同一步骤:
之后,逻辑读是读取这些相关的数据块上的相关数据行到该服务器进程的PGA内存上,而逻辑写是修改这些相关的数据块上的相关数据行的内容。
或者另外一种理解是说,
逻辑读就是根据SQL语句解析过程所获得的要操作的数据块的地址找到相关的数据块的过程。
姑且认为有一种逻辑写的概念,那么逻辑写就是是修改这些找到的相关的数据块上的相关数据行的内容的过程。
注释:
逻辑读包括当前模式读( current read)和一致性读(consistent read)两种情况。
在oracle中,没有所谓逻辑写的概念。逻辑读既是读数据块也是写数据块的意思。见于《oracle性能诊断艺术》2.4读写数据块。
======================================================================================
什么是当前模式(Current Mode)?或者说什么是当前模式读( current read)?
当发生修改数据操作时,Oracle从buffer cache中,找到被修改数据行所在的数据块的过程,就是所谓的当前模式读 的过程。也就是说当前模式读的前提是发生在修改数据(DML)操作的情况下的。由当前模式读出来的数据块就是为了被修改的。而修改数据(DML)操作的第一步是一致性读的过程,第二步才是当前模式读的过程。
总之,DML操作(即修改数据的操作)的第二步过程才是当前模式读(过程),当前模式读(过程)就是DML操作(即修改数据的操作)的第二步过程。
强调下,当前模式读,它是操作中的一步过程。
统计信息里的db block gets的就是由当前模式读而得到的块的读取次数(该读取次数不等于块的个数,因为一次逻辑读不会读取完块里的所有数据,而是一次N数据行,这个N值就是客户端的变量arraysize),这些块包括DML操作要修改的数据行所在数据块(还有该要修改数据行的所在的表的段头块)以及用于保留该表原数据信息的undo块和undo块所在undo段的undo段头块。
至于DML操作的一致性读过程是以全表扫描的方式(全表扫描会读取表的所有数据块,这样consistent gets 的值自然就会高很多,也就意味着该DML操作的执行过程花的时间要长一些)还是其他访问方式取决于优化器(比如,优化器会根据DML操作语句里where的限制条件的具体形式来决定用什么访问方式)。
至于DML操作的当前模式读过程是应该是和DML操作的一致性读过程用同样的访问方式吧,不然set autotrace on后的执行计划里只有一份执行计划,而不是两个过程两份执行计划。(?)亦是或者,只是读取DML操作要修改的数据行所在数据块。
什么是一致性读(consistent read)?
一致性读(和普通的读之间最大的区别就在于),它会根据操作语句开始执行时的 SCN 从UNDO段(或说UNDO表空间)中(的undo块((来自buffer cache))上)读取 undo 内容并应用到需要进行一致性读的数据块(来自buffer cache)上,以保证数据的一致性。
统计信息里的consistent gets的就是由一致性读而得到的块的读取次数(该读取次数不等于块的个数,因为一次逻辑读不会读取完块里的所有数据,而是一次N数据行,这个N值就是客户端的变量arraysize),这些块包括操作(包括DML操作和select操作)要读取的数据行所在数据块(还有该要读取数据行的所在的表的段头块)以及因为有些读取来得数据块需要进行一致性读过程而要用到undo块故而读取(之前修改过有些读取来得数据块的其他事务所对应)的undo块和undo块所在undo段的undo段头块。
至于操作(包括DML操作和select操作)的一致性读过程是以全表扫描的方式(全表扫描会读取表的所有数据块,这样consistent gets 的值自然就会高很多,也就意味着该DML操作的执行过程花的时间要长一些)还是其他访问方式取决于优化器(比如,优化器会根据DML操作语句里where的限制条件的具体形式来决定用什么访问方式)。
CR块则是由当前块通过undo块的配合构造出的副本。
注释:
select操作(即读操作)开始执行时,它会以自己开始执行的时间SCN为准对它所要操作的表的所有数据内容进行一次一致性读模式的读取过程来回滚以获得select操作开始执行时的那一刻表的内容,接着再根据过滤条件(即where后的限制条件)过滤出select语句的结果,最后将这些结果显示在屏幕上。
DML操作(即写操作)开始执行时,它也会以自己开始执行的时间SCN为准对它所要操作的表的所有数据内容进行一次一致性读模式的读取过程来回滚以获得DML操作开始执行时的那一刻表的内容,接着再根据过滤条件(即where后的限制条件)过滤出DML操作的所要操作的那些数据行,最后DML操作根据这些数据行的地址以当前读模式找到在buffer cache上的相关数据行,对它们进行修改操作。
select操作(即读操作)和DML操作(即写操作)两个操作有一个共同点就是都有一个一致性读模式的读取过程,而DML操作(即写操作)还有一个当前读模式的修改过程(不带有order by字句的select操作是没有这一步过程)。所以,执行select操作时所得的统计信息里的db block gets 总是为0的。而执行DML操作时所得的统计信息里的db block gets 当然是不为0的。如下例子所示:
12. HELLODBA.COM>select * from bigtab;
13.
14. 529517 rows selected.
15.
16.
17. Statistics
18. ----------------------------------------------------------
19. 0 recursive calls
20. 0 db block gets
21. 42118 consistent gets
22. 7301 physical reads
12. HELLODBA.COM>update tt set x=1;
13.
14. 2 rows updated.
15.
16. Statistics
17. ----------------------------------------------------------
18. 0 recursive calls
19. 4 db block gets
20. 7 consistent gets
21. 8 physical reads
当然,无论什么类型的操作(select操作也是不例外的)里若是有order by即用于排序的字句存在的话,则db block gets 是不为0的,也就是说带有order by字句的select操作便也会有一个当前读模式的修改过程。例如,
12. HELLODBA.COM>select * from t_test2 order by table_name;
13.
14. 2072 rows selected.
15.
16. Statistics
17. ----------------------------------------------------------
18. 7 recursive calls
19. 21 db block gets
20. 65 consistent gets
21. 201 physical reads
22. 0 redo size
23. 131049 bytes sent via SQL*Net to client
24. 1903 bytes received via SQL*Net from client
25. 140 SQL*Net roundtrips to/from client
26. 0 sorts (memory)
27. 1 sorts (disk)
28. 2072 rows processed
这是由于获得的结果集的值要排序,其实质的操作就是修改存放结果集的各个数据块里的内容,比如本来在未排序前,数据行a放在数据块w上,经过排序后,数据行a放在了数据块y上了,这个过程其实就是修改了数据块w和y的内容,所以是按db block gets来统计的。
这样,我们修正下当前模式读的定义:
当发生修改数据过程时,Oracle从buffer cache中,找到被修改数据行所在的数据块的过程,就是所谓的当前模式读 的过程。也就是说当前模式读的前提是发生在修改数据过程的情况下的。由当前模式读出来的数据块就是为了被修改的。像DML操作的第一步是一致性读的过程,第二步才是当前模式读的过程;像order by字句对应的执行过程就是当前模式读的过程。
总之,修改数据的过程,包括order by字句对应的执行过程和DML操作的第二步过程,才是当前模式读(过程),当前模式读(过程)就是修改数据的过程,包括order by字句对应的执行过程和DML操作的第二步过程。
附加:
1、逻辑读的 db block gets 和 consistent gets 是被分别统计的,也就是说 SQL Trace 中的统计数据要
将两者相加才得到该语句的逻辑读的数量;而(DML操作时)物理读则是将两者被合并统计的(也就是说db block gets 和 consistent gets两个读取的相同的数据块(即数据块的地址相同)部分(这部分是两者重合的)不做两次计数,而是当做一次);
2、数据文件上空的数据块也是要先被读取到内存上后,才能被比如DML操作使用的。数据文件上空的数据块也是要先被读取到内存上,相当于该DML操作在数据文件上占用了该空的数据块。数据文件上空的数据块也是要先被读取到内存上,是因为空的数据块虽然存储数据行的那一层是空的,但是块的块头里还是存在一些关于块本身的信息的,如块地址,大小,块的类型等。
3、物理读的次数不是等于其读取的块的次数。其受参数db_file_multiblock_read_count等的影响。
注释:
Oracle DB_FILE_MULTIBLOCK_READ_COUNT是Oracle比较重要的一个全局性参数,可以影响系统级别及sessioin级别。主要是用于设置最小化表扫描时Oracle一次按顺序能够读取的数据块数。通常情况下,我们看到top events中的等待事件db file scattered read时会考虑到增加该参数的值。
参考:
http://blog.csdn.net/leshami/article/details/8985734
http://blog.itpub.net/23135684/viewspace-749200/
======================================================================================================================
(数据库实例启动后,)所有的操作(DML和select)针对的对象都是内存上的数据块,如果操作所要的数据块不在内存上,就会从数据文件上读取到内存上来。
说一下两个DML操作针对的是同一个数据块上的不同数据行这种情况:
假设会话1上的A操作先开始的,会话2上的B操作后开始的。A操作执行的过程如下:会话1的服务器进程先锁住(用闩锁)buffercache上整条LRU链表,此时其他进程不可访问链表的,接着,会话1的服务器进程找到链表上所要的数据块(之后还是在没锁住链表之前就先在该数据块对应的表上加上TM锁,具体就是,内存上在TM resource上加入一个加什么锁和加锁的表等信息构成的数据结构的节点 ),并pin住它,之后释放掉锁住在整条LRU链表上的闩锁,这样其他进程就可访问该链表了。会话1的服务器进程pin住所要的数据块后,就先用行级锁锁住块上相关的数据行,接着释放pin在该数据块上的pin锁,此时其他进程就可访问该数据块了,之后就是修改这些数据行。假设在会话1的服务器进程释放pin在该数据块上的pin锁后,会话2的服务器进程(代表B操作)来pin住了该数据块,如果会话2的服务器进程所要修改的数据行中是其他进程(如A操作)锁住的数据行,则此时B操作整个就会被阻塞住的,直到那个进程提交之后锁在的数据行上的锁才会被释放掉。如果会话2的服务器进程所要修改的数据行都是没有被其他进程(如A操作)锁住的数据行,那会话2的服务器进程是可以执行的,即使此时如A操作针对的数据行还没有提交。这个就是平时我们看到的多用户并发(比如)更新(同一个表)同一个数据块上的不同行而不相互阻塞的过程,他们操作的是同一个数据块(不是一个数据块的两个副本),他们操作的是在buffer cache上的同一个数据块,也就是所谓的当前模式数据块。
从上述过程,可以看到加锁的过程都是在内存上的数据块上完成的,数据文件上的表上一般是没有锁存在的,除非内存上没有提交的脏块写入到数据文件中,不过实例恢复回滚时这些脏块都会被撤销从数据文件上。
转载:http://blog.csdn.net/haiross/article/details/17008867