暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

上接下文-大对象直接读取

原创 由迪 2021-02-01
1183

可以看到,对其进行并行查询时,是通过物理直接读访问的高水位线下被格式化的 810 块数据块。参数“_small_table_threshold”是允许在会话级别被修改的,当我们该值修改为为大于等于表中高水位线下被格式化的数据块数时:
image.png
image.png
image.png
可以看到,临界值等于表中高水位线下被格式化的数据块数,对其并行查询访问时,数据块被 读入了换成而不是直接读取。

串行查询的直接读取
而在进行串行查询时,如果对象数据块不在缓存当中,通常都是将数据块读入缓存后再进行处 理。但是,10g 中还有另外一个隐含参数(“_serial_direct_read”,串行直接读取,默认为 false) 可以改变这种行为。当设置运行串行直接读取时,要注意两点:

  1. 与并行查询相同,是否直接读取与其是否为“小表”有关,但决定其是否为小表的数据是 高水位线以下的数据块数(包括元数据块和用户数据块);
  2. 当解析语句、为其子游标生成执行计划时,且高水位线以下的数据块数大于缓存数据块数
    (隐含参数“_db_block_buffers”决定)的 10%时,该子游标会必然直接读取对象;
  3. 当解析语句、为其子游标生成执行计划时,且高水位线以下的数据块数小于缓存数据块数 的 10%时,是否为“小表”仅为必要条件之一。是否直接读取,还与其他一些因素有关, 不仅受到语句游标上下文的影响,还与当前的共享缓存资源的使用情况、对象本身被直接

读取和缓存读取的数据块数/次数/频率等条件有关。这些决定因素的条件并不十分明确,, 在 10.2.04 环境中,我们观察到以下几种情况会直接读取对象:
a) 该数据对象在实例启动后第一次被以多数据块读方式访问;
b) 该数据对象在以多数据块读方式访问后,有大量数据块读入了缓存————接近 于缓存数据块总数的 95%;

下面的示例将演示这一特性。我们先启用串行查询的直接读取,全表扫描访问一张高水位线以下的数据块数大于缓存数据块数 10%的表:
image.png
而此时,在其游标未失效的情况下,即便将该特性关闭,还是会直接读取表:
image.png
image.png

提示:在 Library Cache 中的对象,会有多个内存堆(Heap,内存管理的基本单位,由 1 到多个扩展
段—————extent 或者子堆————Subheap 组成,从 Heap0~Heap7)存储其相关的数据结构。而对于一个 SQL 游标来说,主要有 2 个堆,Heap0 和 Heap6 存放其数据。Heap0 中存放对象的控制信息,如对象句柄、依赖对象地址,对于 SQL 游标而言,还会有执行计划的解析环境、绑定变量以及游标何时失效等数据;而 Heap6 中主要存放了游标执行相关信息,如对应的内部调用(Internal
Calls)及其上下文(Context)数据。而在执行计划的全表扫描操作的上下文数据中,则标识操作是 否以直接读取方式读取对象数据块。

我们先用包 DMBS_SHARE_POOL 将语句对象及其子游标保留在 Share Pool 中:
image.png
提示:在 MOS 的文档中提到过,11g 之前,dbms_shared_pool.keep()过程并不能确保 Heap6 的数据
一定被保留,在 Share Pool 资源紧张的情况下,还是有可能被释放、分配给其他游标。

我们找到全表扫描操作的上下文数据所在扩展段的地址:
image.png
而在该扩展段偏移地址 52 位(11gR2 中观察到为 56 位)的位置,可以看到提示操作是否直接读取表的标识:
image.png
image.png
image.png
image.png
image.png

提示:默认情况下,直接 IO 自动调节是启动的,我们可以通过设置事件 10949 的级别为 1 来关闭
IO 自动调节。

实际上,在 11gR2 当中,串行查询的直接读取的特性也发生了变化:一旦打开该特性,无论对象的高水位线下有多少个数据块,都会直接读取。

在直接 IO 自动调节机制下,是否直接读取数据块,根据表的数据块数与当前系统设置分为四个区域:
• 高水位线下的数据块数小于小表临界值(_small_table_threshold),不会直接读取数据块;
• 高水位线下的数据块数大于于小表临界值且小于 10%的缓存数据块数,如果启用了直接 IO
自动调节,仅在极少数情况下(如初次多数据块读方式访问对象)会直接读取数据块;
• 高水位线下的数据块数大于 10%的缓存数据块数并且小于“极大表”临界值(由参数
“_very_large_object_threshold”设置,其值为百分比数,默认是 500,即 5 倍于缓存数据块数),如果启用了直接 IO 自动调节,会根据输出数据大小与输入数据大小以及当前已经被缓存的数据块数等条件来决定是否直接读取数据块。我们观察到,当缓存中数据块数满足以下条件时,会由缓存读取数据块,否则直接读取:
CacheBlks >= CEIL((1-OutputRate*0.5)*DataBlkNum)
其中,OutputRate 为输出记录中字段总的大小占原始数据记录的大小的比例,等于
OSize/ISize=(SUM(ACL)+5)/ARL。ACL 为语句中选择字段的平均长度,ARL 记录平均长度,它们由对象统计数据获得。并且,OutputRate 计算结果会被存储在执行计划的全表扫描操作的上下文当中,游标在运行时刻会读取上下文数据以决定是否直接读取。DataBlkNum 为对象存储段中高水位线以下的用户数据块数。
• 高水位线下的数据块数大于“极大表”临界值,无论启用了直接 IO 自动调节,都会始终直接读取数据块;

提示:在直接 IO 自动调节机制下,Oracle 不会在子游标执行计划的全表扫描操作上下文中的标识其是否在运行时刻对其进行直接读。我们观察到,当高水位线下的数据块数小于 10%的缓存数据块数时,相应标识位的数值始终为:60000881;当高水位线下的数据块数大于 10%的缓存数据块数, 相应标识位的数值始终为:68000801。当然,我们还是通过修改该标识位(如改为 60000981)控制其是否进行直接读。

在直接 IO 自动调节机制下,第三种情况比较特殊,我们使用相应大小表来演示其过程。
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
将所有 IO 等待事件请求读取的数据块数累加,我们可以得到 154,即统计数据中“physical
reads”的大小;将所有多数据块读 IO 等待事件的请求数据块数累加,得到 150,而每次多数据块读的第一个数据块是正常读取(不进入预提取区)的数据块,因此再减去多数据块读 IO 次数
(18),得到 132,即预提取数据块数(“physical reads cache prefetch”)。
而如果,将“_db_block_prefetch_quota”设置为 0,则全表扫描操作的预提取被关闭,相应的, 我们在 10046 事件跟踪信息中就找不到多数据块读的 IO 请求,并且运行统计数据“physical reads cache prefetch”也为 0。
o 预热预提取(Prefetch Warmup)
对于全表扫描等操作,可以预见对象的所有数据块都需要被处理,因而预提取的数据块最终都 会被处理。而对于由索引范围扫描得到 ROWID 后再访问表的情况,则只能预测到后面的数据块
“可能”被访问到。因此,将预提取技术应用到这样的操作时,预提取的数据块的处理过程稍有不
同:与提取的数据块被直接读入缓存————该阶段也成为“预热”(Warmup);用户进程根据 需要从链表中找到数据块并进行处理。它与全表扫描的预提取的不同之处在于:
• 预热数据块直接进入缓存而非预提取区,即预提取区的大小不影响此类预提取操作;
• 预热数据块并不一定会被用户进程处理,甚至直至被置换出缓存————因而预热数据块 可能会造成缓存的浪费。

是否启用预热预提取,可以通过隐含参数“_db_cache_pre_warm”控制,默认为 TRUE。然而, 由于预热预提取可能会造成缓存的浪费,因此它还受到了缓存资源使用情况的限制。例如,我们可 以看到,如果存在预热数据块被处理之前就被置换出了缓存(通过视图 V$SYSSTAT 中的“prefetch warmup blocks aged out before use”数据可以观察到),那么预热预提取的出现几率非常低。

我们先看一个简单的由索引范围扫描访问表的示例:
image.png
image.png
通过统计可以得知,有 7(21-11-3)块预热数据块最终被用户进程处理了,还有 14 块数据块
未被处理。而这些数据块如果一直没有进程处理它们,则可能会由于缓存资源紧张而被置换出缓存, 相应的系统统计数据“prefetch warmup blocks aged out before use”就会增加。

提示:如果这些预热数据块是被 FLUSH 命令人为清除出缓存的,则被统计子系统统计数据“prefetch warmup blocks flushed out before use”当中,这一数值对系统是否继续采用预热预提取没太大影响。

从多数据块 IO 请求的等待事件的请求对象数据编号(obj#)可以看到,不仅表对象(74177) 的数据块以多数据块读的方式读入缓存,索引对象(92600)的数据块也会以多数据块读的方式被 预提取。这一行为可以通过参数“_disable_index_block_prefetching”来控制,其默认值为 FALSE, 即运行预提取索引数据块。

提示:未被处理的预热数据块是不会被统计到逻辑读当中的。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论