11g中直接路径读取对于延迟块清除的影响

本文探讨了在 Oracle 11g 版本中,串行全表扫描从传统的 DBFILESCATTEREDREAD 方式转变为 DIRECTPATHREAD 的优势与潜在影响。详细分析了此转变如何减少栓争用,优化物理 I/O 大小,同时解释了直接路径读取带来的段级检查点和重复延迟块清除操作的缺点。通过测试案例,展示直接路径读取对于延迟块清除的影响及与普通全表扫描方式的对比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Oracle 11g版本中串行的全表扫描可能使用直接路径读取(direct path read)的方式取代之前版本中一直使用的DB FILE SCATTERED READ, 显然direct path read具备更多的优势:

1. 减少了对栓的使用,避免可能的栓争用

2.物理IO的大小不再取决于buffer_cache中所存在的块;试想某个8个块的extent中1,3,5,7号块在高速缓存中,而2,4,6,8块没有被缓存,传统的方式在读取该extent时将会是对2,4,6,8块进行4次db file sequential read,这是一种十分可怕的状况,其效率往往要比单次读取这个区间的所有8个块还要低得多,虽然Oracle为了避免这种情况总是尽可能的不缓存大表的块(读入后总是放在队列最冷的一端);而direct path read则可以完全避免这类问题,尽可能地单次读入更多的物理块。

当然直接路径读取也会引入一些缺点:

1.在直接路径读取某段前需要对该对象进行一次段级的检查点(A segment checkpoint).

2.可能导致重复的延迟块清除操作(我们假设你了解delayed block cleanout是什么).


http://www.oracledatabase12g.com/archives/direct-read-impact-on-delayed-block-read.html


metalink 文档[ID 793845.1] 对该新版本中的变化进行了描述:

    Applies to:

    Oracle Server – Enterprise Edition – Version: 11.1.0.6 to 11.1.0.7
    This problem can occur on any platform.
    Symptoms

    After migrating an 11g database from a standalone to a 4-node RAC,  a noticeable
    increase of 'direct path read' waits were observed at times.

    Here are the Cache sizes and Top 5 events.

    waits

    Cache Sizes                       Begin        End
    ~~~~~~~~~~~                  ---------- ----------
                   Buffer Cache:     3,232M     3,616M  Std Block Size:         8K
               Shared Pool Size:     6,736M     6,400M      Log Buffer:     8,824K
    Top 5 Timed Foreground Events
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                                               Avg
                                                              wait   % DB
    Event                                 Waits     Time(s)   (ms)   time Wait Class
    ------------------------------ ------------ ----------- ------ ------ ----------
    DB CPU                                           13,916          42.1
    direct path read                  1,637,344      13,359      8   40.4 User I/O
    db file sequential read              47,132       1,111     24    3.4 User I/O
    DFS lock handle                     301,278       1,028      3    3.1 Other
    db file parallel read                14,724         554     38    1.7 User I/O

    Changes

    Migrated from a standalone database to a 4-node RAC.
    Moved from Unix file system storage to ASM.

    Using Automatic Shared Memory Management (ASMM).
    The setting of db_cache_size in spfile/pfile is low compared to normal workload requirements.
    Cause

    There have been changes in 11g in the heuristics to choose between direct path reads or reads through buffer cache for serial table scans.
    In 10g, serial table scans for “large” tables used to go through cache (by default) which is not the case anymore.  In 11g, this decision to read via direct path or through cache is based on the size of the table, buffer cache size and various other stats.
    Direct path reads are faster than scattered reads and have less impact on other processes because they avoid latches.
    Solution

    When using Automatic Shared Memory Management (ASMM) and with buffer cache low limit set at a low end compared to the normal workload requirements and usually after startup, 11g might choose to do serial direct path read scans for large tables that do not fit in the SGA. When ASMM increases the buffer cache due to increased demand, 11g might not again do serial direct path read scans for these same large tables.  If you like to avoid this from happening, you should note the buffer cache and share pool requirements for a normal workload and set the low limits of buffer cache and shared pool in spfile/pfile close to these normal workload values.
    db_cache_size
    shared_pool_size

下面我们对直接路径读取对于延迟块清除造成的影响进行测试:

SQL> create table tv as select rownum rn,rpad('A',600,'Z') rp from dual
2       connect by level <=300000;

表已创建。

新建一个会话a:

SQL> set linesize 200 pagesize 1400;
SQL> select count(*) from tv;

COUNT(*)
----------
300000

SQL> select vm.sid, vs.name, vm.value
2    from v$mystat vm, v$sysstat vs
3   where vm.statistic# = vs.statistic#
4     and vs.name in ('cleanouts only - consistent read gets',
5                     'session logical reads',
6                     'physical reads',
7                     'physical reads direct');

SID NAME                                                                  VALUE
---------- ---------------------------------------------------------------- ----------
25 session logical reads                                          27281
25 physical reads                                                 27273
25 physical reads direct                                          27273         
25 cleanouts only - consistent read gets                            0

-- 显然查询采用了直接路径读取方式

SQL> update tv set rn=rn+1;                        -- 尝试批量更新

SQL> alter system flush buffer_cache;             
-- 刷新高速缓存,造成延迟块清除的情景,并提交

系统已更改。

SQL> commit;

提交完成。

新建一个会话b:

SQL> set linesize 200 pagesize 1400;
SQL> select count(*) from tv;

COUNT(*)
----------
300000

SQL> select vm.sid, vs.name, vm.value
2    from v$mystat vm, v$sysstat vs
3   where vm.statistic# = vs.statistic#
4     and vs.name in ('cleanouts only - consistent read gets',
5                     'session logical reads',
6                     'physical reads',
7                     'physical reads direct','redo size');

SID NAME                                                                  VALUE
---------- ---------------------------------------------------------------- ----------
25 session logical reads                                                 54554
25 physical reads                                                        27273
25 physical reads direct                                                 27273
25 redo size                                                                 0
25 cleanouts only - consistent read gets                           27273      
--查询采用direct path read时产生了延迟块清除操作,但不产生redo

SQL> select count(*) from tv;

COUNT(*)
----------
300000

SQL> select vm.sid, vs.name, vm.value
2    from v$mystat vm, v$sysstat vs
3   where vm.statistic# = vs.statistic#
4     and vs.name in ('cleanouts only - consistent read gets',
5                     'session logical reads',
6                     'physical reads',
7                     'physical reads direct','redo size');

SID NAME                                                                  VALUE
---------- ---------------------------------------------------------------- ----------
25 session logical reads                                                109104
25 physical reads                                                        54546
25 physical reads direct                                                 54546
25 redo size                                                                 0
25 cleanouts only - consistent read gets                                 54546

再次查询仍采用直接路径读取,产生了相同数目的延迟块清除操作,并没有产生redo;可见direct path read的清除操作仅是针对从磁盘上读取到PGA内存中的镜像,而不对实际的块做任何修改,因而也没有任何redo;

下面我们使用普通串行全表扫描方式,设置event 10949可以避免采用直接路径读取方式.关于该事件可以参见这里.

SQL> ALTER SESSION SET EVENTS '10949 TRACE NAME CONTEXT FOREVER';

会话已更改。

SQL> select count(*) from tv;

COUNT(*)
----------
300000

SQL> select vm.sid, vs.name, vm.value
2    from v$mystat vm, v$sysstat vs
3   where vm.statistic# = vs.statistic#
4     and vs.name in ('cleanouts only - consistent read gets',
5                     'session logical reads',
6                     'physical reads',
7                     'physical reads direct','redo size');

SID NAME                                                                  VALUE
---------- ---------------------------------------------------------------- ----------
25 session logical reads                                                163662
25 physical reads                                                        81819
25 physical reads direct                                                 54546
25 redo size                                                           1966560
25 cleanouts only - consistent read gets                                 81819

SQL> select count(*) from tv;

COUNT(*)
----------
300000

SQL> select vm.sid, vs.name, vm.value
2    from v$mystat vm, v$sysstat vs
3   where vm.statistic# = vs.statistic#
4     and vs.name in ('cleanouts only - consistent read gets',
5                     'session logical reads',
6                     'physical reads',
7                     'physical reads direct','redo size');

SID NAME                                                                  VALUE
---------- ---------------------------------------------------------------- ----------
25 session logical reads                                                190947
25 physical reads                                                        95673
25 physical reads direct                                                 54546
25 redo size                                                           1966560
25 cleanouts only - consistent read gets                                 81819

第一次采用普通全表扫描方式时产生了与direct path read时相同量的延迟块清除操作,并因此产生了大量的redo,这种模式回归到了最经典的延迟块清除情景中;之后的一次读取则不再需要清除块和产生重做了,我们在读取一个“干净”的表段。

从以上测试我们可以了解到,11g中使用更为广泛的direct path read方式对有需要延迟块清除操作的段所可能产生的影响,因为实际没有一个“修改块”的操作,所以虽然延迟块清除操作在该种模式下每次都必须产生,却实际没有产生脏块,因而也就不会有“写块”的必要,故而也没有redo的产生。所产生的负载可能更多的体现在cpu time的使用上。


reference : 直接路径读取对于延迟块清除的影响


<think>首先,用户的问题是关于在Code Composer Studio (CCS) 中避免或解决延迟堵塞函数导致的问题。延迟堵塞函数通常指的是阻塞式延时函数,如 `__delay_cycles()` 或自定义循环延时,这些函数会占用CPU时间,导致其他任务无法执行,从而影响实时性。 我需要根据系统级指令来构建回答: - 所有行内数学表达式必须使用 `$...$` 格式。 - 独立公式使用 `$$...$$` 格式并单独成段。 - 确保LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如 `[^1]`。 回答结构要清晰,帮助用户逐步解决问题,保证真实可靠。参考提供的引用: - [^1] 提到设置断点进行调试。 - [^2] 是关于编译器错误和解决方法。 - [^3] 是导入官方例程的步骤。 - [^4] 列出了快捷键,如调试功能。 这些引用可能不是直接相关的,但可以间接参考。例如,调试功能 ([^4]) 可以用来分析延迟问题。 核心问题:避免或解决延迟堵塞函数。 - 常见方法: - 使用定时器中断代替阻塞延时。 - 采用RTOS(实时操作系统)进行任务调度。 - 优化代码,使用非阻塞延时。 - 利用CCS的调试工具分析性能。 步骤式回答: 1. **理解问题**:解释什么是延迟堵塞函数及其影响。 2. **解决方法**:提供具体技术。 - 使用定时器中断。 - 实现非阻塞延时。 - 使用RTOS。 - 调试和优化。 3. **在CCS中实现**:结合CCS特性,如调试器、性能分析工具。 4. **引用相关部分**:自然地添加引用标识。 生成相关问题: - 基于主题,如优化延时、使用中断、调试技巧等。 确保回答真实可靠:基于嵌入式系统开发知识。</think>### 在Code Composer Studio中避免或解决延迟堵塞函数导致的问题 在嵌入式开发中,延迟堵塞函数(如 `__delay_cycles()` 或基于循环的自定义延时)会阻塞CPU执行,导致程序无法响应其他事件(如中断或实时任务),从而降低系统效率和实时性。在Code Composer Studio (CCS) 中,可以通过以下步骤避免或解决这类问题。我将逐步解释方法,确保代码高效且可靠。参考了CCS的调试和优化功能[^1][^4]。 #### 步骤1: 理解延迟堵塞函数的影响 延迟堵塞函数通过占用CPU周期来实现延时,例如: - 使用 `__delay_cycles()` 函数(基于CPU时钟循环)。 - 自定义循环延时,如 `for(int i=0; i<10000; i++) {}`。 这些函数会阻塞主线程,导致: - 实时任务(如传感器读取或通信)被延迟。 - 中断响应时间变长,影响系统稳定性。 - CPU利用率过高,可能引发功耗问题。 在CCS中,您可以使用调试工具(如断点和性能分析器)来量化影响。例如,设置断点检查执行时间[^1][^4]。 #### 步骤2: 避免使用阻塞延时,改用非阻塞方法 避免延迟堵塞的关键是采用事件驱动或中断机制,让CPU在等待期间执行其他任务。以下是具体实现方法: - **使用定时器中断代替阻塞延时**: - 配置硬件定时器(如TI器件的Timer模块),在中断服务例程(ISR)中处理延时逻辑。 - 示例代码(基于C2000系列): ```c #include "F28x_Project.h" // 包含TI器件头文件 volatile Uint32 delay_count = 0; // 全局变量,用于计数 // 定时器ISR函数 __interrupt void timerISR(void) { delay_count--; // 每次中断减少计数 PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // 清除中断标志 } // 非阻塞延时函数 void non_blocking_delay(Uint32 cycles) { delay_count = cycles; // 设置延时周期 while(delay_count > 0); // 等待计数归零,但不阻塞CPU } void main(void) { InitSysCtrl(); // 初始化系统控制 DINT; // 禁用全局中断 InitPieCtrl(); // 初始化PIE控制 IER = 0x0000; // 禁用CPU中断 IFR = 0x0000; InitPieVectTable(); // 初始化中断向量表 // 配置定时器(例如Timer0) ConfigCpuTimer(&CpuTimer0, 200, 100000); // 200MHz CPU, 100ms周期 PieVectTable.TIMER0_INT = &timerISR; // 关联ISR PieCtrlRegs.PIEIER1.bit.INTx7 = 1; // 启用Timer0中断 IER |= M_INT1; // 启用CPU中断组1 EINT; // 启用全局中断 while(1) { non_blocking_delay(1000); // 非阻塞延时1000个周期 // 在此执行其他任务,如读取传感器 } } ``` - **优点**:CPU在等待期间可处理其他任务,提高响应性。 - **在CCS中实现**:使用Resource Explorer导入官方例程(如Timer例程),参考引用[^3]的导入步骤。 - **采用RTOS(实时操作系统)进行任务调度**: - 如果项目复杂(如多任务系统),使用TI-RTOS或FreeRTOS管理延时。 - 示例:在RTOS中,用 `Task_sleep()` 代替阻塞延时,允许其他任务运行。 ```c #include <ti/sysbios/knl/Task.h> void taskFunction(UArg arg0, UArg arg1) { while(1) { // 执行主任务 Task_sleep(1000); // 非阻塞延时1000ms // 此处不会阻塞CPU } } ``` - **在CCS中配置**:通过Project Properties启用RTOS支持,并导入相关库。参考编译器设置[^2],确保安装正确编译器。 - **优化为状态机或轮询机制**: - 将延时逻辑分解为状态机,使用全局变量跟踪状态。 - 示例: ```c typedef enum { STATE_IDLE, STATE_DELAYING, STATE_COMPLETE } DelayState; DelayState current_state = STATE_IDLE; Uint32 start_time = 0; void handle_delay(Uint32 current_time, Uint32 delay_cycles) { switch(current_state) { case STATE_IDLE: start_time = current_time; current_state = STATE_DELAYING; break; case STATE_DELAYING: if (current_time - start_time >= delay_cycles) { current_state = STATE_COMPLETE; } break; case STATE_COMPLETE: // 延时结束,执行后续操作 break; } } ``` - **优点**:简单高效,适合资源受限系统。 #### 步骤3: 在CCS中调试和优化 CCS提供工具来分析并解决延时问题: - **使用断点和性能分析器**: - 在代码中设置断点(行号左侧点击蓝色圆圈),检查延时函数的执行时间[^1][^4]。 - 启用Profile工具(Tools > Profile),测量CPU周期占用率。如果延时函数占用过高,表明需要优化。 - **编译器优化**: - 在Project Properties > Build > Compiler Options中,启用优化级别(如-O2),减少不必要的循环。 - 确保编译器版本兼容,避免导入错误[^2]。 - **功耗和实时性分析**: - 使用EnergyTrace工具(针对MSP430等低功耗器件)监视延时对功耗的影响。 - 结合逻辑分析器(如XDS110调试器),验证中断响应时间。 #### 注意事项 - **硬件依赖**:上述方法需根据TI器件(如C2000、MSP430)调整。参考Resource Explorer导入器件特定例程[^3]。 - **性能权衡**:非阻塞方法可能增加代码复杂度,但显著提升实时性。测试时使用CCS的调试快捷键(如F5单步进入、F6单步跳过)[^4]。 - **最佳实践**:在开发初期避免使用阻塞延时;优先使用定时器和RTOS。如果必须用阻塞延时,限制其使用在非关键路径。 通过以上方法,您可以在CCS中有效减少延迟堵塞函数的问题,提升系统响应性。如遇到编译器或导入问题,参考引用[^2][^3]的解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值