Oracle Database10g性能调整与优化


1 Oracle Database10g 性能调整与优化 目录 第 1 章 Oracle Database 10g 新功能 介绍(针对 DBA 和开发人员)1 1.1 安装改进 1.2 SYSAUX 表空间 1.3 自动存储管理 1.4 集群就绪服务(CRS) 1.5 服务器生成的警报 1.6 自动工作量仓库(AWR) 1.7 自动数据库诊断监控程序(ADDM) 1.8 SQL 调整顾问 1.9 自动共享内存管理(ASMM) 1.10 闪回恢复区 1.11 回收站 1.12 恢复管理器的改动 1.13 透明数据加密(10gR2) 1.14 LogMiner 的改动...... 1.15 新的 DBMS_STATS 选项...... 1.16 跟踪增强 1.17 DBMS_SCHEDULER 1.18 默认的(永久)表空间...... 1.19 临时表空间组 1.20 重命名表空间 1.21 大文件表空间 1.22 收缩段 1.23 数据泵(Data Pump) .. 1.24 跨平台的可移植表空间 1.25 写入外部表 1.26 自动撤消保留调整 1.27 包括新信息的 V$SESSION 1.28 OEM 的改动 1.29 网格控制 1.30 10g 版本中的新后台进程 1.31 版本比较表 1.32 新特性回顾 1.33 参考文档 第 2 章 基本的索引原理(针对 DBA 和 初级开发人员) 2.1 基本的索引概念 2.2 组合索引 2.3 限制索引 2.3.1 使用不等于运算符(<>、!=) 2.3.2 使用 IS NULL 或 IS NOT NULL ... 2.3.3 使用函数 2.3.4 比较不匹配的数据类型 2.4 选择性 2.5 集群因子(Clustering Factor) .. 2.6 二元高度(binary height) .. 2.7 使用直方图 2.8 快速全局扫描 2.9 跳跃式扫描 2.10 索引的类型 2.10.1 B 树索引 2.10.2 位图索引 2.10.3 HASH 索引 2.10.4 索引组织表 2.10.5 反转键索引 2.10.6 基于函数的索引 2.10.7 分区索引 2.10.8 位图连接索引 2.11 快速重建索引 2.12 技巧回顾 2.13 参考文档 第 3 章 磁盘实现方法和 ASM (针对 DBA) 3.1 成为规范的磁盘阵列 3.1.1 使用磁盘阵列改进性能和 可用性..... 3.1.2 所需的磁盘数量 3.1.3 可用的 RAID 级别..... 3.1.4 更新的 RAID 5 3.2 安装和维护传统文件系统 3.3 在硬件磁盘之间分布 关键数据文件 62 3.3.1 分开存储数据和索引文件 3.3.2 避免 I/O 磁盘争用..... 3.3.3 通过移动数据文件来 均衡文件 I/O 3.4 本地托管的表空间 3.4.1 创建本地托管的表空间 3.4.2 把字典托管的表空间迁移 到本地托管的表空间 3.4.3 Oracle 大文件表空间 3.4.4 Oracle 托管文件 2 3.5 ASM 简介 3.5.1 I/O 角色之间的通信 3.5.2 ASM 实例 3.5.3 ASM init.ora 参数 3.5.4 ASM 的安装 3.5.5 ASM 参数和 SGA 调整..... 3.5.6 ASM 和权限 3.5.7 ASM 磁盘 3.5.8 ASM 和多路径 3.5.9 ASM 磁盘组 3.5.10 ASM 磁盘组和数据库 3.5.11 ASM 冗余和故障组 3.5.12 Oracle Database 10g Release 2 中新的空间相关列 3.5.13 集群同步服务 3.5.14 数据库实例和 ASM 3.5.15 使用 ASM 进行数据库 合并和集群化 3.5.16 支持 ASM 的数据库进程 3.5.17 大文件和 ASM 3.5.18 支持 ASM 的数据库 init.ora 参数..... 3.5.19 ASM 和数据库部署 最佳实践..... 3.5.20 ASM 存储管理和分配 3.5.21 ASM 重新平衡和重新分布 3.6 使用分区来避免磁盘争用 3.6.1 获得关于分区的更多信息 3.6.2 其他类型的分区 3.6.3 其他分区选项 3.7 使用索引分区 3.8 导出分区 3.9 消除碎片 3.9.1 使用正确的盘区大小 3.9.2 创建一个新表空间并把 数据移到其中 3.9.3 导出和重新导入表 3.9.4 正确设定比例以避免 链化现象..... 3.9.5 自动段空间管理 3.9.6 重建数据库 3.10 增加日志文件尺寸和 LOG_ CHECKPOINT_INTERVAL 以提高速度...... 3.10.1 确定重做日志文件的大小 是否存在问题 3.10.2 确定日志文件的大小和 检查点的时间间隔 3.11 闪回恢复 3.12 增加恢复的可能性:在每次 批处理后提交 101 3.13 使用回滚段 3.13.1 避免回滚段之间的争用 3.13.2 监控回滚段的等待 和争用..... 3.13.3 增加回滚段 3.13.4 把大的事务隔离到它们 自己的回滚段上 3.13.5 更简便的方法:UNDO 表空间 3.13.6 监控 UNDO 空间..... 3.14 结束有问题的会话 3.15 不要在 SYSTEM 或 SYSAUX 表空间中执行排序 3.16 在不同磁盘和控制器上 存放多个控制文件 3.17 对写操作频繁的数据使用 裸设备来提高 I/O 3.17.1 使用裸设备的好处 3.17.2 使用裸设备的缺点 3.18 磁盘 I/O 的其他注意事项 和提示...... 3.19 设计阶段需要注意的问题 3.20 技巧回顾 3.21 参考文档 第 4 章 用初始参数调整数据库 (针对 DBA) 4.1 标识重要的初始参数 4.2 不用重启就修改初始参数 4.3 用 Enterprise Manager 查看初始参数 114 4.4 调整 DB_CACHE_SIZE 来提高性能...... 4.4.1 在调整 DB_CACHE_SIZE 时使用 V$DB_CACHE _ADVICE 4.4.2 保证数据缓存命中率 3 超过 95%..... 4.4.3 监控 V$SQLAREA 视图以 查找较慢的查询 4.5 设定 DB_BLOCK_SIZE 来反映 数据读取量的大小 4.5.1 调整 SHARED_POOL_SIZE 以优化性能..... 4.5.2 使用 Oracle 的多个缓冲池 4.5.3 调整 PGA_AGGREGATE_ TARGET 以优化对 内存的应用..... 4.5.4 修改 SGA 大小以避免 分页和交换..... 4.5.5 了解基于成本的优化 4.5.6 创建足够的调度程序 4.5.7 25 个重要的初始化参数 4.5.8 查找未归档的初始参数 4.5.9 了解典型的服务器 4.5.10 典型的服务器模式 4.5.11 调整 Oracle Application 数据库..... 4.6 技巧回顾...... 4.7 参考文档 第 5 章 企业管理器和网格控制 (针对 DBA 和开发人员)144 5.1 企业管理器(EM)基础...... 5.2 从 All Targets 和其他 分组开始...... 5.3 Policies (Violations)选项卡...... 5.4 监控数据库...... 5.4.1 Database Administration 选项卡..... 5.4.2 Database Administration 选项卡:Tablespace 5.4.3 Database Administration 选项卡:实例级别 5.4.4 Database Administration 选项卡:All Initialization Parameters 5.4.5 Database Administration 选项卡:Manage Optimizer Statistics 5.4.6 Database Administration 选项卡(实例级别):..... 5.4.7 Database Maintenance 选项卡..... 5.4.8 Database Topology 选项卡 5.4.9 Database Performance 选项卡..... 5.5 监控主机 5.6 监控应用服务器 5.7 监控 Web 应用程序...... 5.8 Deployments 选项卡 (补丁选项) 5.9 Jobs 选项卡 5.10 Reports 选项卡 5.11 自动存储管理的性能 5.12 小结 5.13 技巧回顾 5.14 参考文档 第 6 章 使用 EXPLAIN 和 STORED OUTLINES(针对 DBA 和开发人员) 6.1 Oracle 的 SQL TRACE 实用程序 6.1.1 对简单查询使用 SQL TRACE 的简单步骤..... 6.1.2 TRACE 输出部分 6.1.3 更复杂的 TKPROF 输出..... 6.1.4 深入探讨 TKPROF 输出..... 6.1.5 使用 DBMS_MONITOR (10g 的新特性) 6.1.6 使用 TRCSESS 将多个跟踪 文件保存到一个文件中 (10g 的新特性) 6.1.7 单独使用 EXPLAINPLAN 6.1.8 EXPLAIN PLAN—— 从上 至下读取和从下至上读取 6.1.9 阅读 EXPLAIN PLAN 6.1.10 使用 DBMS_XPLAN 6.1.11 另一种 EXPLAIN PLAN 输出方法:构建树结构 6.1.12 另一个使用树的例子 6.1.13 在开发产品中 利用 TRACE/EXPLAIN 发现有问题的查询 6.1.14 PLAN_TABLE 表中的 重要列..... 4 6.1.15 Oracle 支持的一些有用的 程序包..... 6.1.16 适用于未记录入档的 TRACE 操作的初始参数 6.1.17 使用存储纲要 6.1.18 使用 Plan Stability (存储纲要) 6.2 技巧回顾 第 7 章 基本的提示语法 (针对 DBA 和开发人员) 7.1 最常用的提示 7.1.1 谨慎使用提示 7.1.2 首先修正设计方案 7.2 可用的提示和分组 7.2.1 改变执行路径 7.2.2 使用访问方法提示 7.2.3 使用查询转换提示 7.2.4 使用连接操作提示 7.2.5 使用并行执行 7.2.6 其他提示 7.3 指定提示 7.4 指定多个提示 7.5 使用别名时,提示别名而 不是表名...... 7.6 使用提示 7.6.1 使用 FIRST_ROWS 提示..... 7.6.2 使用 ALL_ROWS 提示..... 7.6.3 使用 FULL 提示..... 7.6.4 使用 INDEX 提示..... 7.6.5 使用 NO_INDEX 提示..... 7.6.6 使用 INDEX_JOIN 提示..... 7.6.7 使用 INDEX_COMBINE 提示..... 7.6.8 使用 INDEX_ASC 提示..... 7.6.9 使用 INDEX_DESC 提示..... 7.6.10 使用 INDEX_FFS 提示..... 7.6.11 使用 ORDERED 提示..... 7.6.12 使用 LEADING 提示..... 7.6.13 使用 NO_EXPAND 提示..... 7.6.14 使用 DRIVING_SITE 提示..... 7.6.15 使用 USE_MERGE 提示..... 7.6.16 使用 USE_NL 提示..... 7.6.17 使用 USE_HASH 提示..... 7.6.18 使用 PUSH_SUBQ 提示..... 7.6.19 使用 PARALLEL 提示..... 7.6.20 使用 NO_PARALLEL 提示..... 7.6.21 使用 APPEND 提示..... 7.6.22 使用 NOAPPEND 提示..... 7.6.23 使用 CACHE 提示..... 7.6.24 使用 NOCACHE 提示..... 7.6.25 使用 CLUSTER 提示..... 7.6.26 使用 HASH 提示..... 7.6.27 使用 CURSOR_ SHARING_ EXACT 提示..... 7.6.28 使用 QB_NAME 提示..... 7.7 其他一些提示和注意事项 7.8 使用提示可能遇到的问题 7.9 提示一览表 7.10 技巧回顾 7.11 参考文档 第 8 章 调整查询(针对开发人员和 初级 DBA) 8.1 调整哪些查询?查询 V$SQLAREA 8.1.1 在 V$SQLAREA 视图中选出 最占用资源的查询 8.1.2 在 V$SQL 视图中选出最占用 资源的查询..... 8.2 选出最占用资源的会话和 查询的新 10g 视图...... 8.2.1 从 V$SESSMETRIC 视图中 选出当前最占用 资源的会话..... 8.2.2 查看可用的 AWR 快照..... 8.2.3 从 DBA_HIST_SQLSTAT 视图中选出最占用 资源的查询..... 8.2.4 从 dba_hist_sqlstat 视图中 选择查询文本 8.2.5 从 DBA_HIST_SQL_PLAN 视图中选出查询执行计划 8.3 确定何时使用索引 8.4 遗忘索引 8.5 创建索引 8.6 查看表上的索引 8.7 修正差的索引 8.8 在删除索引时保持谨慎 8.9 对 SELECT 和 WHERE 中的 列使用索引...... 5 8.10 使用快速全局扫描 8.11 使查询魔术般加速 8.12 在内存中缓存表 8.13 使用多个索引(利用最佳 选择性) 8.14 使用索引合并 8.15 处理受限制的索引 8.16 使用基于函数的索引 8.17 了解“古怪”的 OR 8.18 使用 EXISTS 函数...... 8.19 表就是视图 8.20 SQL 和“大一统”理论 8.21 Oracle Database 10g 中的 调整修改...... 8.22 10g 自动 SQL 调整...... 8.22.1 保证调整用户能访问 API 8.22.2 创建调整任务 8.22.3 查看顾问日志中的任务 8.22.4 执行 SQL 调整任务..... 8.22.5 查看调整任务的状态 8.22.6 显示 SQL 调整顾问报告 8.22.7 检查报告输出 8.23 技巧回顾 8.24 参考文档 第 9 章 表的连接和其他高级调整 技术(针对 DBA 和 开发人员) 9.1 连接方法.... 9.1.1 嵌套循环连接 9.1.2 排序合并连接 9.1.3 集群连接 9.1.4 散列连接 9.1.5 索引连接 9.2 表连接的初始化参数 排序合并与散列连接参数 9.3 双表连接:等尺寸表 (基于成本) 9.4 双表索引连接:等尺寸表 (基于成本) 9.5 强制执行特殊的连接方法 9.6 在多表连接中除去连接记录 (侯选行) 9.7 在大小不同的表间进行 双表连接 9.8 三表连接(基于成本) 9.9 位图连接索引 321 9.10 第三方性能调整工具 9.11 调整分布式查询 330 9.12 一切就绪...... 9.13 各种调整技术摘要 9.13.1 外部表 9.13.2 数据快照太旧:开发人员 的编程难题 9.13.3 设置事件以转储每次等待 9.13.4 使用 EXISTS 操作符有效 缩短时间 9.14 在块级别进行调整 9.15 使用简单的数学方法 调整性能...... 9.15.1 传统的数学分析 9.15.2 方法论的七个步骤 9.15.3 模式分析 9.15.4 数学方法总结 9.16 连接调整:关系模型与对象 关系模型的性能 360 9.16.1 使用的模型 9.16.2 结果 9.16.3 总结 9.17 技巧回顾...... 9.18 参考文档...... 第 10 章 使用 PL/SQL 提高性能 (针对 DBA 和开发人员) 10.1 使用 DBMS_APPLICATION _INFO 进行实时监控 376 10.2 在 RAC 环境中为实时监控 使用自定义包代替 DBMS_ APPLICATION_INFO 10.3 在数据库的表中记录 计时信息...... 10.4 减少 PL/SQL 程序的单元 迭代和迭代的时间 10.5 使用 ROWID 进行迭代处理 395 10.6 将数据类型、IF 语句的 排列和 PLS_INTEGER 10.6.1 确保比较运算中的数据 类型相同..... 10.6.2 根据条件出现的频率 6 来排序 IF 条件..... 10.6.3 使用 PLS_INTEGER PL/SQL 数据类型进行 整数运算..... 10.7 减少对 SYSDATE 的调用...... 10.8 减少 MOD 函数的使用...... 10.9 共享池和固定 PL/SQL 对象...... 10.9.1 将 PL/SQL 对象语句 固定(缓存)到内存中..... 10.9.2 固定所有的包 10.10 标识需要固定的 PL/SQL 对象...... 10.11 使用和修改 DBMS_ SHARED _POOL.SIZES 10.12 从 DBA_OBJECT_SIZE 中 获取详细的对象信息 10.13 发现无效的对象 10.14 发现已禁用的触发器 10.15 将 PL/SQL 表用于快速 参考表查询...... 10.16 查找和调整所使用 对象的 SQL 10.17 在处理 DATE 数据类型时 使用时间信息 416 10.18 调整和测试 PL/SQL 10.19 了解 PL/SQL 对象定位 的含义...... 10.20 使用回滚段打开大型游标 10.21 使用数据库的临时表来 提高性能...... 10.22 集成用户跟踪机制以定位 执行位置...... 10.23 限制动态 SQL 的使用...... 10.24 使用管道表函数来建立 复杂结果集...... 10.25 别管调试命令 10.26 为初学者提供的例子 10.26.1 创建 PL/SQL 代码..... 10.26.2 创建过程 10.26.3 执行 PL/SQL 过程..... 10.26.4 创建函数 10.26.5 在 SQL 中执行 GET_ CUST_NAME 函数..... 10.26.6 创建数据包 10.26.7 在数据库触发器中使用 PL/SQL 10.27 技巧回顾 10.28 参考文档 第 11 章 调整 RAC 和使用并行特性 11.1 实时应用集群(RAC) 11.1.1 并行数据库..... 11.1.2 Oracle RAC 的体系结构 11.1.3 Oracle RAC 系统的内部 工作方式..... 11.1.4 SCN 处理 11.1.5 RAC 性能调整概述 11.1.6 RAC 等待事件和互连 统计数据..... 11.1.7 集群互连调整——硬件 等级..... 11.1.8 使用企业管理器网格 控制调整 RAC 11.2 并行操作的基本概念 11.3 并行 DML 和 DDL 语句 和操作...... 11.4 Oracle 9i 的并行 DML 语句 和操作...... 11.5 并行处理和分区 11.6 操作内部和操作之间的 并行处理...... 11.7 使用并行操作生成表和 索引的示例...... 11.8 并行 DML 语句和示例...... 11.8.1 并行 DML 的约束条件..... 11.8.2 并行 DML 语句示例..... 11.9 通过 V$视图监控并行操作 11.9.1 V$PQ_TQSTAT ... 11.9.2 V$PQ_SYSSTAT 11.9.3 V$PQ_SESSTAT 11.10 在并行操作时使用 EXPLAIN PLAN 和 AUTOTRACE 11.11 调整并行执行和 Oracle 9i 初始化参数...... 11.12 并行加载 11.13 性能比较和监控并行操作 11.14 优化 RAC 中的并行操作 490 7 11.14.1 并行操作的目标 11.14.2 RAC 并行使用模型 11.14.3 初始化参数 11.14.4 查看并行统计数据的 V$视图..... 11.14.5 并行配置和相关 基线测试..... 11.14.6 并行查询测试示例 11.14.7 Create Table As 11.14.8 索引构建 11.14.9 性能考虑因素和小结 11.15 使用并行处理时的其他 注意事项...... 11.16 技巧回顾 11.17 参考文档 第 12 章 V$视图(针对开发人员 和 DBA) 12.1 V$视图的创建和访问 12.1.1 获得所有 V$视图的数量 和列表..... 12.1.2 查找用于创建 V$视图的 X$表..... 12.1.3 查找组成 DBA_视图的 底层对象..... 12.1.4 使用有帮助的 V$脚本..... 12.1.5 内存分配摘要(V$SGA) 12.1.6 内存分配的细节 (V$SGASTAT) 12.1.7 在 V$PARAMETER 中发现 init.ora 的设置..... 12.1.8 测定数据的命中率 (V$SYSSTAT) 12.1.9 测定数据字典的命中率 (V$ROWCACHE) 12.1.10 测定共享 SQL 和 PL/SQL 的命中率 (V$LIBRARYCACHE) 12.1.11 确定需要固定的 PL/SQL 对象..... 12.1.12 通过 V$SQLAREA 查找 有问题的查询 12.1.13 检查用户的当前操作及其 使用的资源..... 12.1.14 查找用户正在访问 的对象..... 12.1.15 使用索引 12.1.16 确定锁定问题 12.1.17 关闭有问题的会话 12.1.18 查找使用多会话的用户 12.1.19 查找磁盘 I/O 问题..... 12.1.20 查找回滚段的内容 12.1.21 检查空闲列表是否充足 12.1.22 检查角色和权限设置 12.1.23 等待事件 V$视图..... 12.1.24 一些主要的 V$视图种类..... 12.2 技巧回顾 12.3 参考文档 第 13 章 X$表(针对高级 DBA) 13.1 X$表介绍 13.1.1 有关 X$表的误解..... 13.1.2 授权查看 X$表..... 13.2 创建 V$视图和 X$表...... 13.3 获得所有 X$表的列表...... 13.4 获得所有的 X$索引列表...... 13.5 对 X$表和索引使用提示 13.6 共享池 13.7 监控共享池的查询 13.7.1 ORA-04031 错误 13.7.2 空间分配过大而引起 的争用..... 13.7.3 共享池碎片化 13.7.4 共享池和/或 Java 池中 空闲内存过低 13.7.5 库缓存命中率 13.7.6 大量的硬分析 13.7.7 闩锁等待和/或休眠..... 13.7.8 其他调整选项 13.8 重做 13.9 初始化参数 13.10 缓存/数据块...... 13.10.1 缓存状态 13.10.2 占用数据块缓存的段 13.10.3 热数据块/闩锁争用和 等待事件..... 13.11 实例/数据库...... 13.12 高效使用 X$表及其策略...... 8 13.13 相关的 Oracle 内部机制...... 13.13.1 跟踪 13.13.2 DBMS_TRACE 包 13.13.3 事件 13.13.4 转储 13.13.5 ORADEBUG .... 13.13.6 trcsess 工具 13.14 阅读跟踪文件 13.14.1 等待信息和响应时间 13.14.2 递归调用 13.14.3 模块信息 13.14.4 提交 13.14.5 Unmap .. 13.14.6 绑定变量..... 13.14.7 错误 13.15 X$表的分组 13.16 X$表及相关的非 V$固定视图...... 13.17 常见的 X$表连接...... 13.17.1 Oracle 10gR1 中新增的 X$表..... 13.17.2 Oracle 10gR2 中新增的 X$表..... 13.18 X$表的命名约定 13.19 技巧回顾 13.20 参考文档 第 14 章 使用 STATSPACK 和 AWR 报表调整等待和闩锁 14.1 10gR2 (10.2) STATSPACK 的 新特性...... 14.2 10gR1 (10.1) STATSPACK 的 新特性...... 14.3 安装 STATSPACK 14.3.1 PERFSTAT 账户的 安全管理..... 14.3.2 安装之后 14.3.3 搜集统计数据 14.3.4 运行统计数据报表 14.4 自动工作量仓库(AWR)和 AWR 报表...... 14.4.1 手动管理 AWR 14.4.2 AWR 自动快照 14.4.3 AWR 快照报表 14.4.4 在 Oracle 企业管理器网格 控制中运行 AWR 报表..... 14.5 解释 STATSPACK 的 输出结果...... 14.5.1 报表头信息 14.5.2 负载简档 14.5.3 实例的效率 14.5.4 首要等待事件 14.5.5 Oracle Bugs .. 14.5.6 Oracle 影子进程的 生命周期..... 14.5.7 RAC 等待事件和互连 统计数据..... 14.5.8 首要的 SQL 语句..... 14.5.9 实例活动统计数据 14.5.10 表空间和文件 I/O 的 统计数据..... 14.5.11 段统计数据 14.5.12 其他的内存统计数据 14.5.13 撤消统计数据 14.5.14 闩锁统计数据 14.5.15 在块级别调整和 查看(高级) 14.5.16 数据字典和库缓存的 统计数据..... 14.5.17 SGA 内存统计数据 14.5.18 非默认的初始化参数 14.6 AWR 报表和 STATSPACK 输出结果中需 680 14.6.1 管理 STATSPACK 数据..... 14.6.2 升级 STATSPACK 14.6.3 删除 STATSPACK 14.7 新 ADDM 报表的快速注释 681 14.8 10gR2 脚本 14.9 技巧回顾 14.10 参考文档 第 15 章 执行快速系统检查 (针对 DBA) 15.1 总体性能指数(TPI) 15.2 教育性能指数(EPI) 15.3 系统性能指数(SPI) 15.4 内存性能指数(MPI) 15.4.1 缓冲区命中率 15.4.2 数据字典缓存命中率 15.4.3 库缓存命中率 9 15.4.4 PGA 内存排序命中率 15.4.5 空闲的数据缓冲区的比例 15.4.6 最浪费内存的前 10 个 语句占所有语句的比例 15.4.7 调整前 25 个最浪费 内存的语句..... 15.4.8 固定/缓存对象..... 15.5 磁盘性能指数(DPI) 15.5.1 调整滥用磁盘读操作的 25 个主要语句..... 15.5.2 最浪费磁盘读操作的前 10 个语句占所有语句 的比例..... 15.5.3 表/索引的分离..... 15.5.4 关键任务表管理 15.5.5 分离关键的 Oracle 文件..... 15.5.6 自动撤消管理 15.5.7 临时段的平衡 15.6 总体性能指数(TPI) 15.7 系统综合检查的示例 15.7.1 评级系统 15.7.2 系统检查评级分类 的示例..... 15.7.3 需要立刻解决的问题项 15.7.4 其他需要解决的问题项 15.8 系统信息列表 15.8.1 与内存有关的值 15.8.2 与磁盘有关的值 15.8.3 与 CPU 有关的值..... 15.8.4 与备份和恢复有关 的信息..... 15.8.5 命名约定和/或标准以及 安全信息..... 15.8.6 DBA 知识评级 15.9 TPI 和系统检查需要考虑 的其他项...... 15.10 技巧回顾 15.11 参考文档 第 16 章 使用 UNIX 工具监控系统 (针对 DBA) 16.1 Unix/Linux 工具...... 16.2 使用 sar 命令监控 CPU 的使用...... 16.2.1 sar -u (检查 CPU 沉重程度) 16.2.2 sar –d 命令(查找 I/O 问题) 16.2.3 sar –b 命令(检查缓存) 16.2.4 sar –q 命令(检查运行 队列和交换队列的长度) 16.3 使用 sar 命令和 vmstat 命令 监控分页/交换...... 16.3.1 使用 sar 命令的-p 选项 报告分页活动 16.3.2 使用 sar 命令的-w 选项 报告交换和切换活动 16.3.3 使用 sar 命令的-r 选项 报告空闲内存和空闲交换 16.3.4 使用 sar 命令的-g 选项 报告分页活动 16.3.5 使用 sar 命令的 –wpgr 选项..... 16.4 使用 top 命令发现系统中 最影响性能的用户 16.5 使用 uptime 命令监控 CPU 的负载...... 16.6 使用 mpstat 命令确定 CPU 瓶颈 16.7 将 ps 命令与已选出的 V$视图相结合...... 16.8 使用 iostat 命令确定磁盘 I/O 瓶颈...... 16.8.1 为磁盘驱动器 sd15、sd16、 sd17 和 sd18 使用 iostat 的-d 选项..... 16.8.2 使用 iostat 的-D 选项..... 16.8.3 使用 iostat 的-x 选项..... 16.8.4 将 iostat 的-x 选项与 shell 脚本中的逻辑相结合 16.9 使用 ipcs 命令确定共享 内存的使用情况 736 16.10 使用 vmstat 命令监控系统 的负载...... 16.11 监控磁盘空闲空间 16.12 监控网络性能 16.12.1 使用 spray 命令监控..... 16.12.2 使用 nfsstat –c 监控 10 网络性能..... 16.12.3 使用 netstat 监控 网络性能..... 16.12.4 显示可调整参数的 当前值..... 16.12.5 修改配置信息文件 16.12.6 影响性能的其他因素 16.13 技巧回顾 16.14 参考文档 附录 A 主要的初始化参数 (针对 DBA) A.1 不再支持的初始化参数 A.2 不建议使用的初始化参数 A.3 25 个最重要的初始化参数 A.4 必须记住的最重要的 10 个初始化参数 754 A.5 最重要的 13 个未入档的 初始化参数...... A.6 已入档的初始化参数列表 (V$PARAMETER) A.7 未入档的初始化参数列表 (x$ksppi/x$ksppcv) A.8 Oracle 应用程序 11i 建议 (注释: 216205.1) A.9 不写书的 10 个重要原因...... A.10 技巧回顾 A.11 参考文档 附录 B V$视图(针对 DBA 和 开发人员) B.1 V$视图、GV$视图和 X$表的创建...... B.2 Oracle 10g (10.2.0.1) GV$和 V$视图列表...... B.3 Oracle 10g (10.2.0.1) V$视图...... B.4 用于创建 V$视图的 X$表的 Oracle 10g 脚本...... 附录 C X$表(针对 DBA) C.1 按名称排序的 Oracle10g X$表 C.2 Oracle 10 g X$索引...... C.3 交叉引用 X$表的 Oracle 10gV$视图 C.4 GV$视图没有引用的 Oracle10g X$表 前言前言前言前言 “我们的选择所带来的持久影响力不是我们获得了什么,而是我们将成为什么。” —— Michael Josephson 0.10.10.10.1 64 64 64 64 位和位和位和位和 OracleOracleOracleOracle 打破了时空连续性打破了时空连续性打破了时空连续性打破了时空连续性 许多人认为 20 世纪 90 年代中期 Internet 的出现是我们一生中再也不会遇到的事件。他们的这个观 点完全错误!在本书的上一版中,我提到 TB 级的数据库将在不久后普及,几乎没有人相信我的观点。但是 现在确实发生了这种情况——TB 级的数据库现在非常普遍。随着 Oracle 10g 的发布,PB(1000TB)级的数 据库将开始被人们所使用;而在 Oracle 11g 中,甚至可能出现 EB(1 000 000TB)级的数据库(在 Oracle 12g 中,几乎可以确定会出现这种数据库)。 很少有人了解到 Internet 时代的出现直接归因于 32 位计算机和 32 位计算提供的理论可能性所产生 的连锁反应。Oracle 于 1983 年引入了 32 位计算,然而直到 20 世纪 90 年代中期硬件功能可满足 32 位计 算的需求时,许多公司才开始完全地利用 32 位计算(经过了大约 12 年)。信息时代是往前跨越的另一大步, 其前进步伐无限大于 Internet 时代。我们现在正在使用功能非常全面(除了不能挑战重力)的 Oracle 10g, 同时正在步入将来的 64 位计算世界。64 位计算于 1995 年在 Oracle 7 中引入,通过又一个 12 年的发展, 64 位计算于 2007 年开始普及。下一个时代(Generation 64)和 64 位计算将前所未有地改变世界,这种改 变就从 2007 年开始。考虑 IDC/EMC 和位于伯克利的加利福利亚大学的如下研究: 11 ● 2K:一张打印页面 ● 1M:1000K ● 1M:一部小说 ● 5M:莎士比亚的所有著作 ● 10M:一分钟高保真度的音频 ● 100M:一米高架子上的书籍 ● 1G:1000M ● 1G:一辆装满书的轻型货车(或您的 SGA(2007 年)) ● 100G:一层楼的学术杂志(或者新的笔记本硬盘(2007 年)) ● 1T:1000G ● 2T:一个学术研究图书馆(或者您的世界 500 强数据库(2007 年)) ● 2T:YouTube 上一天内生成的信息量 ● 2P:所有学术研究图书馆(或者您的 Grid SGA(2010 年)) ● 10T:美国国会图书馆中 530 000 000 英里的书架 ● 730T:YouTube 上一年内生成的信息量 ● 1P:1000T ● 20P:1995 年所有硬盘驱动器的容量(或者您的数据库(2010 年)) ● 700P:收入少于 2 亿美元的 700 000 家公司的所有数据 ● 1E:1000P ● 1E:世界 1000 强公司的所有数据库(平均每个公司 1P) ● 1E:接下来世界 9000 强公司的数据库(平均每个公司大约 100T) ● 2E:世界上最大型的 10 000 个公司(使用的全部数据库) ● 2E:1999 年生成的所有信息(可全部容纳在一个 Oracle 10g 数据库中(2007 年)) ● 3E:世界上最大型的 1 0000 000 个公司(使用的全部数据库) ● 5E:2003 年生成的所有新信息(估计值,大多数图像没有存储在数据库中) ● 6E:2006 年生成的所有电子邮件 ● 8E:一个 Oracle 10g 数据库的容量(当前) ● 12E~16E:1999 年之前生成的所有信息(64 位驻留内存) ● 16E:64 位可寻址的内存(当前) ● 161E:2006 年生成的新信息(估计值,大多数图像/视频没有存储在数据库中) ● 246E:2007 年生产的所有硬盘驱动器的容量(估计值) ● 255E:2007 年生成的新信息(估计值,大多数图像/视频没有存储在数据库中) ● 1000E:2010 年生成的新信息(估计为 1Z) ● 1Z:1000E(1Z 可估计为世界所有沙滩上的沙粒数量——125 个 Oracle 数据库) ● 1Y:1000Z(1Y 可估计为 1000 个人体中的原子数量) ● 100TY:100 万亿的 YB,128 位可寻址内存(将来) 如果考虑可寻址内存的理论限制,则可以设想 64 位计算的能力。在无符号 16 位计算中,可以直接寻 址 64K(2 16 字节)的内存。随着这种技术的巨大进步,于 1985 年出现 Windows 1.0(1987 年出现弱化图形的 Windows 2.0 版本),1990 年出现 Windows 3.0,不久之后出现客户端-服务器版本。我记得在我将 SGA 增 加到超过 1M 后遇到内存问题时,Oracle 技术支持告诉我说“您不会需要大于 1M 的 SGA”。在无符号的 32 位计算中,我们可以直接寻址 4G(2 32 字节)的内存(+/-符号将占用 2G 内存)。对于标准的 Oracle 数据库, 直接寻址 4G 内存可以极大地增加系统全局区域(SGA)。在 SGA 中存储最常用的数据,这些数据保留在内存 中以供快速访问。我们现在一般会看到 GB 级的 SGA 和 TB 级的数据库。32 位 Linux 和 Windows 的 DBA 会寻 12 求关于如何将其 SGA 增加到超过 2G 或 4G 的支持。当发展到 64 位计算时,相比于 Internet 时代的发展速 度,信息时代的发展速度以指数级增长。通过使用 64 位,可寻址内存的理论限制(2 64 )变成 16E(EB)或 18 446 744 073 709 551 616(2 64 字节)字节的直接可寻址内存。查看如下数字以了解可寻址内存的巨大飞跃。 内 存 直接可寻址 间接/扩展可寻址 4 位 16 (640) 8 位 256 (65 536) 16位 65 536 (1 048 576) 32位 4 294 967 296 64位 18 446 744 073 709 551 616 Oracle 10g 数据库的最大理论限制为每个数据库 8EB(1EB 等于 100 万的 3 次方字节或 100 万 TB)。就 在几年前(1999 年),全世界只有大约 12EB 到 16EB 的信息。世界范围内的所有数据库(结合在一起)只是这 个数字的一小部分。16E 的直接可寻址内存是非常庞大的数量(Larry 现在可以在一个 Oracle 数据库中运行 世界范围内的每个数据库——世界范围的联机——这些数据库全部驻留在内存中)。设想将世界范围内的每 一部分信息都存储在一个数据库和内存中,不久您就可能会听到:“我将整个 Internet 都装在我的膝上型 电脑中了。”Internet 只以 PB 为单位进行估计(如果删除重复的数据,就可以将多个 Internet 放入您的 Oracle 数据库中)。据估计表层网有 167T(可以在一个 Oracle 数据库中容纳 50 000 个表层网),而深层网 为 92P(可以在一个 Oracle 数据库中容纳不到 100 个深层网)。如果包括所有的电子邮件(440P)和即时消息 (270T),总量也只有 500P(仍然可以在一个 Oracle 数据库中容纳 16 个总的网络)。当硬件在物理上达到 64 位的理论可能性时,情况将发生极大的变化。从 32 位移动到 64 位如同一夜之间从 1971 年来到了 2000 年。 应该注意的是,从 2000 年进行研究以来,向上的调整已经估计到所有的信息都稍大于初始的估计值(然而, 因为存在大量重复的数据,所以在确切的数字方面仍然有争议)。 在这些最近的估计中,更令人惊讶的是我们每年生成超过 8EB(1999 年是 2E,2003 年是 5E,2005 年 是 8E)的原始信息。如果 5E 就等同于存储在 500 000 个图书馆中的信息以及包含在美国国会图书馆中的信 息,超过 8E 是令人难以置信的数量。随着视频和音频的数量不断增加,存储需求的增长正极大地超出预期 情况,但是我们并不真正地需要额外的一百万个美国国会图书馆,只需要在我们已有的图书馆中添加更多 的数字存储设备。很快我们就可能需要多达 5 个或 6 个 Oracle 数据库存储所有信息,而在 8 年前则只需要 一个 Oracle 数据库。 据我估计,如果您将多张纸(每张纸包含 2K 的文本)依次堆积起来,则获得 16E 的信息需要堆叠 4.8B 英里高的纸张。也就是说,您可以从地球开始堆积这些文档,堆积的高度甚至可以到达冥王星!在一个Oracle 数据库中,可以容纳: ● 多个 Internet(没有重复的数据) ● 20 亿部电影(每部 4G 容量) ● 80 亿辆满载文档的轻型货车 ● 一个充满文档的珠穆朗玛峰 ● 世界范围内的所有新刷信息(估计为 5E) ● 说出的所有单词(估计为 5E,然而对该估计值存在争议) 0.20.20.20.2 Oracle Oracle Oracle Oracle 在不断创新在不断创新在不断创新在不断创新 如果在 oracle.com 上没有看到“Oracle Firsts”,我在这儿列出了这些创新,从而可以将一些 Oracle 13 历史方面的注意事项添加到 Oracle 引人注目的未来前景。Oracle 将是信息时代的领先者,这不仅是因为 它们创建了各种“弯路”,而且在产生出乎意料的“弯路”时愿意自动进行调整。与 Microsoft 不同的是, Oracle 全面地包括 Java,许多开发人员都可以使用 Oracle。与 IBM 不同的是,Oracle 引入每种硬件解决 方案,推动实现可伸缩性并且给其用户提供多种选择。Oracle 将会有支持 Linux 的版本,同时大力推动信 息的网络化。Oracle 继续支持 SAP 和 Microsoft,同时迎合开放源代码的团体。在我的记忆中,当我和 Brad Brown、Joe Trezzo 于 1987 年在 Oracle 就职时,我们共同构建了第一个 Oracle 客户端-服务器应用程序。 我们惊讶于为什么其他公司到很久以后才效仿 Oracle 的这种创新。现在,我只需要查看“Oracle Firsts”, 就能知道其他公司将会很快效仿这些创新。但是,我希望成为带头人。现在查看如下的 Oracle 创新并且准 备好迎接更加快速发展的未来: ● 第一个商业 RDBMS ● 第一个 32 位数据库 ● 第一个具有读一致性的数据库 ● 第一个客户端-服务器数据库 ● 第一个 SMP 数据库 ● 第一个 64 位 RDBMS ● 第一个 Web 数据库 ● 第一个具有本地 Java 支持的数据库 ● 第一个移植到 Linux 的商业 RDBMS ● 第一个使用 XML 的数据库 ● 第一个具有真正应用集群(RAC)的数据库 ● 第一个 True Grid 数据库 ● 免费的 Oracle 数据库(Oracle Express 版本) ● 牢不可破的 Linux 支持 0.30.30.30.3 历史加速发展历史加速发展历史加速发展历史加速发展 历史正在加速发展,当前已经有了 64 位的 Oracle,不久就会有 PB 级的 SGA。您已经有了实现可用性 所需的每种调整选项和每个 24×7×52 选项,并且已经有了超出想象的维护和可恢复性选项,可以根据需 要在记录级别执行安全性和审核,并且可以出于安全目的加密表数据和数据库备份。现在所有这些都已经 成为现实!您将构建优秀的数据库!接下来是学习和实现 Oracle 10g,并且进一步发展您的工作。您的工 作将取决于您的效率是否更高。使用网格控制(Grid Control,第 5 章对此进行了介绍)这样的工具可以简 化您的工作,从而可以关注于更为重要的业务问题。您也需要为内部的业务用户将大量的数据聚集到可用 的数据集中。 在此之后的下一个发展阶段将是 128 位(2 128 字节的可寻址内存)计算或 3 后面带 38 个 0(2019 年,256 位将在 Oracle 中产生 1 后面带 77 个 0;2031 年,512 位将在 Oracle 中产生超过 1 个 googol 或 1 后面带 154 个 0)。googol(不是 Google)用于表示一个非常大的、不能达到的数字(小于无穷大,但也是过于大而 无法达到的数字)。googol 表示 10 的 100 次幂或 1 后面带 100 个 0。考虑 70 个表连接具有超过 1 个 googol 的组合(70!——70 的阶乘是 1×2×3×…×70)。已知世界中有只有少于 1 个 googol 的原子(10 的 79 次方 到 10 的 81 次方),而黑洞大约经过 1 个 googol 年后才会消失。如果没有监视特别查询用户连接表的情况, 则这些用户可能会获得 1 个 googol 的查询结果。考虑下面这段来自于早期(60 年代)史努比连环漫画的对 话,这是单相思的 Lucy 和 Schroeder 在钢琴前的一段经典对话。这段对话也显示了相比于大多数领导者, Charles Shultz 的思想超出了当前所处的时代。 14 Lucy:Schroeder,你认为我们在某一天结婚的机会有多大? Schroeder:我认为大约是一个“googol”分之一。 Lucy:“googol”代表多少? Schroeder:10 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000。 Carl Sagan 在其著作 Cosmos 中说到:“准确来说,googol 是与数字 1 相距无穷远。”我们已经开始了 解到 Carl Sagan 的错误所在,在信息技术中,我们与 googol 的距离越来越近。在从 1 到无穷大的实数直 线上进行比较时,即使是 googolplex(10 的 googol 次方)也几乎接近于从数字 1 到数字 2 之间的距离。随 着 128 位计算的出现,googol 看起来并非遥不可及。通过使用这种计算能力,我们或许很容易通过重新排 列分子(而不是手工劳动)来制造事物。 技术令人惊奇——首先我们讨论新技术将在不久出现,然后就可以实现这个新技术。2001 年,街机游 戏“忍者神龟”有“3 个 Googolhertz”处理程序。我们还没有到达未来,但是未来的发展速度将超出大多 数人的认知。因此,您应该准备好下一个飞跃:googolplex 是 1 后面带 1 个 googol 数量的 0。Googolplex 中包含的数量远超出世界范围内的原子数量。我认为我们仍然还没有到使用这个数量级的时候,但是记住 Doc Brown 在“Back to the Future III ”中以及描述 RSEN 时所说的话:“她是 googolplex 中的一个人。” 0.40.40.40.4 Oracle 30 Oracle 30 Oracle 30 Oracle 30 周年庆典周年庆典周年庆典周年庆典 作为具有数十亿美元资产并且确切地推动每个大型企业进步的公司,Oracle 于 2007 年迎来了它的 30 周年庆典。回顾 Oracle 公司的历史,令人惊讶的是许多不同的团队带领该公司走向成功。Larry Ellison 推动 Oracle 公司的发展,而 Bob Miner 则推动了 Oracle 产品的发展。美国是自由的、有韧劲的、充满机 遇的国家,Larry Ellison 就是在自由社会中一个人可以达到什么成就的最好示例。Larry 的姓来源于 Ellis 岛,他创业成功的故事表明,只要人们崇尚自由,任何事情都可能发生。自由女神像上刻有如下文字:“Give me your tired, your poor, your huddled masses yearning to breathe free, the wretched refuse of your teeming shore. Send these, the homeless, tempest-tost to me, I lift my lamp beside the golden door!”这个金色之门最终将 Larry 带到了金门大桥,并且在硅谷中建立了 Oracle 公司。 0.50.50.50.5 Bruce Scott Bruce Scott Bruce Scott Bruce Scott 眼中的眼中的眼中的眼中的 OracleOracleOracleOracle 早期发展阶段早期发展阶段早期发展阶段早期发展阶段 在合伙创建 Oracle 之前,Bob Miner 是 Larry Ellison 的经理,两个人在 Ampex 中就职,并且共同开 发代号为“Oracle”的 CIA 项目。Larry 选择 Bob 作为他的经理,这是因为相比于以前的经理 Ed Oates(Oracle 的另一个创办者),Larry 更喜欢和 Bob 共事。Ed Oates 某天碰巧走过 Bob Miner 办公室的 房门时,Larry Ellison 提到了他妻子的名字,而他妻子正好是Ed Oates 在大学时的实验室助手。Bruce Scott 在 Oracle 公司创办之初就被雇佣,他是 scott/tiger 中的“Scott”(tiger 是指 Bruce 女儿的小猫)。 当 Larry 继续在 Precision Instruments(精密仪器)公司就职时,他发现 Precision Instruments 需 要执行一项价值 40 万美元的咨询项目。对于 3~4 个工程师来说,这是很大数目的一笔金钱,因为工程师当 时的薪水大约只有这个数目的 1/10。Larry 获得了这笔生意。当新公司创立时,Larry 并不是其中的成员, 他仍然在 Precision Instruments 就职。新公司称为 Software Development Labs(软件开发实验室,简称 15 为 SDL)。在 1977 年 8 月份创办该公司时,只有 3 个雇员。Bob Miner 是总裁,而我和 Ed Oates 是软件工 程师。在公司成立的第一年中,我们完成了这个两年期项目 90%的工作,因此接下来的一年我们致力于发 展 Oracle。Ed Oates 在这一年中完成该项目剩余 10%的工作,而我和 Bob Miner 开始编写 Oracle 数据库。 完成 Precision Instruments 的这个项目之后,我们的银行户口中增加了 20 万美元。我们的目标是 成为一家产品公司,而不仅仅是成为一家咨询公司。Bob 希望为 PDP 11 构建 ISAM 产品,他觉得市场上有 访问层方面的需求。Larry 则对此完全不感兴趣。Larry 一直关注 IBM 的工作,并且于 1970 年偶然读到了 Edgar Codd 教授关于关系数据库的论文,这篇论文中描述了 SQL 语言,该语言在当时称为 SEQUEL/2。Larry 向我们展示了这篇论文,并且询问我们是否可以构建这种数据库。我们认为很容易就能构建这样的数据库, 因此开始着手进行工作。我当时 24 岁,在 1982 年离开 Oracle(在该公司工作了大约 5 年半)时,我们已经 完成了该数据库的第三版。该版本中大约有一半代码是我编写的,而其他代码则由 Bob 编写。我相信当前 数据库中的许多分析器代码仍然是我编写的代码。Bruce Scott 说过,他最美好的一天是 Oracle 第一次的 用户讨论会,这是我们于 1982 年发起的一次顾客讨论会,这个讨论会吸引了 25 到 50 个人参加。这是 Oracle 公司受人关注的开端。 在 1998 年的 Nicole Ricci 见面会中,Larry Ellison 说道:“实际上,当我创办 Oracle 时,从来没 有制定过发展为大型公司的目标。在当时,我们的最高目标只是让公司里的 50 个员工过上更好的生活。公 司发展 5 年后,事情变得非常明显:我们具有无限的发展空间,唯一的限制在于我们自己。” 0.60.60.60.6 Oracle RDBMS Oracle RDBMS Oracle RDBMS Oracle RDBMS 历史历史历史历史 下面是 Oracle RDBMS 发展过程的时间线。 ● 1970—— Edgar Codd 博士公布了关系数据模型理论。 ● 1977—— Larry Ellison、Bob Miner、Ed Oates 和 Bruce Scott 用 2000 美元启动资金建立了 Software Development Laboratories(SDL)。Larry 和 Bob 来自于 Ampex,他们当时正负责一个名为 Oracle 的 CIA 项目。而 Bob 和 Bruce 开始编写数据库。 ● 1978—— CIA 是他们的第一个客户,而该产品仍没有作为商品发布。SDL 改名为 Relational Software Inc(RSI)。 16 ● 1979—— RSI 发布了第一个商业版本。数据库的第 2 个版本(没有发布版本 1,是担心人们不愿 购买软件的第 1 版)用汇编语言编写。该软件的第一个商业版本卖给了 Writht-Patterson Air Force Base。 这是市场上的第一个商业 RDBMS。 ● 1981—— 创建出第一个工具 Interactive Application Facility(IAF):它是 Oracle 后来的 SQL*Forms 工具的前身。 ● 1982—— RSI 改 名 为 Oracle Systems Corporation(OSC),接着又简化为 Oracle Corporation。 ● 1983—— 发布了用 C 语言编写(这使得它可移植)的第 3 个版本。Bob Miner 编写了一半代码, 该版本也支持基于 V2 的 Assembler。Bruce Scott 编写了另一半代码。这是首个 32 位的 RDBMS。 ● 1984—— 发布版本 4。发布了第一批工具(IAG-genform、IAG-runform、RPT)。出现首个带有读 一致性的数据库。Oracle 转向 PC 领域。 ● 1985—— 发布版本 5 和 5.1。在 VMS/VAX 上首次出现并行服务器数据库。 ● 1986—— Oracle 在 3 月 12 号上市(在 Sun 上市 8 天后,Microsoft 上市之前)。股价开盘价 15 美元,收盘价达到 20.75 美元。在这一年还推出了 Oracle Client/Server;出现首个客户机/服务器数据 库。发布 Oracle 5.1 版本。 ● 1987—— Oracle 成为最大的 DBMS 公司。成立了 Oracle Applications 组。引入了第一个 SMP(对 称多处理结构)数据库。 ● 1987—— Rich Niemiec、Brad Brown 和 Joe Trezzo 加入 Oracle,实现了第一个运行 Oracle 的 客户机/服务器应用程序产品(为 NEC 公司在 286 计算机上运行 16 位并行客户机/服务器程序)。 ● 1988—— 发布 Oracle V6。首次实现行级锁定。首次启用热数据库备份。Oracle 公司从 Belmont 移到加利福尼亚的 Redwood Shores。引入了 PL/SQL。 ● 1992—— 发布 Oracle V7。 ● 1993—— 引入 Oracle GUI 客户机/服务器开发工具。Oracle Applications 从字符模式移向客 户机/服务器。 ● 1994—— Oracle 数据库技术的领军人物 Bob Miner 因患癌症去世。 ● 1995—— 首次推出 64 位数据库。 ● 1996—— 发布 Oracle7.3。 ● 1997—— 引入 Oracle8 以及 Oracle 应用程序服务器。引入了 Web 应用程序。Oracle 是第一个 Web 数据库。数据仓库中引入了 Oracle BI 工具(如 Discover)。这些工具支持 Java。 ● 1998—— 主要的 RDBMS(Oracle8)首次支持 Linux。装载了 Application11。Oracle 是第一个支 持 Java 的数据库。 ● 1999—— 发布 Oracle 8i。将Java/XML 集成到开发工具中。Oracle 是第一个支持 XML 的数据库。 ● 2000—— 在其成为第一个带有中间层缓存的数据库时,发布了 Oracle 9i 应用程序服务器。发 布了 E-Business Suite,带有 Oracle Mobile 的无线数据库,Oracle 9i Application Sever Wireless 和 Internet File System(iFS)。 ● 2001—— 发布 Oracle 9i(9.1)。Oracle 是第一个带有 RAC(Real Application Cluster) 的数据库。 ● 2002—— 发布 Oracle9i Release 2(9.2)。 ● 2003—— 根据 Winter Group 的调查,在数据库规模排前 10 位的数据库中,法国电信局的 Oracle 是规模最大的数据库,容量达到 29T。 ● 2003—— Oracle 10g 发布:重点面向网格、加密备份、自动调整和 ASM。 ● 2005—— 根据 Winter Group 的调查,Amazon 的 Oracle RAC 进入数据库规模前 10 强,容量达 到 25T。 17 ● 2005—— Oracle 收购 PeopleSoft(包括 JD Edwards)、Oblix(身份管理解决方案)、Retek(零售 软件,花费 6.3 亿美元)、TimesTen (内存数据库)和 Innobase (InnoDB Open Source)公司。 ● 2006—— Oracle 收购 Siebel(花费 58 亿美元)、Sleepycat Software(开放源代码)和 Stellant(内容管理)公司。通过推动开放源代码,Oracle 提供了对 Red Hat Linux 的长久支持。 ● 2006—— Oracle 10g Release 2 发布(本书就基于该版本)。 ● 2007—— Oracle 花费 33 亿美元收购 Hyperion 公司。 ● 2007—— Oracle 11g 发布(基于以前的版本预测的时间)。 ● 2011—— Oracle 12g 发布(基于以前的版本预测的时间)。 当我向 Bruce Scott 问及 Oracle 成功的关键所在时,他说到:“我认为是 Larry,我们当时打败了其 他许多数据库,如 Ingres。是 Larry 的个人魅力、预见性和决策使得这一切很顺利。这正是 Larry 所设想 的。我可以举例说明他的思考过程:我们被指定使用了一些空间,但需要将终端连到隔壁的计算机房。我 们实际上没有地方可以连线。Larry 拿来了锤子,在墙中间凿了个洞!这正是他所想的:打一个洞—— 不 惜一切代价。这正是 Larry,在正确的时间作出了正确的决策。”我总是告诉人们,Larry Ellison 是 Oracle 公司的灵魂人物,而 Bob Miner 是 Oracle 产品的灵魂人物。Bob Miner 的进取精神通过 Derry Kabcenell、 Roger Bamford、Andy Mendelsohn 和其他许多人得以延续。Oracle 多年来的不同团队正是他们得以成功的 秘密所在!生日快乐,Oracle! 0.70.70.70.7 新添内容新添内容新添内容新添内容 本书主要目的是帮助初学者和 Oracle 专业人员理解并更好地优化 Oracle 系统。本书还包括了许多专 题,但其目的主要是帮助受挫的专业人员找到可帮助其提高系统性能的简单提示。本书有一个简单的目标: 提供可用于各种情况的大量技巧,使系统更快速。对于读过本书前一版的读者来说,新版的各个章节有如 下一些新变化。 ● 第 1 章:完全重写了 Oracle 10gR2 基本的功能。 ● 第 2 章:添加了统计集合和 Oracle 10gR2 改动的介绍。 ● 第 3 章:添加了 ASM,并且扩展了本章的篇幅,因为 I/O 已经越来越重要。 ● 第 4 章:添加了 SGA_TARGET,并且更新了 10gR2 中的初始参数。 ● 第 5 章:添加了所有新的屏幕快照,并且针对 Enterprise Manager Grid Control 进行了重新编 写。 ● 第 6 章:更新了 Explain,并且添加了介绍 DBMS_MONITOR 和 TRCESESS 的部分。 ● 第 7 章:补充介绍了新添加的提示,并更新了本书上一版本中的部分内容。 ● 第 8 章:在 Oracle 10gR2 中更新和重新测试的内容;添加了 SQL 和 Grand Unified Theory。 ● 第 9 章:在 Oracle 10gR2 中更新和重新测试的内容;添加了块调整以及关系模型和对象关系模 型的性能对比。 ● 第 10 章:再次对 PL/SQL 的调整进行了扩展;添加了 10gR2 中的调试。 ● 第 11 章:增加了对 RAC 的介绍;更新了 Parallel Query Operations。 ● 第 12 章:介绍更多的 V$视图查询。 ● 第 13 章:扩展了 X$视图查询、跟踪部分以及 X$命名约定。 ● 第 14 章:更新了 10gR2 中 STATSPACK、AWR Report 和 ITL Block Tuning 的内容。 ● 第 15 章:针对 10gR2 和较大型系统进行更新。 ● 第 16 章:更新以包括更多命令。 ● 附录 A:针对 10gR2 的更新信息,包括更新的查询和最重要的 25 个新查询。 ● 附录 B:针对 10gR2 的更新信息,包括更新的查询。 18 ● 附录 C:针对 10gR2 的更新信息,包括更新的查询。 0.80.80.80.8 缅怀缅怀缅怀缅怀 最后,我希望记住过去这么多年 Oracle 领域中去世的朋友。Stan Yellott(2006 年 11 月 30 日去世) 明确地区分了 RMOUG、IOUG 和普通的 Oracle 领域。Stan 致力于教育我们所有人,并且作为一个榜样,告 诉我们在相聚一堂时如何与其他人相处。在我的记忆中,他具有令人难以置信的热情和无私精神。我从来 没有听过 Stan 对任何人说过不体面的话语—— 始终如一!我不知道有多少人能够做到这一点,Stan 总是 快乐地生活并尊敬其他人。当然,Stan 毫无疑问是杰出的人士! 对 Oracle 用户组来说,Marcia Pollard(2003 年去世)同样也很重要,她致力于研究 ODTUG,即使我 们没有按时完成报告书,她也会保持优雅的风度。Marcia 是出色的人士!我们要记住充满热情的、聪明的 Lex de Haan(2006 年 2 月 1 日去世),他是 Oracle 方面的专家和 Oracle 的优秀讲师,并且是世界级的性 能调整和优化专家。我们要记住 Mark Beaton(2006 年 8 月去世),他是一位充满热情的 Oracle 解决方案推 销员和优秀的英式足球运动员。我们也要记住 Ray Mansfiled(2006 年 11 月去世),他是一位才华横溢的 Warehouse Builder 顾问(脸上总是带着幸福的微笑)。最后,我们还要记住杰出的 Elaine DeMeo(2007 年 2 月 11 日去世),他是优秀的 MOUG 和 IOUG 技 术支持人员。当某一天我们完成自己的工作时,上帝会带我们 回家,我们将在那儿和这些朋友再次见面,并且“在铺满金子的街道上和天使一起奔跑”。我期待那一 天 的到来,但是在此之前,我们将继续完善自己的工作,确保将我们的杰出成果互相传达!通过始终寻求改 进诚实、知识、勇气、忠诚、自律、热情、无私、机敏、 尊敬、谦卑和主动等方面的品质,我们确保可以 坚强地面对将来任何残酷的挑战。当然,永远不要忘记信念、希望、爱……这些品质中最重要的就是爱。 不断改进自己的品质,并且心中总是想着其他人的优点!这就是我的生活目标! 0.0.0.0.9999 参考文档参考文档参考文档参考文档 "How Much Information?" http://www.sims.berkeley.edu/how-much-info/summary.html Oracle firsts are from: www.oracle.com "A zettabyte by 2010: Corporate data grows fiftyfold in three years," Lucas Mearian, March, 2007 Roy Williams, Center of Advanced Computing Research, California Institute of Technology "Back to the Future III," Universal Studios Wikipedia, en.wikipedia.com (Googol, Exabyte) "Information Inundation," Forbes.com, November 2005 "64-Bit Computing," Rich Niemiec, Oracle Magazine, 2004 "Rich Niemiec Interviews Bruce Scott," Select Magazine, 2001 "Retrospective: Still Growing after all these Years," Rich Niemiec, Oracle Magazine, 2001 "The Difference between God and Larry Ellison," Mike Wilson, November, 1998 19 History of Oracle , Donita Klement, 1999 "Wish You Were Here," Mark Harris, 2006 作者简介: Richard 被 Oracle 公司授予 Oracle 认证大师——迄今为止,全世界仅有 6 人获得此殊荣,因此他是世界 范围内公认的 Oracle 专家。他是独立 Oracle 用户组(IOUG)的前任主管以及中西部 Oracle 用户组的现任主 管。Richard 已经被 5 次命名为 IOUG 会议的最佳主讲人,并且是企业家名人纪念馆的成员。目前 Richard J. Niemiec 任 TUSC 公司的总裁,该公司旗下有 500 多个专业从事 Oracle 技术全方位服务咨询和培训的机构。 宣传语 1.本书由 Oracle 认证大师 Richard J. Niemiec 倾力打造。Richard 被 Oracle 公司授予 Oracle 认 证大师——迄今为止,全世界仅有 6 人获得此殊荣,因此他是世界范围内公认的 Oracle 专家。 2.本书提供了监控、分析和优化 Oracle Database 10g 的方法。 3.本书包含了详细的案例研究、最佳实践和丰富的 Oracle 新的调整特性的代码示例。 4.本书获得了来自全球的赞誉。独立 Oracle 用户组的总裁、沙特阿拉伯教育部、芝加哥城首席信息 官、Oracle Applications 用户组董事等人给本书都进行了高度评价。 来自全球的赞誉 "For every difficult problem, there exists a simple solution. This is what I've learned from Rich's books." —Ghazi Ben Youssef, MBA, Senior Oracle DBA Sogique, Canada “每个困难的问题都有一个对应的简单解决方案,这就是我从 Rich 的著作中学到的知识。” —Ghazi Ben Youssef,MBA,高级 Oracle DBA,加拿大 Sogique 公司 "I admire Rich for his knowledge of Oracle technology. This book from him is another masterpiece, useful for anyone who would like to excel in Oracle Performance Tuning. The book encompasses Rich's Oracle expertise andexperience and is a must-read for all Oraclelites." —Hardik Bhatt, Chief Information Officer City of Chicago 20 “我非常钦佩 Rich 在 Oracle 技术方面的丰富知识。他编写的这本书是另一本著作,希望熟练掌握 Oracle 性能调整的任何人都可以从本书中获益。本书包含了 Rich 在 Oracle 方面的专家知识和经验,并且 是所有 Oracle 专业人士必读的书籍。” —Hardik Bhatt, 芝加哥城首席信息官 "Someone said that smart people learn from their mistakes while wise people learn from others'mistakes. When involved in performance and tuning issues, I think that both smart and wise people can learn from the vast and valuable experience Rich has documented within these pages." —Maurizio Bonomi Italy “某个人曾经说过:聪明的人从他们自己的错误中学到知识,而明智的人则从其他人的错误中学到知 识。当涉及性能和调整问题时,我认为聪明的人和明智的人都可以从 Rich 在这些著作中介绍的大量而丰富 的经验中学到知识。” —Maurizio Bonomi,意大利 "If you need the best tuning DBA, call Rich Niemiec! Or get his Oracle tuning book." —Julian Dontcheff Senior Oracle Database Administrator, Nokia Corporation, Finland “如果需要最优秀的调整 DBA,那么就找 Rich Niemiec 吧!购买他的 Oracle 调整著作即可。” —Julian Dontcheff 芬兰诺基亚公司的高级 Oracle Database 管理员 "There is nothing more rewarding than to see someone from your family succeed in life. Rich, being from our Purdue Upward Bound Family, has brought much pride to us with not only with his expert Oracle technology knowledge but also with his caring attitude and dedication to help others!" 21 —Joseph Flores Director Upward Bound, Purdue University Calumet, USA “没有什么比看到家人在现实生活中获得成功更让人高兴。Rich 是我们 Purdue Upward Bound 家族的 成员,他使我们感到自豪之处不仅在于他在 Oracle 技术方面的专家知识,还在于他的同情心和致力于帮助 其他人。” —Joseph Flores,美国 Purdue University Calumet 学校的 Upward Bound 主任 "Oracle Performance Tuning is certainly a complex topic, and Rich and the experts at TUSC did a marvelous job explaining the complexities of the Oracle performance in a different approach. The tips and techniques will really help you in solving day-to-day performance problems and increasing DBA productivity. This is a valuable reference for anyone who works on Oracle Performance Tuning. We all can learn something from Rich's expertise in real-life performance tuning. I liked the'Tips Review'sections at the end of each chapter, which are like fast food for busyconsultants." —K Gopalakrishnan Author: Oracle 10g RAC Handbook and Oracle Wait Interface System Performance & Architecture Group with Oracle Consulting “Oracle 性能调整无疑是一个非常复杂的主题,Rich 和 TUSC 中的专家们完成了一项不可思议的工作, 他们通过不同的途径解释了 Oracle 性能方面的复杂性。他们给出的技巧和技术可帮助您解决每天遇到的性 能问题并提高 DBA 的生产力。对于任何从事于 Oracle 性能调整的人来说,这都是一本有价值的参考书籍。 我非常喜欢每章末尾的“技巧回顾”部分,这些内容是适合于繁忙的咨询人员的快餐。” —K Gopalakrishnan, Oracle 10g RAC Handbook and Oracle Wait Interface System Performance & Architecture Group with Oracle Consulting 一书的作者 "Rich Niemiec is a phenomenal entrepreneur with an incredible depth of knowledge regarding Oracle applications." —Professor Gerald Hills, Coleman Chair of Entrepreneurship University of Illinois at Chicago 22 “Rich Niemiec 是杰出的企业家,他对 Oracle 应用程序有着令人难以置信的深入了解。” —Gerald Hills 教授,芝加哥伊利诺斯企业家大学的主讲教授 "We have learned much from Rich." —Nguyen Hoang, Information Expert Ministry of Finance, Vietnam “我们从 Rich 处获益良多。” —Nguyen Hoang,越南财务部信息专家 "Michelangelo tells us that our problem is not aiming too high and missing but in aiming too low and hitting every time. With a master like Rich on your bookshelf, you can aim high with confidence." —Ronan Miles British Telecommunications, London “Michelangelo 告诉我们,我们的问题不是目标过高和缺少目标,而是目标过低和每次都实现目标。 通过学习 Rich 已经出版的著作,读者可以充满信心地制定较高的目标。” —Ronan Miles 英国电信,伦敦 "This book sets the benchmark for what a performance tuning book should be. Rich has done an Ace job with it. It is well written, easy to read, and full of practical tips. Novices and experienced DBAs as well as developers will find this book invaluable. It is just full of gems of information and tips covering areas such as Grid Control, ASH, AWR, ADDM, block level tuning, and mathematical performance modeling. Even veteran DBAs will find this book difficult to put down. It will be The Oracle Book to have and the reference for DBAs for years to come." —Tony Jambu Australia 23 “本书为其他的性能调整书籍的优秀程度设置了基准,Rich 在这方面做出了杰出的贡献。本书经过精 心编写,易于阅读,并且包括各种实际的技巧。初学者、有经验的 DBA 和开发人员都会发现本书的价值所 在。本书包含涉及如下领域的大量有价值的信息和技巧:网格控制、ASH、AWR、ADDM、块级调整和数学性 能建模。即使是富有经验的 DBA 也会在本书中发现有价值的内容。本书将是接下来多年内 DBA 的首选 Oracle 参考书籍。” —Tony Jambu,澳大利亚 "Rich Niemiec had the courage to make his dreams come true. Through hard work and determination he overcame obstacles and serves as a role model for all students in TRiO Pre-College Programs. His knowledge and passion go beyond computers; he seeks to inspire others to have the courage to make their dreams come true, too!" —Bobbi Jo Johnson Upward Bound Advisor, UW-Sheboygan “Rich Niemiec 有勇气将其梦想变为现实。通过刻苦的工作和强大的信念,他克服了种种困难,并且 为 TRiO Pre-College Programs 中的所有学生树立了榜样。他的知识和热情远不是计算机能够提供的;他 也鼓励其他人鼓起勇气将他们的梦想变成现实。” —Bobbi Jo Johnson,Upward Bound 顾问,UW-Sheboygan "The best Oracle Tuning book I ever read is from Rich Niemiec, and I would recommend it to anyone who needs a better understanding about performance tuning and wants to enhance their skills in ORACLE." —Shaharidan Karim Sr. DBA, Commerce Dot Com Sdn Bhd, Malaysia “我曾经阅读过的最优秀的 Oracle 调整书籍就是 Rich Niemiec 编写的书籍,我将其推荐给需要更好 地理解性能调整并希望增强其 Oracle 技能的任何人。” —Shaharidan Karim,高级 DBA,Commerce Dot Com Sdn Bhd,马来西亚 "Rich's boundless passion for technology combined with his zeal to share provides him the unique advantage to create a profound product that is rich in every way. He provides an insider's 24 view that you cannot afford to miss." —Anil Khilani Global Technical Lead, Oracle Corporation “由于具有技术方面的无穷激情以及共享知识方面的热情,Rich 在创建各个方面都包含丰富内容的渊 博作品方面具有独特的优势。他提供了必不可少的权威人士观点。” —Anil Khilani, Oracle 公司的全球技术领导 "Another'must have'technical reference for the Oracle professional. Rich's books provide invaluable insight that allow you to get the most out of Oracle and push it to the limit and beyond." —Matt Swann, Vice President of Software Development, Amazon.com “这是另一本 Oracle 专家必须拥有的技术参考手册。Rich 的著作提供了有价值的见识,通过这些见 识可以最有效地利用 Oracle 并冲破它的种种限制。” —Matt Swann,Amazon.com 软件开发副主管 "Rich Niemiec's earlier version of this book (9i edition) was a ready reckoner for every DBA. This book will be like a dictionary for DBAs facing critical problems. With Rich's invaluable industry experience, this book is just indispensable for a DBA. I have followed thoroughly all his books on tuning from 8i and found them the ultimate for Oracle Performance Tuning going into the minutest details." —Shankar Mukherjee, Oracle Consultant ComTel Technologies, Calcutta, India “Rich Niemiec 编写的本书前一版本(9i 版本)已经成为每个 DBA 都必须拥有的行业手册。本书是遇 到关键问题的 DBA 的字典。通过包含 Rich 有价值的行业经验,本书是每个 DBA 都必不可少的参考书籍。从 8i 版本开始,我就十分密切地注意 Rich 编写的所有调整书籍,并且发现这些书籍是非常详尽地介绍 Oracle 性能调整的最佳书籍。” —Shankar Mukherjee, 位于印度加尔各答的 chnologies 的 Oracle 顾问 25 第第第第 1111 章章章章 Oracle Database 10gOracle Database 10gOracle Database 10gOracle Database 10g 新功能介绍新功能介绍新功能介绍新功能介绍((((针对针对针对针对 DDDDBABABABA 和开发人员和开发人员和开发人员和开发人员)))) 1.1 安装改进 首先,我想申明一下本书最初的目的主要是帮助那些 Oracle 初级、中级专业人员理解并更好地调整 Oracle 系统。本书后面的章节中也将提到许多较专业的主题,但主要的目标也只是帮助那些受阻后苦苦寻 找能提高性能的简单技巧的专业人员。本书有个很简单的目的:提供一些在不同情况下都能使系统更快运 行的技巧。 在 Oracle Database 10g 中,Oracle 引入了“网格计算”的概念。作为 Oracle 的真正应用集群(Real Application Cluster,RAC)的逻辑扩展,网格数据库在理论上能够动态地从池(网格)中“请求”资源以满 足最苛刻的要求。当完全实现网格计算时,这些网格资源(服务器网格)可以存在于不同类型的硬件和操作 系统上,即可以存在于完全不同的环境中。以前的 Oracle 版本需要一开始就适当地调整系统以支持最大的 负载。Oracle Database 10g 则朝着实现这个重要的计划迈出了第一步。 在这本书的上一个版本里,第 1 章也是“新特性”章节,是深受许多读者喜爱的章节。因此,本书的 第 1 章也将主要介绍 Oracle 10g 的新特性。剩下的章节会渐渐地增加复杂度,并提供一套能帮助您进行高 级调整的技巧。我确信您肯定会碰到一些您以前从来没接触过的内容。 如果您仍然只想用一个方法或是包容一切的数据库调整方法(只想阅读一个章节),我也为那些没有太 多时间阅读完整本书的人提供了这样的两章。一个是介绍 Statspack 和 AWR Report 的第 14 章:这两个工 具都非常可靠,包含了很多专家用来调整系统性能的常见脚本。这一章花了很多时间来编写。另一个是介 绍网格控制(Enterprise Manager)的第 5 章:这种图形工具提供了图形化的方法来调整系统的性能,包括 用于 RAC 系统和大规模网格控制的许多特性,并且允许用户通过一个窗口来查看并调整很多系统。 对于那些想了解本书全貌的读者来说,最好先来认识一下 Oracle 10g 的新特性。接下来几章的内容 会着重讲解 Oracle 性能调整方面的特性。第 1 章将简要介绍 Oracle 10g 版本中读者较为感兴趣的一些新 特性,在这个版本中包括了大量新的特性和改进的特性。Oracle 在 10g 版本中的目标不仅是创建更为健壮 的数据库管理系统,而且是简化安装和管理活动,从而增强其可用性。Oracle 10gR2(Oracle 10gRelease 2) 版本延续了从 Oracle 9i 开始的发展趋势,促进了 Oracle 的如下策略方向:提供完全集成的一组特性来代 替 DBA 一般用于帮助他们管理环境的第三方软件。Oracle 的这一版本的正式名称是 Oracle Database 10gRelease 2,但是我将其称为 10gR2、10gR1(Release 1)或 Oracle 10g(该版本的通用名称),而在本书 中则直接简称为 10g。当版本之间存在区别时,我将会指明特定的版本。 本章中介绍的新特性包括如下: ● 安装改进 ● SYSAUX 表空间 ● 自动存储管理(Automatic Storage Management,ASM) ● 集群就绪服务(Cluster Ready Services,CRS) ● 自动工作量仓库(Automatic Workload Repository,AWR) ● 自动数据库诊断监控程序(Automatic Database Diagnostic Monitor,ADDM) 26 ● SQL 调整顾问(Tuning Advisor) ● 自动共享内存管理(Automatic Shared Memory Management,ASMM) ● 闪回恢复区和回收站 ● 透明数据加密(10gR2) ● 新的 DBMS_STATS 选项 ● 跟踪增强 ● 大文件表空间 ● 收缩段 ● 数据泵(Data Pump) ● 跨平台的可移植表空间 ● 写入外部表 ● 自动撤消保留调整 ● V$SESSION 中的新列 ● 网格控制 警告: 因为这些都是新加入的特性,所以在能确保这些特性的使用不会引发数据库问题之前, 您都应该小心谨慎地使用并全方面地测试它们。如果能连接到 Metalink,我们强烈建议 您借此判断一下您正打算使用的这个新特性是否存在一些已知的问题。Google.com(尽管 范围很广)是另一个能找到有关 Oracle 特性和功能信息的最好资源。 1.11.11.11.1 安装改进安装改进安装改进安装改进 对于 10g 版本,首先您将会注意到其较小的磁盘占用空间和更为简单的安装。数据库通过一张 CD 提 供给用户,而一些组件,例如 HTTP 服务器和 HTML DB(Application Express 或最新版本中的 APEX),则通 过一张附带的 CD 提供给用户。相比于以前的版本,安装程序需要的步骤更少,并且自动执行更多的配置任 务。对于 RAC 安装,安装程序将自动检测是否安装了集群就绪服务。 Oracle 宣称 10g 版本的安装和配置将比以前快 1/3,这种说法应该比较准确。只需要处理较少的基本 初始化参数,并且通过脚本增强简化了自动部署。10g 版本已经自动化许多安装前和安装后的步骤。在启 动通用安装程序之前,安装前的验证实用程序会检查是否有正确的 OS 配置、补丁和资源参数。在安装完成 之后,配置助手会自动运行以配置许多已安装的组件。 10g 版本中也对版本升级进行了极大的简化。除了执行升级前验证的升级信息工具(Upgrade Information Tool)以及升级后的配置向导(类似于安装后的配置向导)之外,还有一个时间估计程序,通过 该实用程序合理地确定所需的升级窗口。在我测试的升级过程中没有遇到任何问题,但是如果确实产生错 误,10g 版本的升级过程就可以从产生错误的位置开始继续升级,而在以前的版本中则需要从头开始升级。 1.21.21.21.2 SYSAUX SYSAUX SYSAUX SYSAUX 表空间表空间表空间表空间 为了简化管理支持 Oracle 特性的对象,10g 版本中增加了另一个强制的表空间 SYSAUX,它包含一些 以前位于 SYSTEM 表空间中的对象,并且集中存储支持 Oracle 特性(例如 LogMiner、UltraSearch 和 Data Mining)的对象,这些对象以前存储在其他的表空间中。在 Oracle 10g 中,支持特定特性的每组对象称为 “组件”。Oracle 提供了新的视图 V$SYSAUX_OCCUPANTS,其中显示了每个组件使用的空间数量以及 Oracle 提供的存储过程的名称,可以使用这些存储过程将对象移入和移出 SYSAUX 表空间。管理员可以通过这些存 27 储过程将支持特定特性的对象移动到 SYSAUX 以外的表空间。表 1-1 显示了以前版本中一些 SYSAUX 组件的 存储位置。 需要注意的是,表 1-1 没有显示使用的所有模式。例如,OLAP 也使用 OLAPSYS 模式;Intermedia 和 Spatial 使用 MDDATA、MDSYS、ORDSYS 和 ORDPLUGINS 模式;EM 也使用 DBSNMP 模式。使用 SYSAUX 表空间可 避免 SYSTEM 表空间中在安装和卸载 Oracle 选项时产生的碎片。 表 1-1 SYSAUX 表空间组件以前的位置 特 性 模 式 以前的表空间 OLAP CWMLITE CWMLITE Text CTXSYS DRSYS UltraSearch WKSYS DRSYS Intermedia and Spatial ORDSYS SYSTEM 工作空间管理程序(Workspace Manager) WMSYS SYSTEM 数据挖掘(Data Mining) DMSYS ODM EM 仓库(EM Repository) SYSMAN OEM_REPOSITORY LogMiner SYSTEM SYSTEM StatsPack PERFSTAT 用户指定 作业调度程序(Job Scheduler) SYS SYSTEM 1.31.31.31.3 自动存储管理自动存储管理自动存储管理自动存储管理 自动存储管理(ASM)是内置在数据库内核中的文件系统和文件卷管理程序,用于管理代替原始或已使 用文件系统的磁盘驱动器。ASM 为 Oracle Real Application Clusters(RAC)支持以及单个 SMP 机器提供了 跨越集群内多个节点的管理。ASM 并行执行跨越所有可用磁盘驱动器的自动负载平衡和数据分条 (striping),从而防止产生热点并且最大化性能。此外,ASM 针对存储容量的增加和删除执行自动的联机 磁盘空间重新组织。 ASM 可以维护冗余的数据副本以提供容错功能,或者可以在供应商提供的可靠存储机制的基础上进行 构建。通过为数据类别选择所需的可靠性和性能特性来执行数据管理,而不是通过在每个文件的基础配置 数据库来执行数据管理。 ASM 代码通过使用特殊的实例(称为 ASM 实例)来执行它的任务。ASM 实例不安装数据库,而是管理使 ASM 文件可用于数据库实例所需的元数据。ASM 实例和数据库实例共享相同的磁盘组。一个节点上的多个 Oracle 实例共享一个 ASM 实例。ASM 实例以 NOMOUNT 模式启动,并且必须在启动任何使用 ASM 的数据库实 例之前运行。不使用 RMAN 或其他普通的数据库备份程序备份 ASM 实例,因为它只是内存,但是会备份 ASM 数据。如果已经适当地归档磁盘体系结构,则在遇到介质故障时可以快速重新构建 ASM 实例。 Oracle 添加了大量动态性能视图以支持 ASM,如表 1-2 所示。 表 1-2 中的一些视图将只显示 ASM 实例中的数据。查看第 3 章以了解关于 ASM 的更多信息。 表 1-2 支持 ASM 的动态性能视图 28 视 图 说 明 V$ASM_ALIAS 显示 ASM 实例安装的每个磁盘组的别名 V$ASM_CLIENT 显示使用磁盘组的数据库实例 V$ASM_DISK 显示 ASM 发现的每个磁盘,即使它不是磁盘组的一部分 V$ASM_DISKGROUP 显示 ASM 发现的每个磁盘组 V$ASM_FILE 显示已安装磁盘组中的每个文件 V$ASM_OPERATION 显示长时间运行的操作 V$ASM_TEMPLATE 显示每个已安装磁盘组中的模板 1.41.41.41.4 集群就绪服务集群就绪服务集群就绪服务集群就绪服务(CRS)(CRS)(CRS)(CRS) CRS(集群就绪服务)是 10g Real Application Clusters(RAC)的一个新特性,它提供了所有平台上的 标准集群接口,并且包括以前版本中没有的新的高可用性特性。需要在安装 10g RAC 之前安装并运行 CRS。 CRS 可以使用供应商提供的集群件(clusterware)作为其集群件,例如 HP Serviceguard、Sun Cluster、 Veritas Cluster 或 Oracle 的 OCFS。在安装 CRS 之前,必须有用于表决磁盘(voting disk)文件和 OCR(Oracle Configuration Repository,Oracle 配置仓库)文件的共享设备。表决磁盘文件应该至少为 256MB,OCR 文 件也应该至少为 256MB(在以前的版本中,这两个文件分别为 20MB 和 100MB)。CRS 集群管理程序在不同的 层中使用表决磁盘文件。CRS 节点监控程序(Node Monitor,NM)使用表决磁盘文件作为核心内容,这对于 检测和解决集群通信故障至关重要。如果存在网络“分离”现象(节点之间无法进行通信),一个或多个节 点就会自动重启以防止数据破坏。Oracle 配置仓库(OCR)维护涉及集群节点、共享资源、进程状态的动态 信息。CRS 使用端口监控进程管理受支配的集群活动。这些进程包括如下: ● CRSD:该进程作为根用户运行,它维护 OCR 配置信息以及管理“应用程序资源”,并且执行启动、 停止和故障恢复。该进程在遇到故障时会自动重新启动。 ● OCSSD:该进程作为 oracle 用户运行,它提供所有节点成员的访问。该进程提供组服务和基本的 集群锁定。在最初引入的时候,OCSSD 与已有的供应商集群件集成,但是它也可以在没有集成供应商集群 件的情况下运行。如果该进程失败,就会造成服务器重新启动,这样就可以在遇到节点间通信故障时防止 数据破坏。 ● EVMD:该进程作为 oracle 用户运行,它在发生异常状况时生成相应的事件。EVMD 进程产生一个 永久的子进程 EVMLOGGER,EVMLOGGER 则根据要求产生子进程。该进程在遇到故障时会自动重新启动。查看 第 11 章以了解关于 RAC 和集群的更多信息。 1.51.51.51.5 服务器生成的警报服务器生成的警报服务器生成的警报服务器生成的警报 作为提供通常使用第三方软件获得的功能的成果,Oracle 10g 现在提供了直接来自于 Oracle Database 服务器的服务器生成的警报。这些通知信息可提前告知用户潜在的问题或即将发生的问题,并且通常包含 修正这些问题的建议。在解决问题状况之后,也会提供相应的通知信息。 当问题发生或数据不符合用户可配置度量的预期值时,就会生成警报,例如: ● 每秒的物理读取 ● 文件系统缺少空间 ● 每秒的用户提交 29 ● SQL 响应时间 ● 到达最大盘区 可以基于阈值级别(threshold level)或简单地因为事件而发出服务器生成的警报。可以配置基于阈 值的警报,在处于警告和关键阈值级别时发送通知消息。其他的警报基于特殊情况而发出,例如警报日志 中的 ORA 错误。这种警报类型的一些示例如下: ● 快照过于陈旧(ORA-1555) ● 挂起的可恢复会话 ● 恢复区空间利用 Oracle 10g 数据库收集各种统计数据,并且将其存储在工作量仓库中(后面将讨论工作量仓库),然后 分析这些统计数据以产生各种度量。服务器生成的警报依赖于这些派生的度量。对于已经定义阈值的度量, MMON 进程会验证阈值并根据需要生成警报。接下来,将警报放入 ALTER_QUE 队列。Oracle 企业管理器 (Enterprise Manager)读取该队列,并且提供关于重要的服务器警报的通知信息,如果可能的话,也会提 供修正该问题的建议操作。 阈值警报也称为有状态的警报,在警报状况清除时会自动清除。非阈值警报也称为无状态的警报,存 储在由视图 DBA_ALERT_HISTORY 访问的表中。无状态的警报通过企业管理器的数据库控制(Database Control)界面清除。 Oracle 企业管理器(OEM)一般用于配置警报阈值、定义电子邮件和分页程序的目标,以及查看和响应 触发的警报。Oracle 也提供了 DBMS_SERVER_ALERTS 程序包,从而允许通过 SQL*Plus 或另一个 API 操作警 报设置。 Oracle 提供了大量新的视图,这些视图提供了有关服务器警报的信息,如表 1-3 所示。 表 1-3 新的服务器警报视图 视 图 说 明 DBA_THRESHOLDS 列出为该实例定义的阈值设置 DBA_OUTSTANDING_ALERTS 列出数据库中还没有被清除的已触发警报 DBA_ALERT_HISTORY 列出已经被清除的警报的历史记录 V$ALERT_TYPES 提供每个警报的相关信息,例如所在的组和类型 V$METRICNAME 包含系统度量的相关名称、标识符和其他信息 (续表) 视 图 说 明 V$METRIC 包含度量阈值设置和当前值(也存在一个 V$SYSMETRIC 视图) V$METRIC_HISTORY 包含历史的度量阈值设置 1.61.61.61.6 自动工作量仓库自动工作量仓库自动工作量仓库自动工作量仓库(AWR)(AWR)(AWR)(AWR) 在 10g 版本中,自动工作量仓库是公共可管理性基础结构(Common Manageability Infrast ructure,CMI)的主要组件,它是 StatsPack 的后继产品。虽然在 10g 版本中仍然可以使用 StatsPack,但 是计划对 StatsPack 进行一些改进,因此 AWR 是诊断数据库问题的首选机制。Oracle 将自动工作量仓库描 30 述为“Oracle 10g 数据库的数据仓库”,它提供了其他 CMI 组件使用的数据,例如系统生成的警报和顾问。 自动工作量仓库由两个主要的组件组成:内存中统计和仓库快照。 AWR 依赖于 MMON 后台进程。默认情况下,MMON 进程每小时唤醒一次,并且在仓库快照中执行统计收 集。DBA 可以配置这个时间间隔。AWR 快照提供了数据库统计的持久性视图。在 SYS 模式中创建 AWR 快照, 并且将其存储在 SYSAUX 表空间中。每秒收集一次关于活动会话的内存中统计,这些统计不会写入到数据库 中,并且在收集新的统计时从内存中删除。 10g 版本提供了一个脚本($ORACLE_HOME/rdbms/admin/awrrpt.sql),该脚本使用仓库快照生成报表。 也有一个 awrrpti.sql 报表,该报表具有基本相同的输出,但是允许用户定义并报告特定的实例。自动数 据库诊断监控程序(后面将介绍)使用快照信息自动标识性能问题,并且推荐修正这些问题的方法。 通过视图 V$ACTIVE_SESSION_HISTORY 访问内存中统计,该视图查询 SGA 的 ASH 缓冲区域。这个区域 固定为每个 CPU 占用 2MB 空间,因此删除统计之前的时间长度将根据工作量而变化。查看第 5 章以了解关 于企业管理器(EM)的其他信息,查看第 14 章以了解 AWR 报表和 StatsPack 的其他信息。 1.71.71.71.7 自动数据库诊断监控程序自动数据库诊断监控程序自动数据库诊断监控程序自动数据库诊断监控程序(ADDM)(ADDM)(ADDM)(ADDM) CMI 的自动数据库诊断监控程序(ADDM)特性提供了远多于其前身 OEM Expert 工具的功能。ADDM 由内 置在 Oracle 内核中的功能组成,用于帮助用户更为直观地调整 Oracle 实例。 自动化的 ADDM 是 Oracle RDBMS 的一个完整部分,它能够收集性能统计,并且提出改动建议以解决已 有的性能问题。 每次获得 AWR 快照时都会执行 ADDM 分析,该分析使用在 AWR 中维护的统计来提出诊断推荐方法。除 了提供修正问题的建议之外,ADDM 也可以自动地修正某些问题。ADDM 集成在数据库服务器中,因此运行分 析对数据库性能基本没有影响。通常只需要 3 秒不到的时间就可以完成分析。 10g 版本中提供了称为 DBMS_ADVISOR 的 PL/SQL 接口以支持 ADDM 的使用。这个 PL/SQL 接口可以直接 调用,也可以通过提供的脚本($ORACLE_HOME/rdbms/admin/addmrpt.sql)调用,或者与 Oracle 企业管理器 应用程序结合使用。除了这个 PL/SQL 程序包之外,还可以使用大量视图来检索使用 DBMS_ADVISOR API 执 行的任何操作的结果。访问 ADDM 的首选方法是通过企业管理器界面,因为其中显示了完整的性能概观,包 括如何解决单个屏幕上的瓶颈问题的推荐方法。手动访问 ADDM 时,应该考虑使用和您的 Oracle 版本一起 提供的 addmrpt.sql 脚本,因为该脚本消除了访问 DBMS_ADVISOR 程序包所涉及的复杂性。 为了使用 ADDM 对如何调整实例和 SQL 提出建议,您需要确保已经使用至少两组性能数据填充了 AWR。 将 STATISTICS_LEVEL 设置为 TYPICAL 或 ALL 时,数据库将自动调度 AWR,每隔 60 分钟对其进行一次填充。 如果希望在固定时间间隔以外创建快照,则可以使用 DBMS_WORKLOAD_REPOSITORY 程序包。为了帮助诊断特 定的问题,需要在希望检查的情况之前和之后创建快照。 ADDM 可以指出发生了哪些造成性能问题的事件,并且对如何修正这些性能瓶颈提供遵循的方向。ADDM 将发现的事件按照影响降序排列:造成最严重性能影响的问题列在报表的顶端。解决这些问题将对性能带 来最大的增益。同样,在报表的最后一部分中,ADDM 指示了不代表实例的性能问题的区域。 可以使用一些视图查询 ADDM 信息,如表 1-4 所示。 31 表 1-4 查询 ADDM 信息的视图 视 图 说 明 DBA_ADVISOR_ACTIONS 显示与数据库中所有推荐关联的操作的相关信息 DBA_ADVISOR_COMMANDS 显示数据库中所有顾问用于指定推荐操作的命令的相关 信息 DBA_ADVISOR_DEF_PARAMETERS 显示数据库中所有任务的参数和它们的默认值 DBA_ADVISOR_DEFINITIONS 显示数据库中所有顾问的属性 DBA_ADVISOR_DIRECTIVES 未归档 DBA_ADVISOR_FINDINGS 显示数据库中所有顾问发现的问题事件 DBA_ADVISOR_JOURNAL 显示数据库中所有任务的日志项 DBA_ADVISOR_LOG 显示如下方面的相关信息:数据库中所有任务的当前状 态以及执行特有的数据,例如进度监控和完成状态 DBA_ADVISOR_OBJECT_TYPES 显示数据库中所有顾问使用的对象类型的相关信息 DBA_ADVISOR_OBJECTS 显示数据库中所有顾问当前引用的对象的相关信息 DBA_ADVISOR_PARAMETERS 显示数据库中所有任务的参数和它们的当前值 DBA_ADVISOR_PARAMETERS_PROJ 未归档 DBA_ADVISOR_RATIONALE 显示数据库中所有推荐的基本原理的相关信息 DBA_ADVISOR_RECOMMENDATIONS 显示已完成的诊断任务的结果,其中包含针对在每次运 行中标识的问题的操作推荐 (续表) 视 图 说 明 DBA_ADVISOR_SQLA_REC_SUM 显示在访问顾问(Access Advisor)分析操作之后数据库 中所有工作量对象的推荐累积信息 DBA_ADVISOR_SQLA_WK_MAP 显示数据库中所有任务的工作量引用 DBA_ADVISOR_SQLA_WK_STMTS 显示在访问顾问(Access Advisor)分析操作之后数据库 中所有工作量对象的相关信息 DBA_ADVISOR_SQLW_COLVOL 未归档 DBA_ADVISOR_SQLW_JOURNAL 显示数据库中所有工作量对象的日志项 DBA_ADVISOR_SQLW_PARAMETERS 显示数据库中的所有工作量参数和它们的当前值 DBA_ADVISOR_SQLW_STMTS 显示对应于工作量中所有语句的行 DBA_ADVISOR_SQLW_SUM 显示数据库中所有 SQLWkld 工作量对象的聚集图 DBA_ADVISOR_SQLW_TABLES 显示工作量语句和该语句中引用的表之间的交叉引用 DBA_ADVISOR_SQLW_TABVOL 未归档 DBA_ADVISOR_SQLW_TEMPLATES 显示数据库中所有 SQLWkld 模板对象的聚集图 DBA_ADVISOR_TASKS 显示数据库中所有任务的相关信息 DBA_ADVISOR_TEMPLATES 显示数据库中所有模板的相关信息 DBA_ADVISOR_USAGE 显示数据库中每种类型的顾问的使用情况 1.81.81.81.8 SQLSQLSQLSQL 调整顾问调整顾问调整顾问调整顾问 32 这个新特性可以自动化整个 SQL 调整进程。自动化的进程会尽量替换手动的 SQL 调整,但是在一些情 况下,无法代替有经验的 DBA 或开发人员来从查询中获得最佳性能。SQL 调整顾问分析 SQL 语句,并且执 行语句的完整分析,包括: ● 查找陈旧的或遗漏的统计。 ● 通过评估更多的计划来确定较好的执行计划。 ● 检测较好的访问路径和满足这些访问路径所需的对象(索引和具体化视图)。 ● 重新构造 SQL。 虽然 SQL 调整顾问的基本接口是 Oracle 企业管理器的数据库控制,但是可以使用 DBMS_SQLTUNE 程序 包中的程序来管理该顾问。为了使用这些 API,用户必须被授予 DBA 角色,并且具有 ADVISOR 特权。如果 在 Oracle 企业管理器中使用 SQL 调整顾问,用户必须被授予 select_catalog_role 角色。所有的顾问架构 特权都是 DBA 角色的一部分。 使用 DBMS_SQLTUNE 程序包运行 SQL 调整顾问需要两个步骤:首先创建调整任务,然后执行该任务。 CREATE_TUNING_TASK 函数返回用户提供的任务名,或者生成唯一的任务名。用户可以在使用其他 API 时使用任务名指定该任务。 1.91.91.91.9 自动共享内存管理自动共享内存管理自动共享内存管理自动共享内存管理(ASMM)(ASMM)(ASMM)(ASMM) 在 Oracle Database 10g 中,自动共享内存管理(ASMM)特性是另一个自管理增强特性,引入该特性的 作用是:通过使用初始参数 SGA_TARGET 自动确定数据库缓冲区高速缓存(默认池)、共享池、大池和 Java 池的大小。为了使用 ASMM,必须也将初始参数 STATISTICS_LEVEL 设置为 TYPICAL(默认值)或 ALL。 在以前的 Oracle 版本中,用户必须手动配置缓冲区高速缓存和 SGA 池。通常很难正确地配置这些内 存结构,因为将它们调整得过小会造成内存错误,而将它们调整得过大则会导致内存浪费。用户无法确切 地控制 SGA 的总尺寸,因为 Oracle 为如下方面分配内存:固定的 SGA 和超出用户指定 SGA 参数总尺寸的其 他内部元数据分配。 在 10g 版本中,Oracle 数据库根据工作量需求周期性地重新分配这些组件之间的内存(有时分配得并 不理想,因此需要保持谨慎,确保在有负载的情况下测试这种内存分配)。这个特性最大限度地减少了一些 任务,例如分析数据库工作量和重新分配跨 SGA 池的内存。新的 SGA 尺寸参数 SGA_TARGET 现在包含 SGA 中 的所有内存,包括所有自动调整尺寸的组件、手动调整尺寸的组件以及在启动期间的任何内部分配。设置 SGA_TARGET 为默认值 0 可以禁用自动共享内存管理,并且按照过去的方式构建 SGA。ASMM 不会自动管理固 定的 SGA、日志缓冲区、KEEP、RECYCLE 或其他块尺寸缓存(DB_nK_CACHE_SIZE)的尺寸。在 10gR2 版本中, 当 SGA_TARGET 初始参数被设置为非零值时,ASMM 特性可管理流池的尺寸。 分配给这些区域的、包括在 SGA_TARGET 尺寸中的内存是共享的。例如,如果 SGA_TARGET 是 1000MB, 并且 DB_KEEP_CACHE_SIZE 被设置为 50MB,则可用于自动托管组件的内存是 950MB。Oracle 可以增加 DB_KEEP_CACHE_SIZE 的值,但不会设置低于 50MB 的固定参数。当设置 SGA_TARGET 时,从 SGA_TARGET 值 中减去手动 SGA 尺寸参数的总尺寸,得到的结果就是可用于自动调整的 SGA 组件的内存。 当 SGA_TARGET 没有设置或等于 0 时,自动调整的 SGA 参数将按照以前 Oracle 数据库版本中的方式运 作。然而,SHARED_POOL_SIZE 是一个例外:元数据(例如进程和会话的数据结构)的内部开销分配现在包括 33 在 SHARED_POOL_SIZE 参数的值中。作为结果,在从 Oracle 9i 升级到 Oracle Database 10g 时,您可能需 要增加 SHARED_POOL_SIZE 的设置值以解决这些分配问题。例如,假设在 Oracle 9i Database 中使用 256MB 作为 SHARED_POOL_SIZE 值,并且假设内部分配的值是 32MB。为了在 Oracle Database 10g 中获得同样有 效的共享池尺寸,必须设置 SHARED_POOL_SIZE 为 288MB。 可以通过 OEM 或 ALTER SYSTEM 命令动态设置 SGA_TARGET。可以将该值最多增加到 SGA_MAX_SIZE 的值, 也可以将该值减少到任何一个自动调整的组件都到达其最小尺寸(用户指定的最小值或内部确定的最小 值)。如果增加 SGA_TARGET 的值,则会根据自动调整策略对多个自动调整的组件分配额外的内存。如果减 少 SGA_TARGET 的值,则自动调整策略会从一个或多个自动调整的组件中取走内存。因此,SGA_TARGET 值 的任何改动只会影响自动调整组件的尺寸。 如果通过设置 SGA_TARGET 为 0 来动态地禁用自动共享内存调整,则所有自动调整参数的值都被设置 为相应组件的当前尺寸,即使用户已经在前面为自动调整的参数指定了不同的非零值。这些值写入到SPFILE 中以用于下一次的实例启动。参见第 4 章以了解调整 SGA 和初始参数的其他信息。 自动共享内存管理使用新的后台进程,即内存管理器(Memory Manager,MMAN)。MMAN 起到 SGA 内存调 度程序(Memory Broker)的作用,并且协调内存组件的尺寸调整。SGA 内存调度程序跟踪组件的尺寸和有待 解决的尺寸调整操作。SGA 内存调度程序也观察系统和工作量,从而确定理想的内存分配。它每隔几分钟 就执行一次检查,从而可以及时地调整内存分配以响应工作量的改动。 ASMM 的优点可能非常重要,因为许多数据库都有随时间而产生重大改动的工作量配置文件。例如,考 虑如下的系统:在白天运行需要大量缓冲区高速缓存的大型 OLTP 作业,而在夜间运行需要大池中较大值的 并行批处理作业。DBA 必须同时配置缓冲区高速缓存和大池以适应它们的最大需求。通过使用 SGA 自动调 整,当 OLTP 作业运行时,缓冲区高速缓存可拥有最多的内存以实现良好的 I/O 性能。当 DSS 批处理作业在 后面启动时,内存自动转移到大池,从而并行查询操作可以使用它。基于其工作量分析,ASMM 调整可以: ● 在后台周期性地捕获统计数据。 ● 使用不同的内存顾问。 ● 执行“假设”分析以确定最佳的内存分配。 ● 将内存移动到最需要的位置。 ● 如果使用了 SPFILE,则从最近一次停机中恢复组件尺寸(最近一次停机中使用的组件尺寸)。 下面的视图提供了关于动态 SGA 调整尺寸操作的信息: 视 图 说 明 V$SGA_CURRENT_RESIZE_OPS 当前进行的 SGA 调整尺寸操作 V$SGA_RESIZE_OPS 最近 400 个已完成 SGA 调整尺寸操作的相关信息 V$SGA_DYNAMIC_COMPONENTS SGA的动态组件的相关信息 V$SGA_DYNAMIC_FREE_MEMORY 可用于将来动态 SGA 调整尺寸操作的 SGA 内存数量的相关信 息 1.101.101.101.10 闪回恢复区闪回恢复区闪回恢复区闪回恢复区 在过去几年中,磁盘存储器的价格已经大幅下降,到目前为止已经比磁带存储器的价格更具有竞争优 势。使用磁盘空间作为所有数据库恢复操作的主要介质,这是闪回恢复区的核心特性,闪回恢复区为 Oracle 34 数据库中的所有恢复相关文件和恢复活动提供了统一的存储区域。闪回恢复区可以是一个目录、整个文件 系统或自动存储管理(Automatic Storage Management,ASM) 磁盘组。为了进一步优化用于恢复操作的磁 盘空间的使用,多个数据库可以共享一个闪回恢复区。从介质故障中完全恢复数据库所需的所有文件都是 闪回恢复区的一 部分。闪回恢复区简化了备份操作,并且增加了数据库的有效性,因为当用户打开并使用 数据库时,可以执行使用闪回恢复区的许多备份和恢复操作。 闪回恢复将 Oracle 托管文件的功能扩展到所有恢复相关的文件(备份集、映像副本和归档日志)。通 过删除时间较长的文件(基于用户指定的保留策略)为新的文件留出空间,闪回恢复也提供了自动的空间管 理。用户只指定闪回恢复区的位置,以及 Oracle 为恢复相关文件分配的空间数量。在闪回恢复区中维护 的所有文件都按照永久文件或临时文件进行分类。永久文件将是当前控制文件和联机重做日志的多路复用 副 本。如果没有造成实例失败,则不可以删除这些文件。临时文件包括归档的重做日志、数据文件副本、 控制文件副本、控制文件自动备份、备份片和闪回日志。当需 要闪回恢复区中的空间时,Oracle 自动管 理这些文件的删除。当这些文件根据保留策略已经过时或已经备份到磁带时,就可以删除它们。一旦备份 到磁带,闪回恢复区中的任何临时文件就可以在内部放入已删除列表中。在将磁盘上的文件备份到脱机的 存储设备之后,才可以舍弃该文件。 在 Oracle 9i 版本中引入的闪回查询依赖于撤消表空间(undo tablespace)闪回到数据以前的版本, 因此只能够闪回到较短的时间之前。闪回恢复通过创建闪回日志提供了增强的解决方案,闪回日志类似于 重做日志,用于将数据库回复到以前的状态。利用闪回恢复区,Oracle 10g 也添加了闪回版本查询,该功 能允许用户查看两个时间点之间的所有行版本;并且添加了闪回事务查询,用于查看单个事务执行的所有 改动。在开发帮助符合 Sarbanes-Oxley 法案通常所需的审计解决方案方面,这个新的功能变得日益重要。 闪回数据库(Flashback Database)是允许用户将整个数据库快速回复到以前某个时间点对应状态的另 一个新特性。该特性没有还原备份并执行不完整的恢复,而是在当前数据库的 外部备份最近的改动。当数 据库变得越来越大时,这个方法就可以成为将数据库还原到以前状态的较为有效的方法。 闪回数据库使用新的后台进程 RVWR,将数据从系统全局区(System Global Area,SGA)中的闪回缓冲 区写入到闪回恢复区中的闪回日志。 新的动态性能视图 V$RECOVERY_FILE_DEST 显示了关于闪回恢复区的信息,例如它所在的位置、分配 的空间、闪回恢复区中当前使用的空间、闪回恢复区中的文件数,以及在有空间限制时闪回恢复区中的空 闲空间数。V$FLASHBACK_DATABASE_STAT 监控在闪回日志中记录闪回数据的系统开销。 1.111.111.111.11 回收站回收站回收站回收站 通过使用回收站和闪回表的新特性,Oracle 10g 可以只执行很少的语句就还原已删除的表。回收站是 一种逻辑结构,其中的每个表空间保存已删除的表和与该表相关的对象,例如索引。与已删除表关联的空 间不会直接可用,但是会显示在数据字典视图 DBA_FREE_SPACE 中。在底层,这些对象仍然占据在创建它们 时分配的相同空间。没有移动已删除的表和任何关联的对象,例如索引、约束、嵌套的表和其他相关的对 象,而只是使用前缀 BIN$$重命名它们。当空间需求要求使用这些空间时,就以先进先出(FIFO)的方式删 除回收站中的对象,从而最大化最近删除的对象保留在回收站中的时间。也可以使用各种形式的新 PURGE 命令清空回收站。 已删除的对象仍然属于拥有者,并且仍然算在表空间中该拥有者的配额之内;实际上,通过使用 FLASHBACK TABLE...TO BEFORE DROP 命令,仍然可以直接从回收站中访问这些表。 35 只有非 SYSTEM 的本地托管表空间(LMT)可以有回收站。然而,如果已删除的对象位于本地托管的表空 间中,则保护字典托管表空间中的相关对象。此外,使用定义在其上的细粒度审计(Fine-Grained Auditing, FGA)或虚拟私有数据库(Virtual Private Database,VPD)策略的表不可以驻留在回收站中,无论它们驻留 在哪种类型的表空间中。 Oracle Database 10g 提供了如下的视图来查看回收站的内容: 视 图 说 明 DBA_RECYCLEBIN 显示已经被所有用户删除的对象 USER_RECYCLEBIN 显示当前用户已删除的对象 1.121.121.121.12 恢复管理器的改动恢复管理器的改动恢复管理器的改动恢复管理器的改动 10g 版本中对 RMAN 进行了改进,使用增量更新备份减少表空间或整个数据库的恢复时间。这些增量备 份可以应用于数据文件的映像副本,从而极大地减少在发生介质故障时恢复数据文件所需的时间。RMAN 也 提供了大量其他方面的增强,从而用户可以更容易地备份部分数据库或整个数据库。可以只使用一条命令 就创建整个数据库的映像副本,而不用对每个表空间使用一条命令。RMAN 支持备份集的二进制压缩,这样 不仅可以节省闪回恢复区中的磁盘空间,而且可以潜在地减少执行备份所需的时间。RMAN 以非常有组织的 方式使用闪回恢复区目录结构,对每种文件类型使用单独的目录,例如归档的日志、备份集、映像副本、 控制文件自动备份等。此外,进一步按照日期标记划分每个子目录,从而可以基于它们的创建日期方便地 定位备份集或映像副本。 使用新的 RMAN 命令 BACKUP RECOVERY FILES 可以方便地将闪回恢复区中的恢复文件备份到脱机存储 器上。它将闪回恢复区中没有备份过的所有恢复文件备份到磁带上,包括完整的和增量的备份集、控制文 件自动备份、归档的重做日志和数据文件副本。对于手动的热备份,使用 ALTER DATABASE 命令的新子句 BEGIN BACKUP 可以一次性将所有的表空间置于备份模式,而不需要单独处理每个表空间。 RMAN 现在能够使用命令语法 BACKUP AS COMPRESSED BACKUPSET 压缩备份。该命令在指定的设备类型 上创建备份集(而不是映像副本)。当备份到磁带时,AS BACKUPSET 是唯一可用的命令,它用于在任何目标 位置上创建级别 1 的增量备份。也有 Oracle 安全备份(Secure Backup)产品,它允许 RMAN 备份到磁带 (www .oracle.com/database/ secure-backup.html)。 以前版本的恢复管理器已经提供了用于备份的 NULL 压缩,Oracle 10g 则为备份集压缩添加了新的 BINARY COMPRESSION 特性。这种二进制压缩算法可以极大地减少所需的磁盘备份存储器空间。它的压缩比 例一般为 2 倍到 4 倍,对于包含较多文本的数据库,则可以有更高的压缩比例。RMAN 的 LIST 输出可以验 证备份是压缩的备份集。在当前版本中,RMAN 中的 LIST 输出不会正确地报告实际的压缩尺寸,解决方法 是查询备份视图。通过 COMPRESSED 选项使用二进制压缩。压缩写入到备份集中的数据以减少备份集的总尺 寸。创建备份集的所有备份都可以创建压缩的备份集。压缩备份集的还原操作与未压缩备份集的还原操作 相同。 当存储空间比备份和还原时间更为重要时,就可以使用二进制压缩来减少备份集的尺寸。内置在 Oracle 服务器中的压缩算法专门针对 Oracle 归档日志和数据文件的高效压缩进行了调整,相比于没有针 对 Oracle 数据库文件进行调整的通用压缩实用程序,该算法将产生更好的压缩性能。此外,因为集成在 Oracle 中,所以对备份进行压缩只需要在 BACKUP 命令中添加 AS COMPRESSED BACKUPSET 参数(这可能会增 加一些 CPU 开销)。从压缩的备份中还原完全不需要任何特殊的操作。Oracle 公司推荐用户在建立压缩的 36 备份时使用 RMAN 的集成二进制压缩,而不是使用外部的压缩实用程序。关于备份集的二进制压缩的性能考 虑事项的更多信息,可在Oracle 数据库恢复管理器参考文档中查看BACKUP 命令的AS COMPRESSED BACKUPSET 选项说明。 在以前的版本中也使用未使用块压缩(Unused Block Compression,在 10g 版本之前的名称是 NULL 压 缩),从而不会备份永远不使用的块(不备份所有的数据库块)。数据文件中永远不使用的数据块将不会复制 到备份集中,从而节省备份过程期间的存储空间和系统开销,但在 10g 版本之前会备份至少使用过一次的 空数据块。在 10g 版本中,不会备份空的数据块,无论以前是否使用过该数据块。未使用块压缩对于 RMAN 如何将数据文件写入到备份片中至关重要,因此不可以禁用。 加密备份(Encrypted Backup)是 Oracle 在 Oracle 10gR2 中实现的潜在的最重要的新特性。磁带丢失、 更加聪明的犯罪者以及安全需求都推动了备份加密的进一步发展。加密备份可保证在该备份泄露给外人时 仍然保持其安全性。可用的加密模式包括透明加密(Transparent Encryption,这是默认的加密模式,不需 要 DBA 的干涉,因为建立了基础结构)、密码加密(Password Encryption,DBA 为备份提供密码,在恢复时 需要使用该密码)和双模式加密(Dual-Mode Encryption,使用密码或 Oracle 加密钱夹)。虽然备份和恢复 超出了本书的介绍范围,但我希望每个 DBA 都应该研究这个 Oracle 功能,在 Oracle Database Backup and Recovery Advanced Users Guide 中对此进行了详细介绍。 1.131.131.131.13 透明数据加密透明数据加密透明数据加密透明数据加密(10gR2)(10gR2)(10gR2)(10gR2) 透明数据库加密(Transparent Database Encrption,TDE)是 10gR2 中 的新特性,它通过透明地加密 存储在磁盘上的列数据来提供关键的额外安全层。这就为保护操作系统级别上的数据提供了“即装即用” 的方法。透明数据加密是基于 密钥的访问控制系统。即使获取了加密数据,也需要经过授权的解密操作才 可以理解该数据,对于授权访问该表的用户,解密是自动执行的操作。使用数据库服务器 主密钥加密包含 加密列的所有表的密钥,并且存储在数据库的字典表中。没有任何密钥是采用明文存储的。 加密 INSERT 和解密 SELECT 涉及大约 20%~30% 的额外性能损耗,但是为了解决法律服从问题或安全 限制,这些性能损耗是可接受的。只有在从加密列中获取数据或将数据插入到加密列中时才会产生这种性 能损 耗,而在其他列上执行这些操作则不会降低性能。总体的性能影响取决于加密列的数量和它们的访问 频率。最适合于加密的列很明显是包含最敏感数据的列。 为了使用透明数据加密,安全管理员必须创建电子钱夹和设置主密钥。该电子钱夹可以是与其他 Oracle 数据库组件共享的默认数据库钱夹,也可以是透明数据加密专用的单独钱夹。 当电子钱夹打开时,可以通过查询 V$WALLET 的 cert_id 列来搜索证书标识符。如果指定了无效的证 书标识符,则会返回 ORA-28359。只会显示透明数据加密可以用作主密钥的证书。DBA_ENCRYPTED_COLUMNS 和 USER_ENCRYPTED_COLUMNS 视图用于显示加密列的相关信息。 1.141.141.141.14 LogMiner LogMiner LogMiner LogMiner 的改动的改动的改动的改动 Oracle Database 10g 对 LogMiner 工具引入了一些增强功能。第一个增强功能是自动确定日志文件。 在 10g 版本中,当对生成重做日志文件的相同数据库使用 LogMiner 时,基于您所提供的开始时间或开始 SCN,LogMiner 会扫描控制文件并确定所需的重做日志文件。您不再需要将时间帧映射到一组显式的重做 日志文件。为了执行这种扫描,需要使用 CONTINUOUS_MINE 选项并指定 STARTSCN 或 STARTTIME。 37 LogMiner 现在也可以记录对索引组织表执行的改动。当前限制为只支持不包含任何 LOB 或溢出段的 IOT。LogMiner 现在也支持多字节 CLOB 和 NCLOB 数据类型的挖掘,并且扩展了对包含 LONG 和 LONG RAW 列 的表的支持。此外,LogMiner 提供了对新的 BINARY_FLOAT 和 BINARY_DOUBLE 数据类型的支持。10gR2 版本 中的 LogMiner 支持带有溢出段的 IOT 和 LOB。 通过扩展支持 BINARY_FLOAT 和 BINARY_DOUBLE 数据类型以及多字节 CLOB 和 NCLOB 数据类型,一些使 用 LogMiner 的工具,例如 Oracle Data Guard SQL Apply、Logical Standby 和 Oracle Streams,也进行 了改进以覆盖更多的情况。 DBMS_LOGMNR.START_LOGMNR 过 程 的 新 选 项 (NO_ROWID_IN_STMT) 消 除 了 v$logmnr_contents 的 SQL_REDO 和 SQL_UNDO 列中的 rowid。不同的数据库有不同的 rowid,因此可以在与最初生成 SQL 的数据库 不同的数据库上使用 SQL 语句。如果使用该选项并在不同的数据库上使用 SQL,就需要确保每个 SQL 语句 的 WHERE 子句可以唯一标识它影响的每一行。 在以前的版本中,为了从当前的 LogMiner 会话中删除日志文件,需要调用带有 REMOVEFILE 选项的 DBMS_LOGMNR.ADD_LOGFILE 程序。从 10g 版本开始,通过如下方法实现删除日志文件的操作:调用 DBMS_LOGMNR.REMOVE_LOGFILE 程序,并且将日志文件名作为参数传递。 在 Oracle 9i 中引入的 CONTINUOUS_MINE 需要在使用 CONTINUOUS_MINE 之前至少向该会话手动添加一 个归档日志文件。现在,最低限度只需要作为参数的 startTime 或 startSCN 就可以使用 CONTINUOUS_MINE。 LogMiner 自动确定需要将哪些归档日志文件动态添加到会话中,从而通过读取控制文件满足请求的 time/scn 参数。基于在创建数据库或控制文件时指定的 MAXLOGHISTORY 的值,控制文件具有有限数量的归 档日志,并且保留这些归档日志的相关信息。动态视图 v$archived_log 包含控制文件中可用归档日志的相 关信息。当生成日志时,它们将被自动添加到 LogMiner 会话,查询在遇到这些日志时将返回记录。 注意: 在启动 LogMiner 时只指定 ENDTIME 是无效的操作;指定 STARTTIME 是执行连续挖掘的最 低限度的要求。 1.151.151.151.15 新的新的新的新的 DBMS_STATSDBMS_STATSDBMS_STATSDBMS_STATS 选项选项选项选项 每个版本的 Oracle 数据库都对优化器统计收集进行了改进。Oracle 8i 引入了 DBMS_ STATS 程序包,并且对 DBMS_UTILITY 统计收集过程进行了改进。Oracle 9i 引入了监控特性,该特性能够 部分自动化统计收集进程。必须手动启用监控选项,但是由用户确定何时收集统计。如果认为当前的统计 是陈旧的,则可以使用 GATHER AUTO 选项更新对象的统计。在 Oracle 10g 中,优化器统计收集是完全自动 化的操作,用户完全不需要担心统计收集操作,并且默认启用了表监控。 在 9i 版本中,可以使用 DBMS_STATS.ALTER_SCHEMA_TABLE_MONITORING 禁用模式中所有表的 DML 监控 特性。该过程等同于单独发出 CREATE(或 ALTER) TABLE...MON ITORING (或 NOMONITORING)命令。在 10g 版本中,已经不再使用 MONITORING 和 NOMONITORING 关键字。表 监控特性由 STATISTICS_LEVEL 参数控制。当 STATISTICS 38 _LEVEL 被设置为 BASIC 时,就禁用表监控。当 STATISTICS_LEVEL 被设置为 TYPICAL 或 ALL 时,则启用监 控。 默 认 情 况 下 , STATISTICS_LEVEL 被 设 置 为 TYPICAL,即启用表监控 。 我 们 强 烈 推 荐 保 持 STATISTICS_LEVEL 的设置为 TYPICAL。如果设置该参数为 BASIC,就会禁用大多数新的易管理特性,包括: ● ASH(活动会话历史,Active Session History) ● AWR(自动工作量仓库) ● ASSM(自动共享内存管理) ● ADDM(自动数据库诊断监控程序) 统计监控跟踪从上一次收集统计以来对表执行的 INSERT、UPDATE 和 DELETE 操作的近似数量。在 SGA 中维护受影响行的数量的信息,直到 SMON 周期性地(大约每隔 15 分钟)刷新数据字典中的数据时才修改该 信息。 通过如下视图可以查看数据字典信息: 视 图 说 明 ALL_TAB_MODIFICATIONS 描述从上一次收集表统计以来已经修改的所有可访问表的修改 DBA_TAB_MODIFICATIONS 描述数据库中从上一次收集表统计以来已经修改的所有表的修改 USER_TAB_MODIFICATIONS 描述从上一次收集表统计以来已经修改的用户表的修改 Oracle 使用这些视图标识具有陈旧统计的表。当表中 10%的数据已经改动时,Oracle 就认为它的统计 是陈旧的。 当创建 Oracle 10g 数据库或升级到 Oracle 10g 时,会创建名为 GATHER_STATS_JOB 的作业。该作业 由调度程序进行管理(后面将讨论),并且在打开 MAINTENANCE _WINDOW _GROUP 窗口组时运行。 Oracle 10g 只使用 CBO,因此具有更新的统计对于生成良好的执行计划非常重要。使用 DBMS_STATS 程序包的自动统计收集作业根据监控数据来确定何时收集具有陈旧对象的对象统计。 1.161.161.161.16 跟踪增强跟踪增强跟踪增强跟踪增强 Oracle 跟踪已经得到了极大的增强,现在能够在可能由多个会话组成的多层环境中执行端对端的事 务跟踪。端对端的跟踪可以按照客户端标识符、服务、模块、操作、会 话、或实例标识问题。这就可以将 问题具体到特定的用户、会话或应用程序进程。在以前的版本中,用户很难跟踪跨越不同数据库会话的客 户端进程。新的属性 CLIENT_IDENTIFIER 持续跨越所有层和会话以唯一标识客户端会话。可以通过 V$SESSION 视图的 CLIENT_IDENTIFIER 列查看客户端标识符。 一旦跟踪会话完成,就可以使用 TRCSESS 实用程序聚集生成的跟踪文件。TRCSESS 是 Oracle 10g 自带 39 的命令行工具,可以使用该工具将来自于许多跟踪文件的信息合并到一个输出文件中。来自于 TRCSESS 的 输出是由原始数据构成的统一文件。在使用该输出文件评价性能问题之前,应该使用 TKPROF 实用程序对其 进行处理。 也可以使用 DBMS_MONITOR 程序包监控 WAITS 和 BINDS。该程序包代替了 DBMS_ SUPPORT.START_TRACE_IN_SESSION 过程或 10046 事件设置,设置该事件可向用户提供跟踪特定会话上的 WAITS 和 BINDS 的能力。可以使用企业管理器或 DBMS_MONITOR 程序包启用和禁用统计聚集。 下面的数据字典视图用于显示跟踪相关信息: 视 图 说 明 DBA_ENABLED_TRACES 显示已启用的 SQL 跟踪的相关信息 WK$TRACE 内部表包括事件、来源、操作、说明和日期标记 1.171.171.171.17 DBM DBM DBM DBMS_SCHEDULERS_SCHEDULERS_SCHEDULERS_SCHEDULER Oracle10g 包括了用于自动化例行任务的新调度机制。通过将任务结合为组件(组件可以结合为称为作 业的较大组件),该调度程序允许用户管理 Oracle 数据库环境。通过 DBMS_ SCHEDULER 程序包中的过程和函数集合实现该功能。早期的 Oracle 版本包括了用于调度作业的 DBMS_JOB 程序。该实用程序在 Oracle 10g 中仍然可用,但是新的调用程序提供了在很大程度上增强的功能。 DBMS_JOB 和 DBMS_SCHEDULER 之间的主要区别如下: ● DBMS_SCHEDULER 可以执行存储的程序、匿名块以及 OS 可执行文件和脚本,而 DBMS_JOB 只可以 执行存储的程序或匿名的 PL/SQL 块。 ● 考虑到增强的组件重用,调度程序的程序单元作为模式对象存储。DBMS_JOB 只有一种组件,即 作业;而调度程序具有组件层次结构。 ● 可以使用 DBMS_SCHEDULER 更具描述性地定义作业或进度表间隔。DBMS_ SCHEDULER 也具有更详 细的作业运行状态以及故障处理和报告功能。 使用调度程序的典型示例是自动化数据库维护作业,例如执行数据库备份、加载数据仓库数据、收集数据 库统计、刷新具体化视图、检查警报日志错误、或者生成管理报表。 作业可以由预先定义的部分(程序和进度表)组成,也可以完全独立,这取决于使用哪个版本的 CREATE_JOB 程序来创建作业。作业的运行一般由作业协调程序控制,但也可以使用 RUN_JOB 和 STOP_JOB 程序手动控制它们。调度程序允许用户创建保存有关任务的元数据的程序,但没有任何进度表信息。程序 可能与 PL/SQL 块、存储过程或 OS 可执行文件关联。使用 CREATE_PROGRAM 过程创建程序。表 1-5 显示了调 度程序的动态视图。 表 1-5 调度程序动态视图 视 图 说 明 DBA_SCHEDULER_PROGRAMS¹ 显示数据库中所有调度程序的相关信息 DBA_SCHEDULER_JOBS¹ 显示数据库中所有调度程序作业的相关信息 DBA_SCHEDULER_JOB_CLASSES² 显示数据库中所有调度程序作业类的相关信息 40 DBA_SCHEDULER_WINDOWS² 显示数据库中所有调度程序窗口的相关信息 DBA_SCHEDULER_PROGRAM_ARGS¹ 显示数据库中所有调度程序参数的相关信息 DBA_SCHEDULER_JOB_ARGS¹ 显示数据库中所有调度程序作业参数的相关信息 DBA_SCHEDULER_JOB_LOG¹ 显示数据库中所有调度程序作业的日志信息 DBA_SCHEDULER_JOB_RUN_DETAILS¹ 显示数据库中所有调度程序作业的日志运行细节 DBA_SCHEDULER_WINDOW_LOG² 显示数据库中所有调度程序窗口的日志信息 DBA_SCHEDULER_WINDOW_DETAILS² 显示数据库中所有调度程序窗口的日志细节 DBA_SCHEDULER_WINDOW_GROUPS² 显示数据库中所有调度程序窗口组的相关信息 DBA_SCHEDULER_WINGROUP_MEMBERS² 显示数据库中所有调度程序窗口组的成员 DBA_SCHEDULER_SCHEDULES¹ 显示数据库中所有调度程序进度表的相关信息 (续表) 视 图 说 明 DBA_SCHEDULER_RUNNING_JOBS¹ 显示数据库中所有正在运行的调度程序作业的相关信 息 注释 1 和 2:在 10gR2 中也有一些用于调度程序链的新视图,这些视图超出了本书介绍的范围:DBA_ SCHEDULER_CHAINS、DBA_SCHEDULER_CHAINS_RULES、DBA_SCHEDULER_CHAINS_ STEPS、DBA_SCHEDULER_RUNNING_CHAINS。 1.181.181.181.18 默认的默认的默认的默认的((((永久永久永久永久))))表空间表空间表空间表空间 在早期的 Oracle 版本中,如果在创建用户时没有指定 DEFAULT TABLESPACE 和 TEMPORARY TABLEPACE, 它们将默认为 SYSTEM 表空间。如果用户没有在创建段时显式地指定表空间,则在 SYSTEM 表空间中创建该 表空间,前提是在SYSTEM 表空间中向该用户显式地授予配额,或者通过授予系统特权UNLIMITED TABLESPACE 向该用户提供配额。 从 Oracle 9i 开始,通过如下方法解决该问题:允许 DBA 为在创建时没有显式使用 TEMPORARY TABLESPACE 子句的所有用户指定默认的 TEMPORARY TABLESPACE。 在 Oracle Database 10g 中,可以类似地为用户指定默认的永久表空间。在创建数据库期间,CREATE DATABASE 命令可以包含子句 DEFAULT TABLESPACE {tablespace}。创建完成后,可以通过发布如下命令使 某个表空间成为默认表空间: SQL> ALTER DATABASE DEFAULT TABLESPACE {tablespace}; 在 创 建 时 没 有 使 用 DEFAULT TABLESPACE 子句的所有用户将有分配为永久段默认表空间的 {tablespace}。可以在任何时候通过使用上面的 ALTER 命令改变默认的表空间,该命令允许在不同的时间 点将不同的表空间指定为默认的永久表空间。 如果在创建数据库期间没有指定默认的表空间,它将默认为 SYSTEM。重点需要了解的是,通过 ALTER 命令改变默认的表空间时,不会移动任何已有的段。如果在创建用户时没有指定任何默认的表空间,并且 数据库默认表空间在后面改变,则在闪回数据库默认表空间改变之前删除的表时,闪回的表就会进入初始 41 的表空间,而不是进入用户当前的默认表空间。 如 果在创建用户期间显式地指定默认的表空间,则改变数据库默认表空间将不会改变用户的默认表 空间。然而,在创建时没有指定默认表空间的所有数据库用户都将开 始使用这个新的默认表空间。该规则 的例外情况是已经创建在创建期间显式提及默认表空间的用户,该表空间是数据库当前默认的表空间。在 这种情况下,即使在创 建期间已经分配了表空间,改变数据库默认表空间也将改变用户的默认表空间。 1.191.191.191.19 临时表空间组临时表空间组临时表空间组临时表空间组 表空间组包含一个或多个临时表空间,它提供了允许用户使用来自于多个表空间的空间的方便机制。 关于表空间组的规则如下: ● 表空间组必须至少包含一个表空间。 ● 对于表空间组中包含表空间的最大数量没有明确的限制。 ● 表空间组不可以使用与实例中的任何表空间相同的名称。 ● 表空间组的命名规则(最大长度等)与表空间名相同。 ● 表空间组不可以为空。只要删除最后一个成员表空间,就删除该表空间组。 用户不可以通过任何显式的命令来创建表空间组。将第一个临时表空间分配给表空间组时,就会据此 自动创建表空间组。可以在创建临时表空间时指定表空间组名,或者使用 ALTER TABLESPACE 子句指定。 表空间组的目标是提供一种方法,从而令可并行化的单个 SQL 操作可以使用多个临时表空间进行排序。 这样,就可以在非常大的表上创建索引,而不会有单个表空间尺寸的限制,因为索引创建期间的排序操作 可以分布到多个表空间中。 表空间组可以缓解由于单个表空间太小而造成的无法保存排序结果的问题,特别是在具有许多分区的 表上进行保存时。在早期的版本中,如果单个 SQL 操作超出临时表空间的可用空间,则会生成 ORA-01652 错误。 1.201.201.201.20 重命名表空间重命名表空间重命名表空间重命名表空间 这种表空间增强功能允许 DBA 重命名表空间,并且已经成为人们经常要求的增强功能。除了能够方便 地为表空间分配更有意义的名称之外,该功能也简化了在目标数据库中存在相同名称表空间时 将表空间向 该目标数据库传送的过程。在一些情况下,必须谨慎使用这个功能强大的新命令。用户应该考虑下面的注 意事项: ● 不应该重命名 OMF(Oracle 托管文件)表空间,因为每个 OMF 文件的名称都包含表空间名:Oracle 不会重命名相关数据文件的名称。 ● 不应该重命名 READ ONLY 表空间,因为 Oracle 不会更新 READ ONLY 数据文件头。为了改变文件 头,必须将表空间改为 READ WRITE 模式。 应该谨慎地重命名 UNDO 表空间,因为在数据库初始参数 UNDO_TABLESPACE 中引用了该表空间名。确 保改变该参数的值以反映新的名称。这些改动同时影响内存和 SPFILE,并且都记录在警报日志文件中。如 果使用 SPFILE,则会将特定的消息添加到警报日志文件,其中建议 DBA 手动改变对应的初始参数文件。 42 同样需要注意的是,使用包含原有表空间名的数据文件备份执行恢复并不会产生问题:文件头仍然包 含原有表空间的数据文件被恢复到重命名之后,因此在执行恢复之后,数据文件头具有新的表空间名。不 可以重命名 SYSTEM 或 SYSAUX 表空间,尝试重命名这些表空间将生成 Oracle 错误。 1.211.211.211.21 大文件表空间大文件表空间大文件表空间大文件表空间 10g 版本的另一个新特性是引入了大文件表空间。大文件表空间中只包含一个数据文件,该数据文件 根据块尺寸最大可以为 128TB, 这与可能包含多个数据文件的普通表空间相反。大文件表空间一般是本地 托管的表空间、撤消表空间或临时表空间。使用大文件表空间意味着永远不需要向表空间添 加数据文件, 这就简化了表空间的维护。以前在数据文件级别上执行的一些操作现在在逻辑表空间级别上执行。大文件 表空间必须创建为具有自动段空间管理的本地 托管表空间。虽然大文件表空间的默认分配策略是 AUTOALLOCATE,但也可以在 UNIFORM 分配策略更为有效的位置将该默认值改为 UNIFORM。为了创建大文件 表空间,可以使用 BIGFILE 关键字并以 GB 或 TB 为单位指定表空间的尺寸。对于表行的扩展 ROWID,大文 件表空间有不同的格式。表空间中只有一个数据文件,因此 ROWID 不包含相对文件号,而是包含扩展的、 编码的块标识符。 DBMS_ROWID 程序包中的过程按照以往的方式进行操作,但是具有一个新的参数 TS_TYPE_IN,该参数 标识特定行所属的表空间的类型。TS_TYPE_IN 的值为 BIGFILE 或 SMALLFILE。第 3 章中将更详细地介绍大 文件表空间。 1.221.221.221.22 收缩段收缩段收缩段收缩段 在 10g 版本中引入的另一个用户长期要求的 Oracle 增强功能是收缩段的能力,该功能将帮助 DBA 以 更好的方式管理空间。收缩段压缩表或索引中的数据块,有选择地降低高水位线(High Water Mark,HWM), 并且使未使用的空间可用于表空间中的其他段。为了成功执行段的收缩,该段必须位于使用自动段空间管 理(Automatic Segment Space Management,ASSM)的表空间中。 通过指定 ALTER TABLE…SHRINK SPACE 压缩段中行的存储,并且减少 HWM。如果指定 SHRINK SPACE COMPACT,则同样会收缩段,但是 HWM 保持不变。指定 SPACE CASCADE 可以收缩该段以及所有相关对象的段。 可以对普通表、索引(b 树索引和位映射索引)、包含 LOB 的段以及具体化视图指定段收缩,而不可以对集 群表、具有 LONG 列的表、共享的 LOB 段、临时段和 UNDO 段执行段收缩。 在段收缩期间,对于在块之间移动的行,ROWID 可能会改变。因此,不可以对依赖于 ROWID 的段执行 收缩操作,例如 ROWID 具体化视图。对于将要进行收缩的表段,必须启用 ROW MOVEMENT。 在段收缩期间需要注意索引相关性。索引在收缩后不会处于不可用的状态。段收缩的压缩过程实际上 是通过一对插入/删除操作实现的。通过联机完成段收缩,从而改进了对象的可用性。在段收缩期间,数据 作为压缩阶段的一部分移动,因此保持对单个的行和/或包含数据的块的锁定。这将造成并发的 DML(例如 更新和删除)在锁上串行化。当调整 HWM 时,以互斥的方式锁定段,直到调整完成才解除锁定。 1.231.231.231.23 数据泵数据泵数据泵数据泵(Data Pump)(Data Pump)(Data Pump)(Data Pump) 用户迫切要求进行性能增强的一个 Oracle 领域是用于在 Oracle 表和平面文件之间移动大量数据的工 具。实现这种移动的基本工具是导入(imp)和导出(exp)。在以前的版本中,通过允许并行处理和实现直接 路径方法来不断促进性能改进。 43 在 10g 版本中,Oracle 通过数据泵(Data Pump)引入了全新的体系结构。在 Oracle 企业版、标准版和 个人版中都可以使用数据泵,但是只有在企业版中才可以使用并行功能。数据泵的主要部分替代了 imp 和 exp 工具。数据泵的体系结构被设计用于提供超过 imp 和 exp 的重要性能增强。通过使用直接路径方法和 并行执行,数据泵以比传统导入/导出方法快上几倍的速度加载/卸载数据。数据泵也支持从故障点重新启 动作业,并且提供了监控作业执行的功能。数据泵专门用于大型的导入/导出作业,因为数据泵的启动时间 较长。数据泵必须在开始操作时建立作业、查询和主表,而在结束操作时将主表的数据写入到转储文件集。 在数据泵中,所有的工作都由数据库执行。这是对早期导入/导出实用程序的功能的主要改动,这些 实用程序作为客户端应用程序运行,并且执行绝大部分的工作。由于这种体系结构改动,数据泵的转储文 件总是存储在服务器上。通常情况下,导入/导出的转储文件存储在客户端上。 DBMS_DATAPUMP 是数据泵引擎的 PL/SQL API。 使用该 API 创建和监控数据泵作业。DBMS_METADATA API 允许用户编写与 Oracle 数据泵驱动程序交互的自定义代码,这就允许编写执行如下操作的代码:使用数据 泵导入或导出数据,挂起、恢复或监控数据泵作业和其他数据泵相关的活动。 EXPDP 和 IMPDP 是新的客户端实用程序,该实用程序具有与原有的导入和导出实用程序类似的外观。 但是,EXPDP 和 IMPDP 带有大量新的特性,并且比导入和导出实用程序更为有效。EXPDP 和 IMPDP 的一些新 特性是导入和导出实用程序无法实现的功能,例如如下功能: ● 挂起导出文件和从挂起点恢复该文件。 ● 附加到运行的导出或导入作业,或者从运行的导出或导入作业中分离。 ● 从故障点重新启动失败的作业。 ● 使用多个线程,并且在导出期间控制它们的数量。 ● 使用直接路径数据访问方法。 ● 直接使用网络模式操作,通过数据库链接从另一个数据库加载或加载到另一个数据库。 ● 控制导出对象的版本,从而从某个版本的 Oracle 数据库中导出数据,并且确保它与较低版本的 Oracle 数据库兼容。 注意: 版本控制特性不适用于 10g 之前的 Oracle 数据库版本。 ● 分别提取数据和元数据。可以只提取数据库元数据(例如表和索引创建语句),只从数据库中提取 数据,或者同时提取数据库元数据和数据。 ● 在实际生成文件之前,估计将从 EXPDP 中导出的文件的尺寸。此外,EXPDP 可以在导出时执行细 粒度的对象选择,例如只导出过程和函数,并且可以使用外部表。 10g 版本提供了下面的动态视图来支持数据泵作业: 视 图 说 明 DBA_DATAPUMP_JOBS 显示数据库中的所有数据泵作业 DBA_DATAPUMP_SESSIONS 显示附加到数据库中某个作业的所有数据泵会话 DATAPUMP_PATHS 显示可用数据泵导出文件路径的列表 1.241.241.241.24 跨平台的可移植表空间跨平台的可移植表空间跨平台的可移植表空间跨平台的可移植表空间 44 Oracle 8i 引入了可移植表空间的特性。然而,只有在 Oracle 数据库运行在相同的体系结构和操作系 统上时,才可以使用可移植表空间的特性。Oracle 10g 支持跨越不同的平台移动数据文件,用户现在可以 剪切 Windows NT 数据库上的一个表空间并将其移动到 Sun Solaris 数据库。 在不同平台之间移植表空间可能遇到的障碍是数据文件字节顺序格式。Oracle 数据库服务器在其上运 行的 OS 平台一般使用两种字节顺序方案(称为尾序格式)中的一种。每个操作系统都支持大尾序 (big-endian)或小尾序(little-endian)格式以存储数字值。在使用大尾序格式的平台上,以最重要的字节 首先进入内存的方式存储值。在使用小尾序格式的平台上,以最不重要的字节首先进入内存的方式存储值。 在 10g 版本中,只要其尾序格式相同,就可以在执行跨平台可移植表空间时直接复制数据文件。如果 尾序格式不同,则在将数据文件导入到目标数据库之前,必须在 RMAN 中使用 CONVERT 命令对这些数据文件 进行转换。新的 V$TRANSPORATABLE_PLATFORM 视图显示了每个平台的尾序格式。注意,在移植表空间之后 可能需要进一步转换 CLOB 列。在访问数据时,Oracle Database 10g 自动处理这种转换,但是这种转换可 能会影响到性能。通过在完成移植后重新构建这些表,可以避免这些性能影响。 该特性在数据仓库环境中非常有用,在这种环境中,数据集市(Data Mart)位于较小的平台上,而数 据仓库位于较大的平台上。 数据库必须使用相同的数据库字符集和国家字符集。在可移植表空间中不可以执行字符集转换。 1.251.251.251.25 写入外部表写入外部表写入外部表写入外部表 Oracle 9i 引入了外部表,但是这些外部表在 Oracle 数据库中是只读的表。在 Oracle 10g 中,您不 仅可以写入外部表,而且与外部表相关的增强功能也包括执行并行外部表操作的能力和投射列特性,这可 以消除在外部表选择操作期间由于数据质量问题而引发的故障。 在 Oracle 9i 中,SQL 加载程序(SQL Loader)是外部表的访问驱动程序。可以将 OS 文件中的数据加载 到 Oracle 环境中,但是不可以将 Oracle 中的数据写入 OS 文件。Oracle 10g 使用外部数据泵访问驱动程 序。数据泵生成的文件采用只有数据泵可以读取的专用格式(Oracle 本地外部表示、DPAPI),并且独立于 在其上创建文件的 OS。基于此,您可以使用该文件加载到另一个 Oracle 数据库。 一般禁止写入外部表;不支持插入、更新和删除 DML 操作。然而,在加载或卸载数据时,可以使用数 据泵驱动程序对该数据执行转换操作。此外,可以在加载或卸载数据时对该数据创建连接,而使用数据泵 实用程序 EXPDP 和 IMPDP 则不可以执行该操作。类似于删除表空间的效果,删除外部表不会删除对应的 OS 文件。 1.261.261.261.26 自动撤消保留调整自动撤消保留调整自动撤消保留调整自动撤消保留调整 Oracle 9i 引入了撤消保留,该参数用于支持“闪回查询”特性。然而,该参数没有完全解决产生 ORA-1555“快照过旧”错误的所有情况。以秒为单位指定参数 UNDO_RETENTION 的值。该参数确定撤消保留 的低阈值。系统将至少在该参数指定的时间内保留撤消。当设置参数 UNDO_RETENTION 的值为 0 时,Oracle 10g 自动调整撤消保留以减少在长期运行的查询期间产生“快照过旧”错误的机会。 当 UNDO_RETENTION 被设置为 0 时,撤消保留的最小值是 900 秒(15 分钟)。MMON 后台进程将每隔 30 分钟计算最长运行查询的长度,即 MAXQUERYLEN。使用计算得出的 MAXQUERYLEN 的值,MMON 将调整 45 TUNED_UNDORETENTION,并且撤消保留将被设置为 TUNED_UNDORETENTION。 在具有大量 DML 的系统上,有时即使以损害 DML 操作为代价也应该保证撤消保留的阈值。Oracle 10g 引入了 RETENTION GUARANTEE 子句来保证最小的撤消保留,这意味着数据库确定该撤消总是在指定的撤消 保留周期内可用。可以在创建撤消表空间时指定 RETENTION GUARANTEE 子句,或者在后面使用 ALTER TABLESPACE 语句指定。如果需要关闭保留保证,可使用 RETENTION NOGUARANTEE 子句。 1.271.271.271.27 包括新信息的包括新信息的包括新信息的包括新信息的 V$SESSIONV$SESSIONV$SESSIONV$SESSION 在早期的版本中,为了确定正在经历等待的会话,必须连接 v$session_wait 动态性能视图和 v$session 视图。Oracle 10g 已经将 v$session_wait 中的所有等待事件列添加到 v$session 动态视图,从 而通过消除连接方面的开销增加了性能。 v$Session 视图中的新列包括如下: ● BLOCKING_SESSION:作为 Oracle 10g 中的新列,该列包含阻塞当前列中会话的任何会话的会话 标识符。 ● BLOCKING_SESSION_STATUS:作为 Oracle 10g 中的另一个新列,该列包含 blocking_session 列 值的状态。阻塞会话状态列的有效值如下: • VALID:在阻塞会话列中显示有效的会话 ID。 • NO HOLDER:没有任何人占有该资源。 • UNKNOWN:不能确定占有者的 SID。 • UNIMPLEMENTED:没有实现事件的回调。 • GLOBAL:占有者是另一个实例中的会话。 ● SEQ#:在 v$wait 事件中,该列包含唯一标识该事件的序列号。 ● WAIT_CLASS#:所有等待事件已经按类别分类。该列是事件类号,可以通过 v$event_name 动态视图查询等待类号和名称的有效值。 ● WAIT_CLASS:这个新列包含事件类号,对应于在 WAIT_CLASS#列中给定的类号。 ● WAIT_TIME:零值意味着该会话当前正在等待。 ● SECONDS_IN_WAIT:该列显示当前会话的等待事件的持续时间。 ● STATE:当前会话的等待事件的状态。有效值如下: • WAITING(会话当前正在等待) • WAITED UNKNOWN TIME(最近一次等待的持续时间未知) • WAITED SHORT TIME(最近一次等待的时间小于 1/100 秒) 46 • WAITED KNOWN TIME(WAIT_TIME=最近一次等待的持续时间) 其他包括进来的、起帮助作用(从而不需要查看其他的 v$视图)的列有:EVENT#、EVENT、P1、P2、P3、 P1TEXT、P2TEXT、P3TEXT、P1RAW、P2RAW 和 P3RAW。一些入列和闩锁现在按照特定的事件类型进行划分。 对于特定的入列,您也将会有 EVENT 列。结果是不仅有“排列”等待,而且会看到一些“enq%”类型的等 待。 1.281.281.281.28 OEM OEM OEM OEM 的改动的改动的改动的改动 10g 版本中的 Oracle 企业管理器(OEM)有全新的外观,在易于使用的界面中集成了大量改进的功能。 大多数 OEM 界面现在都以 HTML 语言进行架构,运行在中间层,而不是作为基于 Java 的客户端应用程序。 数据库服务器中包括 Apache HTTP 服务器,从而可以直接使用 OEM,这就要求“零启动时间”。Oracle 企业 管理器的仓库、作业和事件子系统现在都自动进行配置,从而不需要进行手动配置。 在安装基本 Oracle 软件时会安装 Oracle 数据库控制(Database Control),可以选择将该工具安装在 相同的 ORACLE_HOME 位置,或者将其安装在单独的 ORACLE_HOME 位置。如果使用 Oracle 数据库配置助手 (Database Configuration Assistant,DBCA)创建数据库,则连同数据库一起安装和预先配置 OEM 数据库 控制。 1.291.291.291.29 网格控制网格控制网格控制网格控制 Oracle 的 OEM 网格控制(Grid Control)是单独的 OEM 版本,通过单独的介质进行安装。通过分组为单 个的逻辑实体,使用网格控制可以管理多个硬件节点、数据库、应用程序服务器和其他目标。通过执行作 业、实施标准策略、监控性能以及自动化跨越一组目标(而不是在许多单独的系统上)的其他许多任务,网 格控制允许根据网格的增长调整管理功能。由于有了这种特性,您可以以有效的、可负担费用的方式管理 组成网格的大多数硬件。第 5 章将详细介绍网格控制。 1.301.301.301.30 10g 10g 10g 10g 版本中的新后台进程版本中的新后台进程版本中的新后台进程版本中的新后台进程 Oracle 10g 包括以下的新后台进程: ● RVWR:恢复写入程序(Recovery Writer)支持闪回数据库;该进程负责写入存储数据块前像的闪 回日志。 ● CTWR:改动跟踪写入程序(Change Tracking Writer)是新的进程,它使用 10g 版本中新的块改动 跟踪特性来执行快速 RMAN 增量备份。 ● MMNL:内存监控灯(Memory Monitor Light)进程使用自动工作量仓库(AWR)新特性,根据需要将 完整的统计缓冲区写出到磁盘。 ● MMON:内存监控(Memory Monitor)进程与用于自动化问题检测和自调整的自动工作量仓库新特性 关联。MMON 根据调度写出所需的 AWR 统计。 ● M000:这些进程是 MMON 后台从属(m000)进程。 ● RBAL:重新平衡端口监控程序(Rebalancing Daemon)是 ASM 相关的进程,它执行由 ASM 控制的磁 盘资源的重新平衡。 ● ARBx:这些进程由 RBAL 进程管理,用于对 ASM 控制的磁盘资源执行实际的重新平衡。调用的 ARBx 进程数量直接受到 asm_power_limit 参数的影响。 47 ● ASMB:ASMB 进程用于向集群同步服务(Cluster Synchronization Service)提供信息或从中取出 信息,ASM 使用集群同步服务管理磁盘资源。该进程也用于更新统计和提供重要的机制。 1.311.311.311.31 版本比较表版本比较表版本比较表版本比较表 下面的表显示了包括在各个 Oracle 10g 版本中的组件和选项(服从改动)。注意有一个 Oracle 免费版 本,称为 Oracle 10g Express Edition,该版本支持 4GB 的数据库 1CPU 和 1GB 的内存(服从改动)。请根 据 Oracle 版本进行检查以确认特性。 表 1-6 Oracle 版本 选项或特性 10g 标准版 1 10g 标准版 10g 企业版 10g 个人版 高级安全性 N N Y Y 改动管理包 N N Y N 数据挖掘 N N Y Y 诊断包 N N Y N 标签安全性 N N Y Y Oracle OLAP N N Y Y 分区 N N Y Y (续表) 选项或特性 10g 标准版 1 10g 标准版 10g 企业版 10g 个人版 Oracle Programmer N N Y Y Oracle RAC Y N Y N Oracle 空间 N N Y Y 调整包 N N Y N CM包 N N Y Y DB 资源管理程序 N N Y Y VLDB、数据仓库、业务智能 数据压缩 N N Y Y 位映射的索引 N N Y Y 导出传送流 N N Y Y 导入传送流 Y Y Y Y 异步改动数据捕获 N N Y Y 摘要管理 N N Y Y 分析函数 Y Y Y Y 自动化的并行查询 N N Y Y 降序索引 Y Y Y Y 直接路径加载 API Y Y Y Y 外部表 Y Y Y Y 基于函数的索引 Y Y Y Y 长操作监控程序 Y Y Y Y 具体化视图 Y Y Y Y MERGE Y Y Y Y 48 优化程序统计管理 Y Y Y Y 流水线表函数 Y Y Y Y 样本扫描 Y Y Y Y 星形查询优化 Y Y Y Y 并行查询 N N Y Y 并行 DML N N Y Y 并行索引构建 N N Y Y 并行统计收集 N N Y Y 并行数据导出和 数据泵 N N Y Y 并行文本索引创建 N N Y Y 并行备份和恢复 N N Y Y 并行分析 N N Y Y (续表) 选项或特性 10g 标准版 1 10g 标准版 10g 企业版 10g 个人版 并行位图星形查询 N N Y Y 并行索引扫描 N N Y Y 并行加载 Y Y Y Y 高 可 用 性 Oracle 数据保护 N N Y Y 快速启动故障恢复 N N Y Y 联机操作 N N Y Y 备份和恢复 N N Y Y Oracle 闪回特性 N N Y Y 内 容 管 理 动态服务 Y Y Y Y 工作空间管理程序 Y Y Y Y 超级搜索 Y Y Y Y 信 息 集 成 Oracle流 N N Y Y Oracle 通信网关 N N Y Y 数据库特性 高级查询 Y Y Y Y 数据库事件触发器 Y Y Y Y DBMS_REPAIR 程 序包 Y Y Y Y 删除列 Y Y Y Y 闪回查询 Y Y Y Y 全局化 Y Y Y Y 索引合并 N N Y Y 49 索引组织表 Y Y Y Y INSTEAD-OF 触发器 Y Y Y Y LOB(大对象)支持 Y Y Y Y 本地托管的表空间 Y Y Y Y LogMiner Y Y Y Y 计划稳定性 Y Y Y Y 静态数据库 N N Y Y 反向键索引 N N Y Y 临时表 Y Y Y Y (续表) 选项或特性 10g 标准版 1 10g 标准版 10g 企业版 10g 个人版 开 发 AppWizard for Virt Studio Y Y Y Y 自治的事务 Y Y Y Y COM 模块 Y Y Y Y iSQL*Plus Y Y Y Y Java Y Y Y Y JDBC 驱动程序 Y Y Y Y MS 事务服务器 Y Y Y Y 对象/关系扩展 Y Y Y Y PL/SQL 本地编译 Y Y Y Y 触发器中的 PL/SQL 存 储过程 Y Y Y Y 内嵌的 PL/SQL 服务器 Y Y Y Y 用户定义的聚集 Y Y Y Y XML Y Y Y Y 分 布 式 高级复制 N N Y Y 基本复制 Y Y Y Y 分布式查询 Y Y Y Y 分布式事务 Y Y Y Y 异构服务 Y Y Y Y 网 络 化 连接管理程序 N N Y Y 多协议 N N Y Y SDP for Infiniband N N Y Y 连接入池 Y Y Y Y Oracle Net 服务 Y Y Y Y 系 统 管 理 基本备用 DB Y Y Y Y 全局索引维护——DDL Y Y Y Y 50 Legato 存储管理程序 Y Y Y Y 多个块尺寸 Y Y Y Y (续表) 选项或特性 10g 标准版 1 10g 标准版 10g 企业版 10g 个人版 联机备份和恢复 Y Y Y Y 标准管理程序包 N N Y N Oracle 企业管理器 Y Y Y Y Oracle 失效保护 Y Y Y Y Oracle 托管文件 Y Y Y Y 恢复管理器 Y Y Y Y 可恢复的空间分配 Y Y Y Y 备用数据库 GUI N N Y Y 透明的应用程序 故障恢复 Y Y Y Y 未使用的索引标识符 Y Y Y Y 安 全 性 虚拟私有 DB N N Y Y 细粒度的审计 N N Y Y 企业安全性 N N Y Y N 层的验证 N N Y Y 密码管理 Y Y Y Y 加密工具集 Y Y Y Y 代理验证 Y Y Y Y 这绝对不是 Oracle 10g 版本中新特性的完整列表。据称 Oracle 10g 中有 149 个新的特性,但是这个 数量包括对以前版本中引入的一些特性的改进。总体来说,Oracle 在以下方面有了很大的提高:提供增强 的功能、自动化许多管理任务、减少获得所有权的总成本。虽然网格计算在接下来的多个版本中可能都不 是成熟的技术,但是 Oracle 引导了使用该技术的相关工作。 1.321.321.321.32 新特性回顾新特性回顾新特性回顾新特性回顾 ● 安装更优秀、更容易 ● 新的 SYSAUX 表空间 ● 改变磁盘存储的自动存储管理(ASM) ● 用于网格技术的集群就绪服务(CRS) ● 用于存储统计的自动工作量仓库(AWR) ● 用于调整的自动数据库诊断监控程序(ADDM) ● 用于给出调整建议的 SQL 调整顾问 51 ● 用于自动调整的自动共享内存管理(ASMM) ● 用于用户错误的闪回恢复区和回收站 ● 用于安全性的透明数据加密(10gR2) ● 用于快速数据移动的数据泵 ● 用于数据移动的跨平台可移植表空间 ● 用于动态数据的写入外部表操作 ● 自动撤消保留调整现在改进的自动撤消 ● V$SESSION 包括用于调整的新信息 ● 网格控制将在后面的版本中继续改进 1.331.331.331.33 参考文档参考文档参考文档参考文档 Sam Alapati, Oracle Database 10g: New Features for Administrators (Oracle Press, 2005) Robert Freeman, Oracle Database 10g New Features (Oracle Press, 2004) Oracle Database New Features Guide, 10g Release 1 (Oracle Corporation, 2003) Oracle Database Reference, 10g Release 1 (Oracle Corporation, 2003) 第第第第 2222 章章章章 基本的索引原理基本的索引原理基本的索引原理基本的索引原理 ((((针对针对针对针对 DBADBADBADBA 和初级开发人员和初级开发人员和初级开发人员和初级开发人员)))) 本章内容并不是针对数据库专家或是那些想快速找到答案的读者。本章主要是讨论基本的索引原理 (也可能是仅有的一章)。对初学者来说,最困难的就是如何找到那些可以填补最主要差距的信息,以及如 何了解 Oracle 的索引功能。本章就是服务于这个目的。尽管市场上有大量面向中高级用户的书籍,但面向 初学者的资料却非常少,而且需求量往往很高。 Oracle 提 供了大量索引选项。知道在给定条件下使用哪个选项对于一个应用程序的性能来说非常重 要。一个错误的选择可能会引发死锁,并导致数据库性能急剧下降或进程终 止。而如果做出正确的选择, 则可以合理使用资源,使那些已经运行了几小时甚至几天的进程在几分钟内得以完成,这样会使您颇有成 就感。本章将讨论每个索引选 项,然后指出每个选项的优缺点。 本章主要内容: ● 基本的索引概念 ● 查找被索引的表以及具有连接索引的表 ● 组合索引的使用方法 ● Oracle ROWID ● 基于函数的索引的使用方法 ● 如何避免比较不匹配的数据类型,造成索引取消 ● 作为索引策略的集群因子 ● 使用 INDEX_STATS 视图 52 ● 索引的二元高度(Binary height) ● 使用直方图 ● 快速全局扫描 ● 使用索引跳跃式扫描特性的方法 ● B 树索引的解释 ● 使用位图索引的时机 ● 使用 HASH 索引的时机 ● 使用索引顺序表的时机 ● 使用反转键索引的时机 ● 使用基于函数的索引的时机 ● 本地和全局分区索引 2.12.12.12.1 基本的索引概念基本的索引概念基本的索引概念基本的索引概念 当从表中访问数据时,Oracle 提供了两个选择:从表中读取每一行(即全表扫描),或者通过 ROWID 一次读取一行。当访问大型表的少量行时,您可能想使用索引。例如,如果只访问大型表中 5%的行,并且 使用索引标识读取的块,则可以执行较少的 I/O。如果没有使用索引,则要读取表中所有的块。 索引改进性能的程度部分取决于数据的选择性以及在表的块之间分布数据的方式。如果数据非常具有 选择性,则表中将只有很少的行匹配索引值(例如护照号码)。Oracle 将能够快速查询匹配索引值的 ROWID 的索引,并且可以快速查询少量的相关表块。如果数据不是非常具有选择性(例如国家名),则索引可能返 回许多 ROWID,导致从表中查询许多单独的块。 如果数据非常具有选择性,但是相关的行在表中的存储 位置并不互相靠近,则会进一步减少索引的 益处。如果匹配索引值的数据分散在表的多个块中,则必须从表中选择多个单独的块以满足查询。在一些 情况中,您会发 现当数据分散在表的多个块中时,最好是不使用索引,而是执行全表扫描。执行全表扫描 时,Oracle 使用多块读取以快速扫描表。基于索引的读取是单块读取,因此在使用索引时的目标是减少解 决查询所需的单个块的数量。 通 过 使 用 Oracle 中的一些可用选项 , 比 如 分 区 、 并 行 DML、并行查询操作以及使用 db_file_multiblock_read_count 进行更大的 I/O 操作,全表扫描和索引查找之间的平衡点发生了改变。 硬件更为快速,磁盘可以在磁盘上的高速缓存中缓存更多的信息,内存也变得更为廉价。与此同时,Oracle 已经增强的索引特性,包括了跳跃式扫描索引和其他减少检索数据所需时间的操作。 技巧: 当升级 Oracle 版本时,确保测试应用程序的查询以确定查询的执行路径是否仍然使用在 升级之前使用的索引。查看执行路径是否改变,并且查看这种改动的效果是更好还是更 差。 索引通常能提高查询的性能。SELECT 语句、UPDATE 和 DELETE 命令的 WHERE 子句的性能(当访问的行 较少时)可以从索引中获益。一般来说,增加索引会降低 INSERT 语句的性能(因为需要同时对表和索引进行 插入)。如果未索引列,则索引列的 UPDATE 操作将会减慢执行速度,因为数据库必须管理对表和索引的改 动。此外,大量行的 DELETE 操作将会由于表中存在索引而减慢执行速度。 53 用于删除表中一半数据的 DELETE 语句同时需要删除所有这些行的索引(这种情况是非常耗时的)。通 常,表中的每个索引都会使对表执行的 INSERT 操作变慢两倍;使用两条索引通常会使插入操作变慢一倍(然 而,一个由两部分组成的单一索引并不比只有一个部分组成的单一索引差很多)。索引列的 UPDATE 和 DELETE 操作同样也会变慢。您需要根据对数据操作性能的影响平衡索引对查询性能带来的益处。查询 DBA_INDEXES 视图可获得表上所有查询的清单。同样需要注意的是,可以通过访问 USER_INDEXES 视图检索模式的索引。 查询 ALL_INDEXES 视图可以查看已经访问的所有表的索引。 例如,在 EMP 表上创建了一些索引,EMP 表是 Oracle 的一个演示表。 create index emp_id1 on emp(empno, ename, deptno); create index emp_id2 on emp (sal); 当发出这些命令时,数据库将在 EMP 表上创建两个单独的索引。每个索引将包含 EMP 表中的指定值以 及匹配指定值的行的 ROWID 值。如果需要查找 Sal 值为 1000 的 EMP 记录,优化器就会使用 EMP_ID2 索引查 找该值,在索引中查找相关的 ROWID,并且使用该 ROWID 在表中查找正确的行。 下面的 USER_INDEXES 查询显示了 EMP 表上的新索引: select table_name, index_name from user_indexes where table_name = 'EMP' ; TABLE_NAME INDEX_NAME ------------------------------ ------------------------------ EMP EMP_ID1 EMP EMP_ID2 输出显示了两个索引,但是没有显示每个索引中的列。为了获得给定表中被索引的特定列,可访问 USER_IND_COLUMNS 视图。同样需要注意的是,DBA 可以通过访问 DBA_IND_COLUMNS 视图检索所有模式中被 索引的列,而通过访问 ALL_IND_COLUMNS 视图则可以查看所有表的索引列。 column index_name format a12 column column_name format a8 column table_name format a8 select table_name, index_name, column_name, column_position from user_ind_columns order by table_name, index_name, column_position; TABLE_NA INDEX_NAME COLUMN_N COLUMN_POSITION -------- ------------ -------- --------------- EMP EMP_ID1 EMPNO 1 EMP EMP_ID1 ENAME 2 EMP EMP_ID1 DEPTNO 3 EMP EMP_ID2 SAL 1 EMP 表中有两个索引。首先,EMP_ID1 是一个组合(concatenated)索引,它对 Empno、Ename 和 Deptno 这几列进行索引。而第二个索引 EMP_ID2 只对 Sal 列进行索引。显示在程序清单中的 Column_Position 显 示了组合索引中的列顺序,即按照 Empno、Ename 和 Deptno 的顺序。 54 技巧: 查询 DBA_INDEXES 和 DBA_IND _COLUMNS 可以检索到一个给定表的索引列表。对于您自己 的模式,只能使用 USER_INDEXES 和 USER_IND_ COLUMNS 来检索信息。 2.22.22.22.2 组合索引组合索引组合索引组合索引 当某个索引包含有多个已索引的列时,我们称这个索引为组合(concatenated)索引或是复合索引。虽 然 Oracle 9i 引入的跳跃式扫描索引访问方法增强了优化器在使用组合索引时的选择,但是您应该谨慎地 选择索引中的列顺序。一般来说,索引的第一列应该是最有可能在 WHERE 子句中使用的列,并且也是索引 中最具选择性的列。 在引入跳跃式扫描功能之前,查询只能在 WHERE 子句中使用索引的第一列时使用索引。考虑如下程序 清单中的示例,其中表 EMP 有一个组合索引,该索引包含了 Empno、Ename 和 Deptno。注意第一部分是 Empno, 第二部分则是 Ename,最后是 Deptno。如果没有使用跳跃式扫描功能,除非在 WHERE 子句中对第一列(Empno) 指定一个值,否则 Oracle 一般不会使用这个索引。 select job, empno from emp where ename = 'RICH'; 因为 Ename 不是索引的第一列,优化器可能会选择不使用该索引。随着在 Oracle 9i 中引入了跳跃式 扫描功能,即使在 WHERE 子句中没有指定 Empno 值,优化器也可能会选择使用该索引。相反,优化器可能 会选择索引的快速全局扫描或全表扫描。 如果在 WHERE 子句中使用索引的第三列,也会产生相同的情况: select job, empno from emp where deptno = 30; 在该程序清单中,WHERE 子 句指定了索引中第三列的值。优化器可能选择执行索引快速扫描访问、索 引快速全局扫描或全表扫描。通过创建索引,您可以在执行查询时为数据库提供更多的选 择。从而有希望 改进整体的性能。注意,用户的代码没有改变,优化器可以识别该索引,并且根据每种替代方法的预期成 本决定使用何种方法。 在下面的示例中,使用了索引的一部分。将第一列 Empno 用作 WHERE 子句中的限制条件,以便 Oracle 可以使用该索引。 select job, empno from emp where empno = 'RICH'; 两种最常见的索引扫描类型是唯一扫描和范围扫描。在唯一扫描中,数据库知道索引包含一个唯一值 列表。在范围扫描中,数据库将根据查询标准从索引中返回多个值。在该示例中,emp_id1 和 emp_id2 索 引没有被创建为唯一索引。Oracle 将在检索它们的值时执行范围扫描。在创建索引时,使用 CREATE UNIQUE INDEX 命令可以创建唯一索引。 55 在创建主键约束或 UNIQUE 约束时,Oracle 将基于指定的列自动创建唯一索引(除非使用 DISABLE 子句 创建约束)。如果创建多列的主键,Oracle 将创建组合索引,其中的列按照在创建主键时指定的顺序排列。 通过提供每个行的 ROWID,类似于 EMP_ID1 和 EMP_ID2 的索引为 Oracle 提供了访问单行数据的能力。 ROWID 其实就是直接指向单独行的物理位置的指针。 技巧: 将 Oracle 的 ROWID 硬编码成特定代码时,一定要小心谨慎。因为不同版本的 ROWID 结构 会有所不同,而且在将来的版本里可能还会有所改变。我建议不要对 ROWID 进行硬编码。 2.32.32.32.3 限制索引限制索引限制索引限制索引 限制索引是一些没有经验的开发人员经常犯的错误之一。在SQL 中有很多陷阱会使一些索引无法使用。 后文的各小节将讨论一些常见的问题。 Oracle 优化器在后台工作,选择并使用可能最有效的数据检索方法。例如,在许多情况下不需要指定 WHERE 子句,从而 Oracle 可以使用索引。如果查询索引列的 MIN 或 MAX 值,Oracle 将从索引(而不是表) 中检索该值。同样,如果对索引列执行 COUNT 函数,Oracle 可以使用索引而不是该列。在下面的小节中, 您将看到 WHERE 子句的逻辑阻止 Oracle 使用索引的情况。 2.3.12.3.12.3.12.3.1 使用不等于运算符使用不等于运算符使用不等于运算符使用不等于运算符(<>(<>(<>(<>、、、、!=)!=)!=)!=) 索引只能用于查找表中已有的数据。每当在 WHERE 子句中使用不等于运算符时,都将无法使用所引用 的列的索引。请考虑下文对 CUSTOMERS 表的查询,CUSTOMERS 表中的 CUST_RATING 列有一个索引。下面的 语句仍会执行一次全表扫描(因为大多数记录都可以被检索到),即使列 CUST_RATING 上存在索引。 select cust_id, cust_name from customers where cust_rating <> 'aa'; 当分析表时,Oracle 收集表中数据分布的相关统计信息。通过使用这种分析,基于成本的优化器就可 以决定在 WHERE 子句中对一些值使用索引,而对其他的值不使用索引。在应用程序开发和测试期间,应该 使用具有代表性的行集,从而可以模拟产品环境中实际的数据值分布。 技巧: 通过使用 CREATE INDEX 命令的 COMPUTE STATISTICS 子句,可以在一个步骤中创建索引 并分析它们。也可以从产品数据库中导入统计信息以测试执行路径(参考 10gR2 Database Performance Tuning Guide (Part Number B14211-01) 的 14.5.2 节)。 2.3.22.3.22.3.22.3.2 使用使用使用使用 IS NULL IS NULL IS NULL IS NULL 或或或或 IS NOT NULLIS NOT NULLIS NOT NULLIS NOT NULL 在 WHERE 子句中使用 IS NULL 或 IS NOT NULL 同样会限制索引的使用,因为 NULL 值并没有被定义。 数据库中没有值等于 NULL 值;甚至 NULL 也不等于 NULL。 56 在 SQL 语句中使用 NULL 会有很多麻烦。如果被索引的列在某些行中存在 NULL 值,在索引中就不会有 相应的条目(例外情况是位图索引,这是位图索引对于 NULL 搜索通常较为快速的原因)。一般情况下,下面 的语句将造成执行全表扫描,即使 Sal 列被索引。 select empno, ename, deptno from emp where sal is null; 如果要在上面的三列中禁用 NULL 值,可以在创建或修改表时使用 NOT NULL。注意,如果表中已经包 含数据,只有在表中每一行都有非 NULL 值或是使用 ALTER TABLE 命令的 DEFAULT 子句时,才可以为列设置 NOT NULL 属性。下面的程序清单显示了修改 EMP 表的 Sal 列以禁用 NULL 值: alter table emp modify (sal not null); 注意,如果想尝试在 Sal 列中插入一个 NULL 值,会返回一个错误信息。 技巧: 在创建表时对列指定 NOT NULL 后会禁用 NULL 值,而且可以避免与使用 NULL 值相关的性 能问题。 下面的创建表语句为 Deptno 列提供了一个默认值。如果在执行 INSERT 操作时该列没有指定的值,就 会使用默认值。如果指定了默认值,并且您确实需要使用 NULL 值,则需要在该列中插入 NULL。 create table employee (empl_id number(8) not null, first_name varchar2(20) not null, last_name varchar2(20) not null, deptno number(4) default 10); insert into employee(empl_id, first_name, last_name) values (8100, 'REGINA', 'NIEMIEC'); 1 row created. select * from employee; EMPL_ID FIRST_NAME LAST_NAME DEPTNO ---------- -------------------- -------------------- ---------- 8100 REGINA NIEMIEC 10 insert into employee values (8200, 'RICH', 'NIEMIEC', NULL); 1 row created. select * from employee; EMPL_ID FIRST_NAME LAST_NAME DEPTNO ---------- -------------------- -------------------- ---------- 8100 REGINA NIEMIEC 10 8200 RICH NIEMIEC 57 技巧: NULL 值通常会限制索引。在创建表时对某一列指定 NOT NULL 或 DEFAULT,对于避免可能 出现的性能问题很有帮助。 2.3.32.3.32.3.32.3.3 使用函数使用函数使用函数使用函数 如果不使用基于函数的索引,那么在 SQL 语句的 WHERE 子句中对存在索引的列使用函数时,会使优化 器忽略掉这些索引。一些常见的函数,如 TRUNC、SUBSTR、TO_DATE、TO_CHAR 和 INSTR 等,都能改变列的 值。因此,无法使用已被函数引用的索引和列。下面的语句会执行一次全表扫描,即使 hire_date 列上存 在索引(只要它不是基于函数的索引)。 select empno, ename, deptno from emp where trunc(hiredate) = '01-MAY-01'; 把上面的语句改成如下所示的语句,这样就可以通过索引进行查找。 select empno, ename, deptno from emp where hiredate > '01-MAY-01' and hiredate < (TO_DATE('01-MAY-01') + 0.99999); 技巧: 通过改变所比较的列上的值,而不用改变列本身,就可以启用索引用。这样可避免全表 扫描。 关于基于函数的索引的更多详情,可查看本章后面的“基于函数的索引”一节。 2.3.42.3.42.3.42.3.4 比较不匹配的数据类型比较不匹配的数据类型比较不匹配的数据类型比较不匹配的数据类型 比较不匹配的数据类型也是比较难于发现的性能问题之一。Oracle 并不会对那些不匹配的数据报错 —— 事实正好相反。例如,Oracle 可以隐式地把 VARCHAR2 列的数据转换成要被比较的数值型数据类型。 考虑如下的示例,其中 account_number 就是一个 VARCHAR2 类型。 如果 Account_Number 列使用 VARCHAR2 数据类型,下面的语句将执行全表扫描,即便是索引 account_number 列: select bank_name, address, city, state, zip from banks where account_number = 990354; Oracle 可以自动把 WHERE 子句变成 to_number(account_number)=990354 这样就限制了索引的使用。这个查询的 EXPLAIN PLAN 仅显示通过“全表扫描”访问这个表(对编程人 员来说通常都很迷惑)。对一些 DBA 或开发人员来说,这样的情况可能很少见,但在很多系统中,数字型值 58 可以用零填充,然后指定为 VARCHAR2 类型。前面的语句可以改写成如下语句,这样可以正确地对这个字段 使用单引号,以使用账号上的索引。 select bank_name, address, city, state, zip from banks where account_number = '000990354'; 作为选择,可以定义 Account_Number 列使用 NUMBER 数据类型,前提是前置的 0 不是该列的关键信息。 技巧: 不匹配数据类型之间的比较会让 Oracle 自动限制索引的使用。即便对这个查询执行 EXPLAIN PLAN 也不能让您明白为什么做了一次“全表扫描”。只有了解关于数据类型的 知识才能帮助您解决这个问题。 2.42.42.42.4 选择性选择性选择性选择性 Oracle 根据查询和数据,提供了多种方法来判断使用索引的价值。第一个方法就是判断索引中的唯一 键或不同键的数量。可以对表或索引进行分析来确定不同键的数量。可以查询 USER_INDEXES 视图的 Distinct_Keys 列来研究分析的结果。比较一下唯一键的数量和表中的行数(如 USER_INDEXES 视图的 Num_Rows 列所示),就可以判断索引的选择性。选择性越高,索引返回的行数就越少,该索引就越好。 技巧: 索引的选择性可以帮助基于成本的优化器来判断执行路径。索引的选择性越高,针对每 个不同值返回的行数也越少。对于组合索引在索引中添加额外的列不会显著改善选择性, 并且使用额外列的成本会超出收益。 2.52.52.52.5 集群因子集群因子集群因子集群因子(Clustering Factor)(Clustering Factor)(Clustering Factor)(Clustering Factor) 集群因子是索引与它所基于的表相比较而得出的有序性度量,它用于检查在索引访问之后执行的表查 找的成本(将集群因子与选择性相乘即可得到该操作的成本)。集群因子记录在扫描索引时将读取的块数量。 如果使用的索引具有较大的集群因子,则必须访问更多的表数据块才可以获得每个索引块中的行(因为邻近 行位于不同的块中)。如果集群因子接近于表中的块数量,则表示索引适当排序;但是,如果集群因子接近 于表中的行数量,则表示索引没有适当排序。集群因子的计算简要介绍如下: (1) 按顺序扫描索引。 (2) 将当前索引值指向的 ROWID 的块部分与以前的索引值进行比较(比较索引中的邻近行)。 (3) 如果 ROWID 指向不同的 TABLE 块,则增加集群因子(对整个索引执行该操作)。 Clustering_Factor 列位于 USER_INDEXES 视图中,该列反映了数据相对于已索引的列是否显得有序。 如果 Clustering_Factor 列的值接近于索引中的树叶块(leaf block)的数目,表中的数据就是有序的。索 引的树叶块存储索引值以及它们指向的 ROWID。 例如,CUSTOMERS 表上 Customer_Id 列的值可以由序列生成器产生,而且是表 CUSTOMERS 上的主键。 59 Customer_Id 的索引的集群因子就有可能非常接近于树叶块数(表示有序)。当往数据库中添加客户数据时, 它们就按照序列产生器所产生的序列值有序地存储在表中。然而,因为整个表的客户名字排列是随机的, 所以 customer_name 上的索引会有一个很高的集群因子。 集群因子对执行范围扫描的 SQL 语句有一定的影响。如果集群因子很低(相对于树叶块的数量),需要 读取的表中块的数量就可以减少很多。这样也增加了相同的数据块已经存在于内存中的可能性。一个较高 的集群因子(相对于树叶块的数量)会增加满足基于索引列的范围查询所需的数据块数目。 技巧: 可以使用表中数据的集群,这样可以提高执行范围扫描类型操作的语句的性能。如果要 决定如何在语句中使用列,对列进行索引是最好的选择。 2.62.62.62.6 二元高度二元高度二元高度二元高度(binary height)(binary height)(binary height)(binary height) 索引的二元高度对把 ROWID 返回给用户进程时所要求的 I/O 数量起到关键作用。二元高度的每个级别 都会增加一个额外的读取块,而且由于这些块不能按顺序读取,它们都要求一个独立的 I/O 操作。在图 2-1 中,我们检索一个二元高度为 3 的索引,这样会返回一行数据给用户,同时有 4 个块被读取:3 个来自索 引,一个来自表。随着索引的二元高度的增加,检索数据所要求的 I/O 数量也会随之增加。 在对索引进行分析后,可以通过查询 DBA_INDEXES 的 blevel 列查看它的二元高度: 图 2-1 具有二元高度或 blevel=3 的索引(级别 3 是树叶块驻留的级别) EXECUTE DBMS_STATS.GATHER_INDEX_STATS ('SCOTT','EMP_ID1'); PL/SQL procedure successfully completed. select blevel, index_name from dba_indexes where index_name = 'EMP_ID1'; 60 BLEVEL INDEX_NAME ---------- ------------------------------ 0 EMP_ID1 技巧: 对索引或者表进行分析可以得到索引的二元高度。使用 USER_INDEXES 视图里的 blevel 列可以检查所有索引的二元高度。 二元高度主要随着表中索引列的非 NULL 值数量以及索引列中值的范围狭窄程度而变化。索引上如果 有大量被删除的行,它的二元高度也会增加。重建索引可能会降低二元高度。虽然这些步骤可以减少针对 索引执行的 I/O 数量,但对性能的改进却很小。如果一个索引中被删除的行接近 20%~25%,重建索引会 降低二元高度以及在一次 I/O 中所读取的空闲空间。 技巧: 一般来说,数据库块尺寸越大,索引的二元高度就越低。二元高度中的每个额外级别 (blevel)在 DML 操作期间会增加额外的性能成本。 2.72.72.72.7 使用直方图使用直方图使用直方图使用直方图 在分析表或索引时,直方图用于记录数据的分布。通过获得该信息,基于成本的优化器就可以决定使 用将返回少量行的索引,而避免使用基于限制条件返回许多行的索引。直方图的使用不受索引的限制,可 以在表的任何列上构建直方图。 构造直方图最主要的原因就是帮助优化器在表中数据严重偏斜时做出更好的规划:例如,如果一到两 个值构成了表中的大部分数据(数据偏斜),相关的索引就可能无法帮助减少满足查询所需的 I/O 数量。创 建直方图可以让基于成本的优化器知道何时使用索引才最合适,或何时应该根据 WHERE 子句中的值返回表 中 80%的记录。 要创建直方图,首先要确定好它的尺寸。该尺寸与直方图所需的存储桶(bucket)数相关。每个存储桶 包含列值和行数的相关信息。 EXECUTE DBMS_STATS.GATHER_TABLE_STATS ('scott','company', METHOD_OPT => 'FOR COLUMNS SIZE 10 company_code'); PL/SQL procedure successfully completed. 前面的查询会在 COMPANY 表上创建一个带有 10 个存储桶的直方图,如图 2-2 所示。图中 COMPANY_CODE 列的所有值被分成 10 个存储桶。这个例子中有一个占大部分的 company_code 值(大约 80%,即 1430)。同 样如图中所示,多数宽度均衡的存储桶都只有 3 行记录;有一个存储桶却有 73 行记录。在高度均衡图中, 每个存储桶有相同数目的行,多数存储桶的终点都是 1430,这也反映了数据的偏斜分布。 Oracle 的直方图是高度均衡的,而不是宽度均衡。也就是说,直方图里的所有存储桶都有相同的行数。 存储桶的起点和终点取决于包含这些值的行数。宽度均衡的直方图则着重于确定每个存储桶的值的范围, 61 然后统计出这个范围内的行数。这并不是一个理想的选择。 图 2-2 构建在 Company_Code 字段上的直方图(具有 10 个存储桶) 技巧: 如果表中的数据分布得较不均匀,直方图会为基于成本的优化器提供一个数据分布的 均衡图(把数据平均分布到各个存储桶)。在不是很偏斜的列上使用直方图并不会提高 性能。 技巧: 默认情况时,Oracle 的直方图会产生 75 个存储桶。可以把 SIZE 的值指定在 1~254 之间。 2.82.82.82.8 快速全局扫描快速全局扫描快速全局扫描快速全局扫描 在索引的快速全局扫描期间,Oracle 读取 B 树索引上的所有树叶块。这个索引可以按顺序读取,这样 可以一次读取多个块。初始化文件中的 DB_FILE_MULTIBLOCK _READ_COUNT 参数可以控制同时被读取的块的 数目。相比于全表扫描,快速全局扫描通常需要较少的物理 I/O,并且允许更快速地处理查询。 如果表查询中的所有列都被包括在索引里,而索引的前置列并不在 WHERE 条件中,就可以使用快速全 局扫描(必须指定第 7 章讲到的 INDEX_FFS 提示)。在下面的示例中用到了 emp 表。它有一个组合索引,包 括列 empno、ename 和 deptno。 select empno, ename, deptno from emp where deptno = 30; 由于 SQL 语句中的所有列都包括在索引中,因此可以执行快速全局扫描。通常在只查询索引连接键列 的连接期间执行索引快速全局扫描。作为选择,Oracle 可能执行索引的跳跃式扫描访问;优化器应该考虑 Deptno 列的直方图(如果有可用的直方图),并且确定哪个可用的访问路径可以产生最低的性能成本。 62 技巧: 如果索引相对于表的总体尺寸来说很小,快速全局扫描就可以使应用程序的性能陡增。 如果表中有一个包含了大部分列的组合索引,索引可能要比真实的表要大,这样快速全 局扫描反而会降低性能。 2.92.92.92.9 跳跃式扫描跳跃式扫描跳跃式扫描跳跃式扫描 本章前面的“组合索引”一节中介绍过,索引跳跃式扫描特性允许优化器使用组合索引,即便索引的 第一列没有出现在 WHERE 子句中。索引跳跃式扫描比全索引扫描快得多,这是因为它只需要执行很少量的 读取。例如,下面的查询显示了索引全扫描和跳跃扫描之间的区别。参考第 6 章,可以更好地了解什么是 执行计划或后面的程序清单中列出的统计数据。在该程序清单中,EMP5 有成百上千的行。 跟随查询的执行,该程序清单显示了查询花费的时间、它在数据库中的执行路径,以及显示处理该查询所 需的逻辑读数量(一致的获取)和物理读数量的统计数据。 create index skip1 on emp5(job,empno); Index created. select count(*) from emp5 where empno = 7900; Elapsed: 00:00:03.13 (Result is a single row…not displayed) Execution Plan 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=1 Bytes=5) 1 0 SORT (AGGREGATE) 2 1 INDEX (FAST FULL SCAN) OF 'SKIP1' (NON-UNIQUE) Statistics 6826 consistent gets 6819 physical reads select /*+ index(emp5 skip1) */ count(*) from emp5 where empno = 7900; Elapsed: 00:00:00.56 Execution Plan 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=6 Card=1 Bytes=5) 1 0 SORT (AGGREGATE) 2 1 INDEX (SKIP SCAN) OF 'SKIP1' (NON-UNIQUE) Statistics 21 consistent gets 17 physical reads 如同该程序清单所示,第二个选项使用 INDEX (SKIP SCAN)操作读取索引。该执行路径需要 21 个逻辑 读,这些逻辑读又需要 17 个物理 I/O 操作。第一个选项执行 INDEX (FAST FULL SCAN)操作,该操作需要 更多数量的逻辑和物理 I/O。 63 为了让优化器选择跳跃式扫描,可能需要在查询中使用提示,如同该程序清单所示。提示影响了优化 器,使其偏向您所指定的执行路径。 技巧: 对于那些有组合索引的大型表而言,索引跳跃式扫描特性可以提供一个快速访问,即使 索引的第一列没有在限制条件中使用。 2.102.102.102.10 索引的类型索引的类型索引的类型索引的类型 下面列出了本节要讨论的索引类型: ● B 树索引 ● 位图索引 ● HASH 索引 ● 索引组织表索引 ● 反转键(reverse key)索引 ● 基于函数的索引 ● 分区索引(本地和全局索引) ● 位图连接索引 2.10.12.10.12.10.12.10.1 B B B B 树索引树索引树索引树索引 B 树索引在 Oracle 中是一个通用索引。在创建索引时它就是默认的索引类型。B 树索引可以是一个列 的(简单)索引,也可以是组合/复合(多个列)的索引。B 树索引最多可以包括 32 列。 在图 2-3 的例子中,B树索引位于雇员表的 last_name 列上。这个索引的二元高度为 3;接下来,Oracle 会穿过两个树枝块(branch block),到达包含有 ROWID 的树叶块。在每个树枝块中,树枝行包含链中下一 个块的 ID 号。 树叶块包含了索引值、ROWID,以及指向前一个和后一个树叶块的指针。Oracle 可以从两个方向遍历 这个二叉树。B 树索引保存了在索引列上有值的每个数据行的 ROWID 值。Oracle 不会对索引列上包含 NULL 值的行进行索引。如果索引是多个列的组合索引,而其中列上包含 NULL 值,这一行就会处于包含 NULL 值 的索引列中,且将被处理为空(视为 NULL)。 64 图 2-3 B 树索引创建过程 技巧: 索引列的值都存储在索引中。因此,可以建立一个组合(复合)索引,这些索引可以直接 满足查询,而不用访问表。这就不用从表中检索数据,从而减少了 I/O 量。 2.10.22.10.22.10.22.10.2 位图索引位图索引位图索引位图索引 位图索引非常适合于决策支持系统(Decision Support System,DSS)和数据仓库,它们不应该用于通 过事务处理应用程序访问的表。它们可以使用较少到中等基数(不同值的数量)的列访问非常大的表。尽管 位图索引最多可达 30 个列,但通常它们都只用于少量的列。 例如,您的表可能包含一个称为 Sex 的列,它有两个可能值:男和女。这个基数只为 2,如果用户频 繁地根据 Sex 列的值查询该表,这就是位图索引的基列。当一个表内包含了多个位图索引时,您可以体会 到位图索引的真正威力。如果有多个可用的位图索引,Oracle 就可以合并从每个位图索引得到的结果集, 快速删除不必要的数据。 下面的程序清单给出了一个创建位图索引的例子: create bitmap index dept_idx2_bm on dept (deptno); Index created. 技巧: 对于有较低基数的列需要使用位图索引。性别列就是这样一个例子,它有两个可能值: 男或女(基数仅为 2)。位图对于低基数(少量的不同值)列来说非常快,这是因为索引的 尺寸相对于 B 树索引来说小了很多。因为这些索引是低基数的 B 树索引,所以非常小, 因此您可以经常检索表中超过半数的行,并且仍使用位图索引。 当大多数条目不会向位图添加新的值时,位图索引在批处理(单用户)操作中加载表(插入操作)方面通 65 常要比 B 树做得好。当多个会话同时向表中插入行时不应该使用位图索引,在大多数事务处理应用程序中 都会发生这种情况。 位图索引示例 下面来看一个示例表 PARTICIPANT,该表包含了来自个人的调查数据。列 Age_Code、Income_Level、 Education_Level 和 Marital_Status 都包括了各自的位图索引。图 2-4 显示了每个直方图中的数据平衡情 况,以及对访问每个位图索引的查询的执行路径。图中的执行路径显示了有多少个位图索引被合并,可以 看出性能得到了显著的提高。 图 2-4 位图索引创建过程 如图 2-4 所示,优化器依次使用 4 个单独的位图索引,这些索引的列在 WHERE 子句中被引用。每个位 图记录指针(例如 0 或 1),用于指示表中的哪些行包含位图中的已知值。有了这些信息后,Oracle 就执行 BITMAP AND 操作以查找将从所有 4 个位图中返回哪些行。该值然后被转换为 ROWID 值,并且查询继续完成 剩余的处理工作。注意,所有 4 个列都有非常低的基数,使用索引可以非常快速地返回匹配的行。 技巧: 在一个查询中合并多个位图索引后,可以使性能显著提高。位图索引使用固定长度的数 据类型要比可变长度的数据类型好。较大尺寸的块也会提高对位图索引的存储和读取性 能。 下面的查询可显示索引类型。B 树索引作为 NORMAL 列出;而位图索引的类型值为 BITMAP。 select index_name, index_type from user_indexes; 技巧: 如果要查询位图索引列表,可以在 USER _INDEXES 视图中查询 index_type 列。 建议不要在一些联机事务处理(OLTP)应用程序中使用位图索引。B 树索引的索引值中包含 ROWID,这 样 Oracle 就可以在行级别上锁定索引。位图索引存储为压缩的索引值,其中包含了一定范围的 ROWID,因 66 此 Oracle 必须针对一个给定值锁定所有范围内的 ROWID。这种锁定类型可能在某些 DML 语句中造成死锁。 SELECT 语句不会受到这种锁定问题的影响。 位图索引有很多限制,如下所示: ● 基于规则的优化器不会考虑位图索引。 ● 当执行 ALTER TABLE 语句并修改包含有位图索引的列时,会使位图索引失效。 ● 位图索引不包含任何列数据,并且不能用于任何类型的完整性检查。 ● 位图索引不能被声明为唯一索引。 ● 位图索引的最大长度为 30。 技巧: 不要在繁重的 OLTP 环境中使用位图索引 2.10.32.10.32.10.32.10.3 HASH HASH HASH HASH 索引索引索引索引 使用 HASH 索引必须要使用 HASH 集群。建立一个集群或 HASH 集群的同时,也就定义了一个集群键。 这个键告诉 Oracle 如何在集群上存储表。在存储数据时,所有与这个集群键相关的行都被存储在一个数据 库块上。如果数据都存储在同一个数据库块上,并且将 HASH 索引作为 WHERE 子句中的确切匹配,Oracle 就可以通过执行一个 HASH 函数和 I/O 来访问数据—— 而通过使用一个二元高度为 4 的 B 树索引来访问数 据,则需要在检索数据时使用 4 个 I/O。如图 2-5 所示,其中的查询是一个等价查询,用于匹配 HASH 列和 确切的值。Oracle 可以快速使用该值,基于 HASH 函数确定行的物理存储位置。 HASH 索引可能是访问数据库中数据的最快方法,但它也有自身的缺点。集群键上不同值的数目必须在 创建 HASH 集群之前就要知道。需要在创建 HASH 集群的时候指定这个值。低估了集群键的不同值的数字可 能会造成集群的冲突(两个集群的键值拥有相同的 HASH 值)。这种冲突是非常消耗资源的。冲突会造成用来 存储额外行的缓冲溢出,然后造成额外的 I/O。如果不同 HASH 值的数目已经被低估,您就必须在重建这个 集群之后改变这个值。ALTER CLUSTER 命令不能改变 HASH 键的数目。 HASH 集群还可能浪费空间。如果无法确定需要多少空间来维护某个集群键上的所有行,就可能造成空 间的浪费。如果不能为集群的未来增长分配好附加的空间,HASH 集群可能就不是最好的选择。 如果应用程序经常在集群表上进行全表扫描,HASH 集群可能也不是最好的选择。由于需要为未来的增 长分配好集群的剩余空间量,全表扫描可能非常消耗资源。 67 图 2-5 使用 HASH 索引的例子 在实现 HASH 集群之前一定要小心。您需要全面地观察应用程序,保证在实现这个选项之前已经了解 关于表和数据的大量信息。通常,HASH 对于一些包含有序值的静态数据非常有效。 技巧: HASH 索引在有限制条件(需要指定一个确定的值而不是一个值范围)的情况下非常有用。 2.10.42.10.42.10.42.10.4 索引组织表索引组织表索引组织表索引组织表 索引组织表会把表的存储结构改成 B 树结构,以表的主键进行排序。这种特殊的表和其他类型的表一 样, 可以在表上执行所有的 DML 和 DDL 语句。由于表的特殊结构,ROWID 并没有被关联到表的行上。 对于一些涉及精确匹配和范围搜索的语句,索引组织表提供了一种基于键的快速数据访问机制。基于 主键值的 UPDATE 和 DELETE 语句的性能也同样得以提高,这是因为行在物理上有序。由于键列的值在表和 索引中都没有重复,存储所需要的空间也随之减少。 如果不会频繁地根据主键列查询数据,则需要在索引组织表中的其他列上创建二级索引。不会频繁根 据主键查询表的应用程序不会了解到使用索引组织表的全部优点。对于总是通过对主键的精确匹配或范围 扫描进行访问的表,就需要考虑使用索引组织表。 技巧: 可以在索引组织表上建立二级索引。 2.10.52.10.52.10.52.10.5 反转键索引反转键索引反转键索引反转键索引 当载入一些有序数据时,索引肯定会碰到与 I/O 相关的一些瓶颈。在数据载入期间,某部分索引和磁 盘肯定会比其他部分使用频繁得多。为了解决这个问题,可以把索引表空间存放在能够把文件物理分割在 68 多个磁盘上的磁盘体系结构上。 为了解决这个问题,Oracle 还提供了一种反转键索引的方法。如果数据以反转键索引存储,这些数据 的值就会与原先存储的数值相反。这样,数据 1234、1235 和 1236 就被存储成 4321、5321 和 6321。结果 就是索引会为每次新插入的行更新不同的索引块。 不能对位图索引和索引组织表进行反转键处理。 技巧: 如果您的磁盘容量有限,同时还要执行大量的有序载入,就可以使用反转键索引。 不可以将反转键索引与位图索引或索引组织表结合使用。 2.10.62.10.62.10.62.10.6 基于函数的索引基于函数的索引基于函数的索引基于函数的索引 可以在表中创建基于函数的索引。如果没有基于函数的索引,任何在列上执行了函数的查询都不能使 用这个列的索引。例如,下面的查询就不能使用 JOB 列上的索引,除非它是基于函数的索引: select * from emp where UPPER(job) = 'MGR'; 下面的查询使用 JOB 列上的索引,但是它将不会返回 JOB 列具有 Mgr 或 mgr 值的行: select * from emp where job = 'MGR'; 可以创建这样的索引,允许索引访问支持基于函数的列或数据。可以对列表达式 UPPER(job)创建索引, 而不是直接在 JOB 列上建立索引,如同下面的程序清单所示: create index EMP$UPPER_JOB on emp(UPPER(job)); 尽管基于函数的索引非常有用,但在建立它们之前必须先考虑下面一些问题: ● 能限制在这个列上使用的函数吗?如果能,能限制所有在这个列上执行的所有函数吗? ● 是否有足够应付额外索引的存储空间? ● 在每列上增加的索引数量会对针对该表执行的 DML 语句的性能带来何种影响? 基于函数的索引非常有用,但在实现时必须小心。在表上创建的索引越多,INSERT、UPDATE 和 DELETE 语句的执行就会花费越多的时间。 注意: 对于优化器所使用的基于函数的索引来说,必须把初始参数 QUERY _REWRITE _ ENABLED 设定为 TRUE。 69 看一下使用基于函数的索引的巨大好处,考虑如下的示例,这个示例中有一个包含一百万行的 SAMPLE 表。 select count(*) from sample where ratio(balance,limit) >.5; Elapsed time: 20.1 minutes create index ratio_idx1 on sample (ratio(balance, limit)); select count(*) from sample where ratio(balance,limit) >.5; Elapsed time: 7 seconds!!! 2.10.72.10.72.10.72.10.7 分区索引分区索引分区索引分区索引 分区索引就是简单地把一个索引分成多个片断。通过把一个索引分成多个片断,可以访问更小的片断 (也更快),并且可以把这些片断分别存放在不同的磁盘驱动器上(避免 I/O 问题)。B 树和位图索引都可以 被分区,而 HASH 索引不可以被分区。可以有好几种分区方法:表被分区而索引未被分区;表未被分区而索 引被分区;表和索引都被分区。不管采用哪种方法,都必须使用基于成本的优化器。分区能够提供更多可 以提高性能和可维护性的可能性。 有两种类型的分区索引:本地分区索引和全局分区索 引。每个类型都有两个子类型,有前缀索引和 无前缀索引。表各列上的索引可以有各种类型索引的组合。如果使用了位图索引,就必须是本地索引。把 索引分区最主 要的原因是可以减少所需读取的索引的大小,另外把分区放在不同的表空间中可以提高分区 的可用性和可靠性。 在使用分区后的表和索引时,Oracle 还支持并行查询和并行 DML(可以在第 11 章中看到更详细的内 容)。这样就可以同时执行多个进程,从而加快处理这条语句。 1. 1. 1. 1. 本地分区索引本地分区索引本地分区索引本地分区索引((((通常使用的索引通常使用的索引通常使用的索引通常使用的索引)))) 可以使用与表相同的分区键和范围界限来对本地索引分区。每个本地索引的分区只包含了它所关联的 表分区的键和 ROWID。本地索引可以是 B 树或位图索引。如果是 B 树索引,它可以是唯一或不唯一的索引。 这种类型的索引支持分区独立性,这就意味着对于单独的分区,可以进行增加、截取、删除、分割、 脱机等处理,而不用同时删除或重建索引。Oracle 自动维护这些本地索引。本地索引分区还可以被单独重 建,而其他分区不会受到影响。 有前缀的索引 有前缀的索引包含了来自分区键的键,并把它们作为索引的前导。例如,让我们再次回顾 participant 表。在创建该表后,使用 survey_id 和 survey_date 这两个列进行范围分区,然后在 survey_id 列上建立 一个有前缀的本地索引,如图 2-6 所示。这个索引的所有分区都被等价划分,就是说索引的分区都使用表 的相同范围界限来创建。 70 技巧: 本地的有前缀索引可以让 Oracle 快速剔除一些不必要的分区。也就是说没有包含 WHERE 条件子句中任何值的分区将不会被访问,这样也提高了语句的性能。 无前缀的索引 无前缀的索引并没有把分区键的前导列作为索引的前导列。若使用有同样分区键(survey_id 和 survey_date)的相同分区表,建立在 survey_date 列上的索引就是一个本地的无前缀索引,如图 2-7 所示。 可以在表的任一列上创建本地无前缀索引,但索引的每个分区只包含表的相应分区的键值。 图 2-6 分区的、有前缀的索引 图 2-7 无前缀的本地索引 如果要把无前缀的索引设为唯一索引,这个索引就必须包含分区键的子集。在这个例子中,我们必须 把包含 survey 和(或)survey_id 的列进行组合(只要 survey_id 不是索引的第一列,它就是一个有前缀的 索引)。 技巧: 对于一个唯一的无前缀索引,它必须包含分区键的子集。 2. 2. 2. 2. 全局分区索引全局分区索引全局分区索引全局分区索引 全局分区索引在一个索引分区中包含来自多个表分区的键。一个全局分区索引的分区键是分区表中不 同的或指定一个范围的值。在创建全局分区索引时,必须定义分区键的范围和值。全局索引只能是 B 树索 引。Oracle 在默认情况下不会维护全局分区索引。如果一个分区被截取、增加、分割、删除等,就必须重 建全局分区索引,除非在修改表时指定 ALTER TABLE 命令的 UPDATE GLOBAL INDEXES 子句。 有前缀的索引 通常,全局有前缀索引在底层表中没有经过对等分区。没有什么因素能限制索引的对等分区,但Oracle 在生成查询计划或执行分区维护操作时,并不会充分利用对等分区。如果索引被对等分区,就必须把它创 建为一个本地索引,这样 Oracle 可以维护这个索引,并使用它来删除不必要的分区,如图 2-8 所示。在该 图的 3 个索引分区中,每个分区都包含指向多个表分区中行的索引条目。 71 图 2-8 分区的、全局有前缀索引 技巧: 如果一个全局索引将被对等分区,就必须把它创建为一个本地索引,这样 Oracle 可以维 护这个索引,并使用它来删除不必要的分区。 无前缀的索引 Oracle 不支持无前缀的全局索引。 2.10.82.10.82.10.82.10.8 位图连接索引位图连接索引位图连接索引位图连接索引 位图连接索引是基于两个表的连接的位图索引,在数据 仓库环境中使用这种索引改进连接维度表和 事实表的查询的性能。创建位图连接索引时,标准方法是连接索引中常用的维度表和事实表。当用户在一 次查询中结合查 询事实表和维度表时,就不需要执行连接,因为在位图连接索引中已经有可用的连接结果。 通过压缩位图连接索引中的 ROWID 进一步改进性能,并且减少访问数据所需的 I/O 数量。 创建位图连接索引时,指定涉及的两个表。相应的语法应该遵循如下模式: create bitmap index FACT_DIM_COL_IDX on FACT(DIM.Descr_Col) from FACT, DIM where FACT.JoinCol = DIM.JoinCol; 位图连接的语法比较特别,其中包含 FROM 子句和 WHERE 子句,并且引用两个单独的表。索引列通常 是维度表中的描述列—— 就是说,如果维度是 CUSTOMER,并且它的主键是 CUSTOMER_ID,则通常索引 Customer_Name 这样的列。如果事实表名为 SALES,可以使用如下的命令创建索引: create bitmap index SALES_CUST_NAME_IDX on SALES(CUSTOMER.Customer_Name) from SALES, CUSTOMER 72 where SALES.Customer_ID=CUSTOMER.Customer_ID; 如果用户接下来使用指定 Customer_Name 列值的 WHERE 子句查询 SALES 和 CUSTOMER 表,优化器就可 以使用位图连接索引快速返回匹配连接条件和 Customer_Name 条件的行。 位图连接索引的使用一般会受到限制:只可以索引维度表中的列。用于连接的列必须是维度表中的主键或 唯一约束;如果是复合主键,则必须使用连接中的每一列。不可以对索引组织表创建位图连接索引,并且 适用于常规位图索引的限制也适用于位图连接索引。 2.112.112.112.11 快速重建索引快速重建索引快速重建索引快速重建索引 执行 ALTER INDEX 语句中的 REBUILD 选项,可以使用已有索引而不是整个表快速重建索引: alter index cust_idx1 rebuild parallel tablespace cust_tblspc1 storage (pctincrease 0); Index altered. 我们可以同时修改 STORAGE 子句,并且可以使用 parallel 选项。 技巧: 利用 ALTER INDEX 语句中的 REBUILD 选项,可以使用已有索引而不是表来快速重建索引。 在执行这个操作时必须要有足够的空间来保存所有的索引。 技巧: 还可以在重建索引时使用 REBUILD ONLINE 选项,以允许对表或分区进行 DML 操作。但不 能对位图索引或那些强制参照完整性约束的索引指定 REBUILD ONLINE。 2.122.122.122.12 技巧回顾技巧回顾技巧回顾技巧回顾 ● 在升级 Oracle 版本时,确保测试应用程序的查询以确定查询的执行路径是否仍然使用在升级之 前使用的索引。查看执行路径是否改变,并且查看这种改动的效果是更好还是更差。 ● 查询 DBA_INDEXES 和 DBA_IND_COLUMNS 以检索一个表的索引列表。使用 USER_INDEXES 和 USER_IND_COLUMNS 检索唯一模式的信息。 ● 避免将 Oracle 的 ROWID 硬编码为特定编码。不同版本的 ROWID 结构会有所不同,而且可能在将 来的版本里还会有所改变。我建议不要对 ROWID 进行硬编码。 ● 通过使用 CREATE INDEX 命令的 COMPUTE STATISTICS 子句,可以在一个步骤中创建并分析索引。 ● 对表的列使用默认值子句,这样就可以禁止使用 NULL 值,并且消除与使用 NULL 值关联的性能问 题。 ● 通过使用函数(例如 TO_DATE 或 TO_CHAR)修改所比较的列值而不是列本身,就可以使用索引,在 对列本身使用函数时就会限制使用这些索引。 ● 比较不匹配的数据类型可能会导致Oracle内部限制对索引的使用。即使对查询使用EXPLAIN PLAN 也不能解释为什么会执行全表扫描。 ● 索引的选择性可以帮助基于成本的优化器判断一条执行路径。索引选择性越高,返回的行数就越 少。可以通过建立组合/复合(多列)索引来提高选择性。 ● 通常,数据库块越大,索引的二元高度越小。 73 ● blevel 中的每个额外级别都会额外增加 DML 的执行成本。 ● 表中的数据集群可以用来提高执行范围扫描类型操作的语句的执行性能。通过判断语句中是如何 使用列的,可以在对列进行索引时提供很多好处。 ● 对索引或表进行分析可以得到索引的二元高度。使用 USER_INDEXES 视图中的 blevel 列可以检查 索引的二元高度。 ● 如果索引中被删除的行数达到了 20%~25%,就必须重建索引,这样可以减少二元高度和在一次 I/O 过程中读取的空闲空间量。 ● 如果表中的数据是偏斜的,直方图可以为基于成本的优化器提供一个分布图。在那些不偏斜的列 上使用直方图并不会提高性能,反而可能降低性能。 ● 默认情况下,Oracle 在一个直方图里创建了 75 个存储桶。这个数目可以指定为 1~254。 ● 对于有组合索引的大型表来说,索引跳跃式扫描特性可以进行快速访问,即使是索引的前导列没 有出现用在限制条件中。 ● 已索引列的值存储在索引中。因此,您可以建立一个组合(复合)索引,这样查询可以直接访问这 些索引,而不必从表中检索数据,同时也就减少了 I/O。 ● 对于低基数的列可以使用位图索引。性别列就是一个典型的例子,性别只能是男或女(基数只为 2)。 ● 查询 USER_INDEXES 视图可以查询位图索引列表。 ● 不要在高 OLTP 的环境中使用位图索引;一定要记住位图索引的限制。 ● 在实现 HASH 集群之前一定要记住相关的注意事项。您需要仔细地检查应用程序,保证在实现这 个选项之前已经知道尽可能多的关于表和数据的信息。通常,HASH 选项对于一些包含较有序的值的静态数 据非常有效。 ● HASH 索引在限制条件指定了一个确定值时而不是一个范围的值时最为有用。 ● 考虑对那些经常通过在主键上指定值或范围扫描来访问数据的表使用索引组织表。 ● 如果只有有限的磁盘数,同时还有大量的载入操作要执行,反转键索引就是可行的方案。 ● 对于优化器中所使用的基于函数的索引,必须把初始参数QUERY _ REWRITE_ ENABLED设为TRUE(默 认值为 FALSE)。 ● 有前缀的本地索引可以帮助 Oracle 快速剔除不必要的分区。若分区没有包含任何一个 WHERE 子 句中所使用的值,就可以不用访问分区,这样提高了语句的执行性能。 ● 要使无前缀索引唯一,它必须包含分区键的子集。 ● 在修改分区表时指定 ALTER TABLE 命令的 UPDATE GLOBAL INDEXES 子句。默认情况下,在改变分 区表时需要重建全局索引。 ● 如果一个全局索引将被对等分区,必须把它创建为本地索引,这样 Oracle 可以维护这个索引, 并使用它来删除不必要的分区。 ● 使用位图连接索引改进数据仓库环境中连接的性能。 ● 在 ALTER INDEX 语句中使用 REBUILD 选项可以使用已有索引而不是表来快速重建索引。 ● 可以在重建索引时使用 REBUILD ONLINE 选项,以允许对表或分区进行 DML 操作。但不能在位图 索引或那些强制参照完整性约束的索引上指定 REBUILD ONLINE 选项。 2.132.132.132.13 参考文档参考文档参考文档参考文档 Greg Pucka, Oracle Indexing (TUSC) Oracle7 Server Tuning (Oracle Corporation) Oracle8 Server Tuning (Oracle Corporation) 74 Server Concepts (Oracle Corporation) Server Reference (Oracle Corporation) Kevin Loney, Oracle8 DBA Handbook (Oracle Press) Rich Niemiec, Tuning Tips: You Will Be Toast! (Oracle Press) Metalink Note: 39836.1 第第第第 3333 章章章章 磁盘实现方法和磁盘实现方法和磁盘实现方法和磁盘实现方法和 ASM(ASM(ASM(ASM(针对针对针对针对 DBA)DBA)DBA)DBA) 随着自动存储管理(ASM)技术的发布,Oracle 在 10g 版本中改变了磁盘访问的前景。本章将重点介绍 ASM 以及 Oracle 中非 ASM 特有的磁盘实现方法。 在过去的几年中,磁盘配置技术似乎到达了瓶颈,无法采取进一步的措施来改进系统的性能,同时又 不会极大地增加 DBA 的日常管理工作。如果系统在操作时具有一些独特的特性,或者用户选择频繁地评审 表空间上的 I/O 活动,则相比于在一个逻辑设备中进行所有磁盘使用操作,这些方法能够实现稍好一些的 性能。但是,对于在此情况下的大多数人来说,并不值得这样做。如果使用 RAW 分区并且非常勤奋地进行 操作,则可能通过该“技术”改进性能,但是这同样不会简化 DBA 的日常管理工作。最后,随着单个设备 的容量急剧增长,即使是在高端的光纤通信扇区(Fibre-Channel sector)中,事情也变得进一步复杂化, 因为现在简单地限制用户只能使用 4 到 6 个磁盘,而在以前则可以有一个完整的磁盘阵列或多个磁盘阵列。 在最近的 Oracle Database 版本中,为用户提供了一个全新的工具箱。 现在可以使用更多的特性来管理如何在磁盘上驻留数据。过去 18 个月中发布的磁盘管理特性数量超 出了以前的所有磁盘管理特性。为了更好地管理磁盘,运行 Oracle 的几乎任何人都可以使用这些新特性。 不用感到担心,本章仍然会讨论平衡磁盘空间分配和消除磁盘碎片,但是也将讨论一些新特性,我们可以 利用这些新特性来避免在过去需要重复执行的一些工作,或者非常有可能避免同时执行这些工作。 为了保持系统最高效地运行,本章将介绍如下技巧: ● 了解存储硬件和它的性能含义 ● 了解 RAID 级别 ● 在文件系统之间分布“关键”数据文件以最小化争用 ● 移动数据文件以平衡文件 I/O ● 使用本地托管的表空间(LMT)与使用字典托管的表空间 ● 了解大文件表空间并接触 8EB 字节的 Oracle 数据库 ● 查看文件和表空间信息以确定问题区域 ● 了解 ASM 实例、安装和 SGA 参数调整 ● 了解 ASM 磁盘、磁盘组和多路径 ● 了解 ASM 最佳实践和重新平衡实践 ● 通过使用分区避免磁盘争用和管理大型表 ● 适当调整盘区以消除碎片、减少链接并保持最佳性能 ● 管理数据库中的重做和回滚以提高速度 ● 使用撤消管理 75 ● 只在 TEMPORARY 表空间中排序 ● 在不同的磁盘和控制器上存放多个控制文件 ● 使用高级文件系统特性和裸设备以改进性能 ● 了解在系统的计划阶段要考虑的问题 3.13.13.13.1 成为规范的磁盘阵列成为规范的磁盘阵列成为规范的磁盘阵列成为规范的磁盘阵列 使用 RAID(冗余独立/廉价磁盘阵列,Redundant Array of Independent/Inexpensive Disks)配置磁 盘现在已经成为一种规范。人们已经普遍使用 RAID,甚至是购买没有 RAID 的中等系统都会承受很大的压 力。在本章的后面,您将看到 ASM 也提供了冗余级别。即使是在个人计算领域,使用一些基于硬件的冗余 磁盘配置也已经变得更为常见。对于 DBA 来说,这意味着比以前更多的管理工作,他们必须采取特殊的保 护,确保磁盘阵列配置使用增强的 I/O,同时也对磁盘故障提供相应保护。无论 RAID 配置是基于硬件还是 软件,配置工作都应该尽可能地设置正确,以获得最佳的性能,同时保持适当的保护。 3.1.13.1.13.1.13.1.1 使用磁盘阵列改进性能和可用性使用磁盘阵列改进性能和可用性使用磁盘阵列改进性能和可用性使用磁盘阵列改进性能和可用性 通过以如下的方式分组一些磁盘(分组为卷或虚拟磁盘)来创建 RAID 逻辑单元号(Logical Unit Number,LUN):将单个磁盘作为一个逻辑磁盘使用。在存储区域网络(Storage Area Network,SAN)出现之 前,LUN 就是磁盘驱动器地址(编号)。因此,在正常操作期间,一个逻辑设备可以得益于它背后的多个物 理设备。这意味着更为快速的数据访问(在适当地配置时)和容量远大于单个设备物理限制的存储卷。如果 一个磁盘失败并且这个磁盘上的所有数据都被破坏,此时就可以重建磁盘组,因此数据可以存储在多个位 置。系统并不会因为一个磁盘故障而停止下来(在使用适当的 RAID 级别时)。在一个磁盘出现故障时,用户 可以继续他的操作,就好像什么也没有发生一样。系统会警告系统管理员某一个磁盘发生了故障。然后管 理员会取出这块磁盘并插入另一个磁盘。硬件控制器或操作系统会自动在新的磁盘上写入丢失的信息。系 统会继续操作,就像没有中断一样。 3.1.23.1.23.1.23.1.2 所需的磁盘数量所需的磁盘数量所需的磁盘数量所需的磁盘数量 我知道处于这种情况的硬件供应商会感谢我,但实情就是如此。关于购买磁盘的经验之谈是:“不要 单凭容量来购买磁盘”。如果您有一个中等规模的 200GB 数据库,为什么需要购买具有 1TB 磁盘容量的磁 盘阵列?原因就在于磁盘主轴。由于单个磁盘的容量一直保持在 146GB 左右,合理地分布数据可能非常困 难,但是近来我经常看到人们单凭容量购买磁盘,这就给这些人带来了不适当的冗余或较差的性能,或者 同时产生这两种情况。除此之外,在正确地配置 1TB 的磁盘之后,您可能只有 500GB 的可用存储空间,其 他的 300GB 容量用来执行各种有用的操作。至于具体的操作,使用 SAN 就是应该最先执行的操作。为什么 不投资在可以给多个系统带来益处的硬件上?本章后面将对此进行介绍。 3.1.33.1.33.1.33.1.3 可用的可用的可用的可用的 RAIDRAIDRAIDRAID 级别级别级别级别 当前几乎每个中等规模的服务器或企业级的服务器都提供了硬件 RAID 解决方案,这种解决方案内置 在服务器中,或者作为附加的存储设备。使用各种可用的 RAID 级别已经变成一个通用标准,而不管购买的 是什么类型的阵列。下文列出了 Oracle 数据库管理员需注意的一些较为常用的选项: ● RAID 0(分段集) 这个级别允许自动磁盘分段(striping)。这意味着 Oracle 的数据文件可以自 动扩展到多个磁盘上。表空间所对应的数据文件片断可以同时扩展到多个磁盘(而不是一个磁盘)上,并可 76 同时对它们进行访问(节省了大量的磁盘 I/O)。需要注意的是,这不是高可用性或容错的解决方案,因为 磁盘组中某个磁盘的丢失意味着需要恢复所有的数据。 ● RAID 1(镜像集) 现在多数系统都支持自动磁盘镜像。在操作系统里也经常用到这个技术,但用 在 Oracle 数据库中主要是想得到更高的可用性。相比于数据的数量,使用 RAID 1 级别需要两倍的存储空 间。 ● RAID 5(带有奇偶校验的分段集) 这个级别将奇偶检验块放到额外的磁盘上,这主要是为了媒介 恢复。有大量读操作的应用程序都可以从这种磁盘阵列分布中获得最大的性能。这是一个低成本的解决方 案,对于有大量写操作的 Oracle 应用程序,其效率并不高。下面的小节中将讨论这个 RAID 级别的改进方 案。 ● RAID 1+0(RAID 10,镜像的分段) 先镜像磁盘,然后对其进行分段。这是最常见的 Oracle OLTP 产品 RAID 级别,也称为 RAID TEN。它通过将 RAID 0 的磁盘 I/O 分段优势融入到 RAID 1 带来的镜像,结 合了前两个 RAID 级别的优点。在高读/写量的环境(如 OLAP)中,由于对数据的小规模访问会很频繁,我们 建议使用这个级别的 RAID。 ● RAID 0+1(RAID 01,分段的镜像) 先分段磁盘,然后对其进行镜像。该级别通常会与 RAID 10 混淆或者被认为不存在,它通过将 RAID 0 的磁盘 I/O 分段优势提供给 RAID 1 带来的镜像,结合了前两个 RAID 级别的优点。在高读/写量的环境(如 OLAP)中,由于对数据的小规模访问会很频繁,使用这个 RAID 级 别也很适合,但是它不如 RAID 10 健壮,并且不能容忍来自不同分段的两个磁盘同时产生故障。此外,在 产生故障之后的重建过程中,阵列中的所有磁盘都必须参与到重建过程中,这一点也不如 RAID 10 令人满 意。 ● RAID 1+0+0(RAID 100,RAID 10 的分段) 先镜像磁盘,然后对其进行分段,接下来再次分段(顶 层的分段通常使用软件进行操作,称为 MetaLun 或软分段)。这种 RAID 级别的优点主要是改进随机读的性 能和消除热点。 3.1.43.1.43.1.43.1.4 更新的更新的更新的更新的 RAID 5RAID 5RAID 5RAID 5 许多硬件供应商都使用 RAID 5 配置来配置系统,从而充分利用磁盘上的可用空间,并且减少阵列的 总体成本。尽管 RAID 5对于不昂贵的冗余是个很好的方案,但它对于写入密集型操作的性能来说通常较差。 一般来说,当对 RAID 5阵列发出一个写入请求时,必须改变磁盘上已修改的块,从磁盘上读取“奇偶校验” 块,并且使用已修改的块计算新的奇偶校验块,然后把数据写入到磁盘上。无论请求写入的数据为多少, 这个过程都会限制吞吐量,因为对于每一个写操作,都有至少两个额外的 I/O 操作。建议仅在一个文件系 统大部分进行的是读取操作或只读操作时使用 RAID 5。大多数存储器供应商都了解到这种奇偶校验写操作 对性能有一定影响,并且已经提出各种解决方案来减少这种额外操作带来的影响。最常见的解决方案是在 阵列上实现一个内存缓存,从而加速阵列上所有 I/O 的写性能。对于周期性的或少量的写活动,这种解决 方案可能完全适合于您的系统,但是您需要记住这些写操作最终都需要写入到磁盘。如果由于执行大量写 活动而使该磁盘缓存超载,则可能产生通常所说的“串行化 I/O”情况。在这种情况中,阵列无法足够快 速地写入磁盘以清除缓存,这就基本上抵消了缓存带来的优势。确保检查您的供应商可能实现的其他解决 方案,主动询问他们如何处理大量的 I/O。一些期待实现的解决方案如下: ● 动态缓存管理:这是阵列用于调整缓存使用方式的功能。一些供应商简单地将缓存对半划分—— 如果有 1GB 的缓存,则将 500MB 用于读,另外 500MB 用于写。因为 Oracle 缓冲区缓存基本上已经是读缓存, 并且能够调整阵列缓存,因此主要通过写缓存提供一些灵活性。这种解决方案也适用于 RAID 5之外的其他 配置。 ● 绑定的写操作:一般来说,写操作的最大尺寸大于 Oracle 块的尺寸。一些供应商已经在其阵列 中实现了智能化,从而这些阵列可以将多个奇偶校验操作分组到一个 I/O 操作中。因为需要较少的物理磁 盘往返操作,当运行 RAID 5 时,这种解决方案可以极大地改进缓存的性能和有效性。 77 RAID 6 是 RAID 5 的另一个变体,您可能已经通过广告看到过这种 RAID 级别。RAID 6 的运行方式类 似于 RAID 5,不同之处在于它利用每组数据块分段对应的奇偶校验块。虽然这种 RAID 级别带来了更多容 错的额外优势,但是您可能丢失两个磁盘,因此这种 RAID 级别具有甚至更低的发展潜力。 我仍然会优先选择 RAID 1+0(镜像和分段)。RAID 1+0 一般比 RAID 5 更为快速,或者至少一样快,并 且对于多种设备故障自然地具有更多的容错。您可能处于磁盘具有多个物理外壳的情况,因此使用分段和 镜像也可以在外壳之间构建容错。 3.23.23.23.2 安装和维护传统文件系统安装和维护传统文件系统安装和维护传统文件系统安装和维护传统文件系统 使用 RAID 配置的物理设备和传统文件系统组可以让 DBA 在执行 Oracle 数据文件安装和维护时变得更 为轻松,这是因为手动均衡磁盘已经不再繁琐。随着当前的存储系统中采用大容量的磁盘,将文件系统配 置划分为 4 到 6 个设备已经变成无意义的事情。除非使用涉及 12 个或更多物理磁盘的系统,否则将这些磁 盘划分为多个逻辑磁盘设备只会带来很少的优势。即使产生大量使用两个数据文件的情况,这些数据文件 在阵列上共享的缓存或主机总线适配器(Host Bus Adapter,HBA)也可能是常用的磁盘访问方法。最后,根 据预期的发展情况,您最终可以管理的文件系统数量会使创建的多个逻辑磁盘失去存在的意义。 技巧: 不要把磁盘阵列上的一个逻辑设备分割成两个文件系统。该操作看起来能够向您提供灵 活性,但它也会增加必须管理的数据文件位置的数量。 考虑成本考虑成本考虑成本考虑成本 为了支持镜像数据的磁盘阵列,需要较多(更多)的原始磁盘存储(对于 RAID 1,您可能需要双倍的磁 盘空间)。尽管这可能增加最初系统的价格,但同时优点也很明显。由于上述原因,在做出关于如何配置将 要购买的新存储系统的决定时,应仔细考虑保持该系统运行的投资回收率(Return On Investment,ROI) 以及改进性能的价值。这就将我们导向了另一类正在变得较为流行的存储系统。因为最基本的存储阵列的 容量在不断增大,公司正在寻求通过多节点访问技术利用存储空间。无论采用的实现方法是存储区域网络 (Storage Area Network,SAN)还是网络附加存储(Network-Attached Storage,NAS),您的存储系统能够 放入另一个服务器中的初始投资和额外收益通常都值得这样做。因此,当具有 4Gbit/sec 传输速度的光纤 通道存储阵列(带有 4 个磁盘)并且感觉没有充分利用该资源时,就可以考虑购买更多的基础设备,这样您 的企业就可以进一步发展,并且在所有重要的系统之间共享该资源。 技巧: 使用磁盘阵列可以提高系统性能,另外在磁盘出现故障时保护您的数据。使用适当的 RAID 级别和技术解决方案,这样就可以维持组织所需的可用性。不要认为已经足够好,这样 您就会在临晨两点时因为丢失某个磁盘而感到悔恨。 3.3 在硬件磁盘之间分布关键数据文件 为了更有效地在传统文件系统上操作 Oracle 数据库,一定要特别注意把关键的数据文件分布到可用 的文件系统里。例如,经常被访问的表应该放置在与相应的索引分开的文件系统上。另外,如果磁盘配置 允许 的话,联机重做日志和归档日志都应该与用来恢复的数据文件分开放置。事实是,在可以获得硬件的 78 大多数情况下,您需要确保不会因为过多地划分磁盘而影响到有 效使用磁盘的能力。除非在这些文件系统 下有许多设备,否则就需要做更多的工作。与如下元素关联的文件应该尽可能分离: ● SYSTEM 表空间 ● TEMPORARY 表空间 ● UNDO 表空间 ● 联机重做日志文件(最好放在最快的磁盘上) ● 操作系统磁盘 ● 放在 ORACLE_HOME 目录下的关键 Oracle 文件 ● 经常被访问的表的数据文件 ● 经常被访问的索引的数据文件 ● 归档区域(应该总是与将要恢复的数据分离) 下面的示例说明了在 UNIX 环境下跨 11 个文件系统的文件分布: /: Operating System /u01: Oracle software /u02: Temporary Tablespace, Control File 1 /u03: Undo Segments, Control File 2 /u04: Redo Logs, Archive Logs, Control File 4 /u05: System and SYSAUX Tablespaces /u06: Data1, Control File 3 /u07: Redo Log Mirror, Index3 /u08: Data2 /u09: Index2 /u10: Data3 /u11: Index1 3.3.13.3.13.3.13.3.1 分开存储数据和索引文件分开存储数据和索引文件分开存储数据和索引文件分开存储数据和索引文件 通常应该把所连接的表(在一个查询中同时访问的表)的数据和索引表空间分开放置。下面的示例显示 了一个表连接,以及一个管理数据的可行解决方案: select COL1, COL2, .... from CUST_HEADER, CUST_DETAIL where ...; 下面所示的为数据管理解决方案: Disk1: CUST_HEADER Table Disk5: CUST_HEADER Index Disk8: CUST_DETAIL Table Disk12: CUST_DETAIL Index 这个解决方案通过访问 4 个不同的磁盘和控制器来完成表的连接。把数据和索引文件分别放置在不同 的物理磁盘设备和控制器上;这样,当表和索引同时被访问时,就不会访问同一个物理设备。当然还可以 79 扩展到更多的磁盘上。本章的后面将会介绍到,表和索引分区可以帮助我们更容易地完成这项任务。 技巧: 把关键的 Oracle 数据文件分开放置,这样可以避免磁盘争用成为一个“瓶颈”。通过把 经常连接的几个表的表和索引分开放置,可以保证即使是最糟糕的表连接也不会导致磁 盘争用。 3.3.23.3.23.3.23.3.2 避免避免避免避免 I/OI/OI/OI/O 磁盘争用磁盘争用磁盘争用磁盘争用 磁盘争用通常发生在有多个进程试图同时访问一个物理磁盘的情况下。把磁盘的 I/O 更平均地分布到 多个可用的磁盘上,这样可以有效地减少磁盘竞用,同时也提高了性能。减少磁盘 I/O 也可以减少磁盘争 用。要监控磁盘争用,可以查看数据库控制中的数据库文件度量(Database Files Metrics)。该度量组包 含两组度量:文件平均读取时间(Average File Read Time)和文件平均写入时间(Average File Write Time) 度量应用于与数据库关联的所有数据文件。如果发现一个或两个数据文件看起来具有特别高的值,则可以 单击相应的数据文件,然后使用比较对象文件名(Compare Object File Name)链接以查看收集的这些数据 文件之间的统计。如果这些数据文件同时繁忙,并且位于相同的磁盘上,则出于改进这段时间内的性能的 考虑,可以选择将一个数据文件重新放置到另一个文件系统。 也可以选择通过运行如下查询来确定文件 I/O 问题: col PHYRDS format 999,999,999 col PHYWRTS format 999,999,999 ttitle "Disk Balancing Report" col READTIM format 999,999,999 col WRITETIM format 999,999,999 col name format a40 spool fio1.out select name, phyrds, phywrts, readtim, writetim from v$filestat a, v$datafile b where a.file# = b.file# order by readtim desc / spool off 下面是部分的查询输出: Fri Mar 24 page 1 Disk Balancing Report NAME Phyrds Phywrts ReadTim WriteTim /d01/psindex_1.dbf 48,310 51,798 200,564 903,199 /d02/psindex_02.dbf 34,520 40,224 117,925 611,121 /d03/psdata_01.dbf 35,189 36,904 97,474 401,290 /d04/undotbs01.dbf 1,320 11,725 1,214 39,892 /d05/system01.dbf 1,454 10 10 956 80 ... 注意: 您也可能有 sysaux01.dbf、users01.dbf 和 example01.dbf。 在磁盘上的物理写入和读取次数上如果出现很大的差别,就表明肯定有哪个磁盘负载过多。在前面的 示例中,文件系统 1-3 都被经常使用,而文件系统 4-5 仅被少量使用。为了获得一个较好的均衡,可以移 动一些数据文件。把数据文件分布在多个磁盘上,或者使用分区,都可以帮助您把对表或索引的访问移动 到另外一个磁盘上。 技巧: 查询 V$FILESTAT 和 V$DATAFILE,以查看到均衡数据文件后的效果。注意到使用 V$TEMPFILE 和 V$TEMPSTATS 监控临时表空间。 3.3.33.3.33.3.33.3.3 通过移动数据文件来均衡文件通过移动数据文件来均衡文件通过移动数据文件来均衡文件通过移动数据文件来均衡文件 I/OI/OI/OI/O 可以按照下面的步骤来物理移动一个导致文件争用的数据文件: (1) 使与数据文件有关的表空间脱机: ALTER TABLESPACE ORDERS OFFLINE; (2) 把数据文件复制到磁盘的新位置上: $cp /disk1/orders1.dbf /db2/orders1.dbf (UNIX 的复制命令) (3) 用新数据文件位置为表空间重命名数据文件: ALTER TABLESPACE ORDERS RENAME DATAFILE ′/disk1/orders1.dbf′ TO ′/disk2/orders1.dbf ′; (4) 使表空间重新联机: ALTER TABLESPACE ORDERS ONLINE; Delete the old data file (when sure the moved data file can be accessed): $rm /disk1/orders.dbf (UNIX delete command) 技巧: 通过把数据文件移到不经常被访问的磁盘上,可以有效解决磁盘争用的问题。 通常用于大型的关键文件的另一种方法遵循如下步骤: (1) 将表空间置于 READ ONLY 模式,通过查询 DBA_TABLESPACES 确认该状态。 81 (2) 在 OS 级别上复制数据文件。比较复制操作后的文件尺寸以确保具有相同的尺寸。 (3) 将表空间改为脱机。 (4) 使用 ALTER TABLESPACE 命令重命名数据文件。 (5) 将表空间改回联机。 (6) 将表空间改为 READ WRITE。 (7) 通过查询 V$DATAFILE 确认控制文件是更新的。 (8) 在 OS 级别上删除旧的数据文件。 技巧: 使用Enterprise Manager 中的数据库文件度量可以确定发生在每个数据库文件上的I/O。 将大量使用的数据文件移动到单独的文件系统以分布 I/O。 3.43.43.43.4 本地托管的表空间本地托管的表空间本地托管的表空间本地托管的表空间 在 Oracle 8i 之前,所有表空间段上的盘区信息都通过 Oracle 数据字典来维护。这样,发生在数据 库的段上并关系到盘区分配(extent allocation)的操作,比如扩展或截取一个表,将会导致对数据字典的 操作。这从数据库管理的角度来看代价就显得太高了,这是因为如果有很多带有很多盘区的表被操作时, 数据字典将会成为这些操作的瓶颈。Oracle 8i 推出了一个新的盘区管理选项,叫作本地托管的盘区。通 过本地托管的盘区,这些盘区管理操作都会被重新分配到数据文件头中的位图块上。这同时也提高了性 能, 原因是数据库的每个表空间都只包含自己的盘区信息,可以使用快速散列进程访问该信息,而不是使用较 慢的、基于表的查询访问。 使用本地托管的表空间特性时,除了在字典托管表空间中可用的传统“用户”托管盘区定义之外, Oracle 还提供了将盘区分配到段中的两个选项,即“自动分配”和“统一”选项。在自动分配管理模式下, 数据库使用一个内部算法,在段大小增长时增加盘区的尺寸。使 用自动分配选项,当表空间中的段增长时, 数据库使用一个内部算法来确定适当的下一盘区尺寸,该内部算法以盘区数量和扩展比例作为系数进行计 算。该选项的优 点是,如果表已经被定义为具有不适当的较小盘区尺寸,数据库将自动增加表的下一盘区 尺寸,并且因此减少表具有的全部盘区数量。因此,如果正在操作新的应用 程序,并且不确定段增长的数 量,则使用自动分配选项可以确保段的数量不会超出控制范围。 在统一的盘区管理中,表空间中的所有盘区都使用在创 建表空间时指定的相等大小进行分配,而不 考虑在段创建语句中指定的存储子句。尽可能优先使用统一选项,主要的原因是在移动或删除段时可以更 为有效地重用表 空间中的空闲盘区,因为它们对于剩余的段已经具有适当的尺寸。我们将在本章后面介绍 到减少碎片部分时详细讨论这点。 3.4.13.4.13.4.13.4.1 创建本地托管的表空间创建本地托管的表空间创建本地托管的表空间创建本地托管的表空间 要创建一个本地托管的表空间,可以在 CREATE TABLESPACE 语句中使用盘区管理子句,并设定如何分 配盘区的模式: 82 CREATE TABLESPACE USER_256K_DAT datafile '/u01/user_256k_dat_01.dbf' SIZE 100M extent management local uniform size 256K; 在使用统一盘区创建本地托管的表空间时,建议在表空间名称中指定盘区的尺寸。这样可以很容易地确定 表空间上的盘区尺寸,并且可以很容易地确定该把这个段移动或创建到哪个表空间上。 在本地托管的表空间中创建段时,不要指定定义参数的存储子句,例如 INITIAL、NEXT 和 PCTINCREASE。 这些参数可能会导致产生混淆,因为是在 dba_segments 字典表中跟踪这些参数,而在如何分配盘区时会忽 略它们。下面的查询显示了如何得到数据库中更多关于表空间的盘区管理的信息,它可以帮助归档您所具 有的段: SELECT tablespace_name, extent_management, allocation_type from dba_tablespaces; TABLESPACE_NAME EXTENT_MAN ALLOCATION ------------------ ---------- ---------- SYSTEM DICTIONARY USER TOOLS LOCAL UNIFORM TEMP LOCAL UNIFORM USERS LOCAL UNIFORM TS_SMALL_64K_DAT LOCAL UNIFORM 默认的数据库安装将如下表空间的 allocation_type 设置为 SYSTEM:SYSTEM、USERS、SYSAUX、EXAMPLE 和 UNDOTBS1。Oracle 9.2 版本中引入了本地托管的 SYSTEM 表空间。在 10.1 版本中,如果使用 CREATE DATABASE 命令手工创建一个数据库,默认情况下是创建 SYSTEM 的字典托管表空间。如果使用 DBCA 通过模 板创建新的数据库,则 LOCALLY MANAGED 值就是所有表空间(包括 SYSTEM 表空间)的存储(Storage)选项卡 中的默认值。 3.4.23.4.23.4.23.4.2 把字典托管的表空间迁移到本地托管的表空间把字典托管的表空间迁移到本地托管的表空间把字典托管的表空间迁移到本地托管的表空间把字典托管的表空间迁移到本地托管的表空间 可以把字典托管的表空间迁移到本地托管的表空间,但 我们不建议这样做。当表空间迁移为本地托 管的形式时,盘区映射就移入表空间数据文件头中,但是不能使用自动分配或统一盘区尺寸管理,从而无 法有效地减少碎 片,因为现在必须为表空间中的每个段指定存储子句。盘区位图放置在先前由文件中第一 个空闲盘区的开始地址占用的位置中。用户不会从迁移中获得策略方面的优 势,但却可以获得性能方面的 优势:没有 ST(空间事务)入列争用和更为有效的盘区操作。 只要可能,您都可以用下面的方法把一个字典托管的表 空间重建为本地托管的统一盘区模式:导出 表空间中的段,删除并重新创建表空间,然后再导入段。在执行这个过程之前,先检查一下段的大小。最 好把一个单独的 表空间分割成有着不同大小的盘区的表空间,这样可以容纳不同大小的段。 对于应该迁移为本地托管形式的表空间,唯一的例外情况是 SYSTEM 表空间。虽然首选的方法仍然是 使 用 导 出 / 导 入 重 新 构 建 数 据 库 ,但有时不能使用该方法 。 在 10g 版 本 中 , 可 以 使 用 DBMS_SPACE_ADMIN.TABLESPACE_MIGRATE_TO_LOCAL 程序包迁移 SYSTEM 表空间。在执行该操作之前,需要 注意如下的一些限制条件: ● 数据库中所有用户的默认临时表空间必须是不同于 SYSTEM 的表空间。 ● 必须将计划进行读/写转换的所有表空间迁移到本地托管的表空间。 83 ● 必须以受限制的模式启动数据库。 ● 必须有所有表空间(除了用于转换的、处于只读模式的撤消表空间),或者有在本地托管表空间中 定义的联机回滚段。 3.4.33.4.33.4.33.4.3 Oracle Oracle Oracle Oracle 大文件表空间大文件表空间大文件表空间大文件表空间 Oracle 10g 为超大型数据库引入了新的本地托管表空间类型。大文件表空间允许用户创建只有一个文 件的表空间,该数据文件的大小完全地结合了 64 位系统的能力。如果与 Oracle 托管文件或自动存储管理 一起实现,大文件表空间可以极大地简化存储系统的管理工作。此外,大文件表空间只有相当少的数据文 件,因此可以改进数据库管理操作(例如检查点)的性能,但需要注意的是,如果发生数据文件破坏,则恢 复操作将需要较长的时间。 现在,您可能会问:“大文件表空间的优点是什么?”具有典型 8K 块的大文件表空间可以包含一个 32TB 的数据文件。如果使用 32K 的块,则可以包含一个 128TB 的数据文件。实现包含大容量数据文件的方 法是改变表空间中 ROWID 的管理方式。在传统的表空间中,使用 ROWID 中的 3 个位置来标识行的相对文件 数。在大文件表空间中只有一个数据文件,因此这 3 个位置被改为用于延长行的数据块编号,从而可以使 用比传统小文件表空间多很多的大量 ROWID。 注意: 为了拥有最大可以为 8EB 的 Oracle 数据库,必须使用 128T 的数据文件。 使用大文件表空间的必要条件是,用户必须结合使用本地托管的表空间和自动段空间管理(ASSM)。同 样,不可以将大文件表空间用作 UNDO、TEMP 或 SYSTEM 表空间。如果用户考虑使用大文件表空间减少系统 管理操作的数量,则也要考虑使用 Oracle 托管文件(OMF)和 ASM(本章后面将介绍)。此外,如果使用传统 的文件系统,则要确保使用逻辑卷管理器,该管理器提供了合理安排存储系统的灵活性,从而单个的数据 文件可以根据需要增长。 3.4.43.4.43.4.43.4.4 Oracle Oracle Oracle Oracle 托管文件托管文件托管文件托管文件 Oracle Database 9i 版本中第一次引入了 Oracle 托管文件(OMF)的特性。该特性的目标是使 DBA 不再 需要直接管理属于数据库的数据文件的名称。虽然 Oracle 托管文件听起来确实很好,但它具有一些初始的 限制,从而显得不那么理想。 如果您有针对所有数据库相关文件的大文件系统,则可以使用 OMF。为了实现 OMF,首先需要为您的数 据库指定一些初始参数。 DB_CREATE_FILE_DEST 该参数定义了新的数据文件、临时文件、重做日志文件、控制文件以及任何块改动跟踪文件的默认位置。 DB_RECOVERY_FILE_DEST 该参数定义了重做日志、控制文件、RMAN 备份文件、归档日志和闪回日志的默认位置。该参数将重写 这些常见文件类型先前的参数。 84 DB_CREATE_ONLINE_LOG_DEST_n 该参数定义重做日志文件和控制文件的默认位置,并且重写这些文件类型先前的两个参数。和往常一样, 我们建议您指定两个位置,以确保有归档日志和控制文件的多个副本。多次指定该参数以设置镜像位置。 现在,当您需要添加数据文件时,可以简单地运行“ALTER TABLESPACE. . .ADD DATAFILE”命令。 如果需要创建新的表空间,可以只运行“CREATE TABLESPACE”命令,而完全不需要指定涉及的实际数据文 件,因为数据库将自动执行该操作。 例如,如果您有 6 个用于数据库的文件系统,为了将每个文件系统用于不同的文件,您需要在将数据 文件添加到特定表空间时标识所需的文件系统,如下所示: SQL> ALTER SYSTEM SET DB_CREATE_FILE_DEST = '/u01/oradata'; SQL> CREATE TABLESPACE tbs_1; 该方法的优点是,您不再需要担心会创建已经存在的文件。但是,该方法没有提供容量管理或平衡 I/O 方面的优点。为了通过 OMF 获得这种平衡,我们需要了解 Oracle 提供的另一项技术:ASM。 3.53.53.53.5 ASM ASM ASM ASM 简介简介简介简介 在深入介绍 ASM 的复杂内容之前,首先需要感谢 Oracle 公司的 Nitin Vengurlekar,他负责编写了本 章中关于 ASM 的优秀补充内容。 在 Oracle Database 10 g Release 2 中,使用自动存储管理(Automatic Storage Management,ASM) 极大地简化了数据库的存储管理和配置。ASM 提供了内置于 Oracle 数据库内核中的文件系统和卷管理器功 能。通过这些功能,ASM 简化了各种存储管理任务,例如创建/布置数据库和磁盘空间管理。ASM 允许用户 使用熟悉的 CREATE/ALTER/DROP SQL 语句执行磁盘管理,因此 DBA 不需要学习新的技术集,也不需要进行 关键的配置决策。不熟悉 SQL 的 ASM 管理员也可以使用企业管理器界面(查看第 5 章以了解更多相关信息) 以及新的命令行实用程序(Oracle Database 10 g Release 2 中的新增功能)。 ASM 是专门构建用于简化 DBA 工作的管理工具,它提供了跨越所有服务器和存储平台的简单存储管理 界面。ASM 为 DBA 提供了管理动态数据库环境的灵活性,并且可以有效地提高效率。该特性是网格计算和 数据库存储合并的关键组成部分。 下面是 ASM 的一些主要优点: ● 将 I/O 均匀地分布到所有可用磁盘驱动器以防止产生热点,并且最大化性能。 ● 不再需要过多地进行配置工作,并且最大化推动数据库合并的存储资源利用。 ● 内在地支持大文件。 ● 在增量增加或删除存储容量后执行自动联机重分配。 ● 维护数据的冗余副本以提供高可用性,或者利用第三方的 RAID 功能。 ● 支持 Oracle Database 10 g 以及 Oracle Real Application Clusters(RAC)。 ● 可以利用第三方的多路径技术。 为了更加简单而方便地迁移到 ASM,Oracle Database 10 g Release 2 数据库可以包含 ASM 文件和非 ASM 文件。任何新的文件都可以创建为 ASM 文件,同时已有的文件也可以迁移到 ASM。可以使用 Oracle Database 10g 企业管理器管理 ASM 磁盘和文件管理活动。 85 ASM 降低了 Oracle Database 10g 的成本和复杂性,并且不会影响到性能或可用性。此外,ASM 完全 不涉及存储方面的内容;因此,ASM 使用多种存储阵列,其范围从高端的存储阵列(例如 EMC DMX 和 HDS) 到低成本的常用阵列(例如 Apple XServe)。构建 ASM 的主要用途是解决数据库的配置和布局以及 IT 角色 之间的通信。 在部署和创建数据库之前,DBA 必须考虑和确定如下事项: ● 计划文件系统布局和设备使用情况。 ● 确定应用程序工作量特征(OLTP 的随机读/写对 DDS 系统的连续 I/O)。 ● 计算存储容量并调整数据库的大小。 ● 过去,DBA 会创建文件系统以存储他们的数据库文件,并且根据需要创建额外的文件系统。这种 方法可能很难管理和配置,因为 DBA 必须管理每个文件系统上的 I/O 负载。ASM 提供了单一的存储池(磁盘 组),因此不需要维护多个文件系统容器,并且不需要担心下一个数据文件的放置。 ● ASM 的一个核心优点是能够扩展存储以满足应用程序的容量需求。因此,可以扩展驻留数据库的 ASM 磁盘组,而不需要过多地担心存储容量管理问题。 ● 通过使用 ASM 并应用已定义的一般性最佳实践,基于 ASM 的数据库应该能够处理任何工作量。此 外,ASM 内在地使用裸设备,因此不需要考虑异步 I/O 或直接 I/O 等问题。 3.5.13.5.13.5.13.5.1 I/O I/O I/O I/O 角色之间的通信角色之间的通信角色之间的通信角色之间的通信 DBA、系统管理员和存储管理员之间有时不能保持密切的联系。DBA 请求 200GB 的文件系统,而存储/ 系统管理员却提供了 200GB 的 RAID 5 设备或 RAID 10 设备,该设备有不适当的或无效的数据条大小,并且 会影响到性能。DBA 在后面的工作中发现了该设备的实际配置,从而显得非常不高兴。 DBA 和其他技术 IT 角色总是会有一些内在的联系中断,因为这些角色组有不同的思考和运作方式。这 主要是一种通信问题,因此 ASM 不一定需要修正这种联系中断情况。然而,可以使用 ASM 附带的一些功能 来 减 少 这 种 通 信 问 题。 首 先,Oracle 发布了名为“方便地实现最优存储配置” 的 论 文( 可 以 在 technet.oracle.com 上获得该论文),该论文中提出了 S.A.M.E(Stripe-and-Mirror-Everything)方法。这 篇论文为数据库部署提供了标准的方法论,这就使 DBA 和存储管理员之间可以更为方便地进行通信,因为 DBA 现在可以通过某种方法来表达他们所需的内容。 ASM 结合了 SAME 方法论的所有基本要点,并且为存储容量管理提供了流线化的方法。通过 ASM,可以 按照业务计划或容量计划的指示扩展数据库存储,所有这些操作都不会使应用程序停止运作。 3.5.23.5.23.5.23.5.2 ASM ASM ASM ASM 实例实例实例实例 在 Oracle Database 10g 中,有两种类型的实例:数据库实例和 ASM 实例。ASM 实例一般命名为+ASM, 并且以 INSTANCE_TYPE=ASM 初始化参数作为开始。如果设置该初始化参数,则可以通过信号通知 Oracle 初 始化例程启动 ASM 实例,而不是启动标准的数据库实例。不同于标准的数据库实例,ASM 实例不包含任何 物理文件,例如日志文件、控制文件或数据文件,并且在启动时只需要很少的一些初始化参数。 ASM 实例在启动时将产生所有的基本后台进程,并且产生一些新的后台进程,这些进程专门用于 ASM 操作。ASM 实例的 STARTUP 子句类似于数据库实例的 STARTUP 子句。例如,使用 NOMOUNT 选项可在不安装 任何磁盘组的情况下启动 ASM 实例,而使用 MOUNT 选项则会安装所有已定义的磁盘组。 86 ASM 是在给定节点上使用 ASM 的所有数据库的卷管理器。因此,不论节点上有多少个数据库实例,每 个节点只需要一个 ASM 实例。此外,ASM 无缝地使用 RAC 体系结构以支持集群存储环境。在 RAC 环境中, 每个集群节点只有一个 ASM 实例,并且 ASM 实例使用互连的方式在对等的基础上进行通信。下面的示例查 询连接的实例名: select instance_name from v$instance; INSTANCE_NAME ---------------- +ASM 3.5.33.5.33.5.33.5.3 ASM init.ora ASM init.ora ASM init.ora ASM init.ora 参数参数参数参数 下面的列表显示了启动 ASM 所需的一些基本的 init.ora 参数。观察该列表可以了解到,所有 ASM 进 程都以 asm 开头,而数据库实例的进程则以 ora 开头。 *.background_dump_dest='/opt/app/admin/+ASM/bdump' *.core_dump_dest='/opt/app/admin/+ASM/cdump' *.instance_type=asm *.asm_diskgroups=+DATA *.large_pool_size=12M *.asm_diskstring='/dev/rdsk/ c3t19 d*s4' *.remote_login_passwordfile='SHARED' *.user_dump_ 3.5.43.5.43.5.43.5.4 ASM ASM ASM ASM 的安装的安装的安装的安装 如果单个的 ASM 实例只管理一个数据库实例,则只要为 ASM 和数据库维护一个 ORACLE_HOME 即可。然 而,对于具有管理多个数据库实例存储的 ASM 实例并且需要较高可用性的系统来说,推荐的方法是在单独 的 ORACLE_HOME (ASM_HOME)中安装 ASM 实例,而不是在数据库 ORACLE_HOME 中进行安装。该方法也允许用 户独立于 Oracle Database 修补 ASM。 在 Oracle Database 10 g Release 2 中,对 Oracle 全局安装程序(Oracle Universal Installer, OUI)和数据库配置助手(DBCA)进行了增强,从而允许用户在单独的 ORACLE_ HOME 中无缝地创建和安装 ASM 实例。OUI 现在具有如下选项: ● 安装和配置使用 ASM 进行存储管理的数据库。 ● 在不创建数据库的情况下安装和配置 ASM 实例。 ● 在已经有运行数据库的系统上安装和配置 ASM,DBA 随后可以在该系统中使用 EM 迁移实用程序 (Migration Utility)将数据库迁移到 ASM。 此外,在 Oracle Database 10g Release 2 中,ASM 明显地支持数据库的早期软件版本和较新的软件 版本。这种新的特性提供较高的可用性以及数据库存储合并特性(Database Storage Consolidation Feature)(本章后面将对此进行介绍)的基础。 87 3.5.3.5.3.5.3.5.5555 ASM ASM ASM ASM 参数和参数和参数和参数和 SGASGASGASGA 调整调整调整调整 启用 ASM 实例只需要配置一些 init.ora 参数。ASM 的参数文件可以是 Pfile 或 Spfile。默认情况下, DBCA 将在数据库/ASM 创建期间创建 ASM Pfile,用户可以在后面根据需要手工配置 Spfile。然而,如果在 集群 ASM 环境中使用 Spfile,则该文件必须位于共享的裸设备上。下面指定的 init.ora 参数是启动 ASM 所需的基本参数。 下面描述 SGA 组件的用途: ● db_cache_size:该组件的值确定高速缓存的大小。这个缓冲区高速缓存区域用于缓存元数据块。 默认值将适合于大多数的实现。 ● shared_pool:该组件用于管理实例的标准内存使用(控制结构等),也用于存储盘区图。默认值 将适合于大多数的实现。 ● large_pool:用于大型的分配。默认值将适合于大多数的实现。 可能需要修改 ASM 的“进程”init.ora 参数。下面的推荐修改方法适合于 Oracle 的 10.1.0.3 版本和 以后的版本,并且将作用于 RAC 和非 RAC 系统。可以使用下面的公式确定参数的最佳值:25+15n,其中 n 是使用 ASM 作为其存储的数据库的数量。 3.5.63.5.63.5.63.5.6 ASM ASM ASM ASM 和权限和权限和权限和权限 ASM 实例的访问权限类似于标准实例的访问权限,即 SYSDBA 权限和 SYSOPER 权限。然而,需要注意的 是,因为没有任何数据字典,所以通过操作系统级和/或 Oracle 密码文件完成验证。一般情况下,通过使 用操作系统组授予 SYSDBA 权限。在 Unix 操作系统中,该操作系统组一般是 dba 组。默认情况下,dba 组 的成员有当前节点上所有实例(包括 ASM 实例)的 SYSDBA 权限。使用 SYSDBA 权限连接 ASM 实例的用户具有 对该系统中所有磁盘组的全部管理性访问权限。ASM 实例中支持 SYSOPER 权限,该权限将可允许的 SQL 命 令集限制为对已配置系统执行基本操作所需的最小 SQL 命令集。SYSDBA 用户可以使用如下的命令: ● STARTUP/SHUTDOWN ● ALTER DISKGROUP MOUNT/DISMOUNT ● ALTER DISKGROUP ONLINE/OFFLINE DISK ● ALTER DISKGROUP REBALANCE ● ALTER DISKGROUP CHECK ● 访问所有 V$ASM_*视图 其他所有命令,例如 CREATE DISKGROUP、ADD/DROP/RESIZE DISK 等,需要具有 SYSDBA 权限,而具有 SYSOPER 权限的用户则不允许执行这些命令。 3.5.73.5.73.5.73.5.7 ASM ASM ASM ASM 磁盘磁盘磁盘磁盘 构建 ASM 基础结构的第一项任务是在 ASM 管理中发现和关联(添加)磁盘,最好是由存储管理员和系统 管理员协调工作来完成该步骤。存储管理员将标识存储阵列提供给主机的一组磁盘。术语“磁盘”可能有 多种含义。磁盘可以是物理主轴的一个分区、整个主轴或 RAID 组集(在存储阵列中定义);这取决于存储阵 列将逻辑单元号(Logical Unit Number,LUN)提供给操作系统(OS)的方式。在本章中,我们将提供给 OS 的 LUN 或磁盘统一简称为磁盘。在 Solaris 系统中,磁盘一般具有如下的 SCSI 名称格式:CwTxDySz,其中 C 88 是控制器号,T 是目标,D 是 LUN/磁盘号,S 是分区。注意,每个 OS 都有其独特的 SCSI 磁盘命名表示法。 ASM 必须只使用字符设备作为磁盘,而不能使用块设备。根据 Oracle 10.2 版本的管理指南,用户可 以使用 ASMLib 访问块设备:“ASMLib 是 ASM 的可选附件,它的目标是为 ASM 支持的内核提供发现和访问块 设备的备选接口”。在大多数 Unix 系统上,字符设备显示为/dev/rdsk,而在 Linux 上则显示为/dev/raw/raw, 唯一的例外情况是在 ASM 使用 NAS 文件系统文件作为磁盘时。 例如,Solaris 系统上的最佳方法是在磁盘上创建分区,例如创建 4 个或 6 个分区,并且跳过磁盘中 最初的 1MB 空间。创建分区操作服务于多个目标。分区创建占位符以标识正在使用该磁盘,而未分区的磁 盘可能会无意中被误用或覆盖。跳过磁盘中的 1MB 空间是为了跳过 OS 标号/VTOC(内容的卷表),并且保存 ASM 条带和存储阵列内部条带之间的对齐方式。不同的操作系统将有不同的 OS 标号需求;也就是说,一些 操作系统在使用之前可能需要 OS 标号,而其他操作系统则不需要。尽管过程可能有所不同,但是不同的操 作系统应用相同的基本原理。 在 SAN 环境中,假设已经标识和配置了磁盘;也就是说,这些磁盘在 SAN 结构中被适当地分区或添加 LUN 标记,并且 OS 可以查看这些磁盘。一旦进行标识,ASM 就需要发现这些磁盘。这就需要将磁盘设备(Unix 文件名)的所有权从 root 改为拥有 ASM 软件的 OS 用户(例如 oracle)。 贯穿本章的运行示例由磁盘组 DATA 组成,它将用于过程化地概述创建磁盘组和其他 ASM 对象的步骤。 在我们的示例中,标识磁盘 c3t19d5s4、c3t19d16s4、c3t19d17s4、c3t19d18s4,并且将这些磁盘的所有 权设置为正确的“oracle:dba”所有权。现在可以在 init.ora 参数 asm_diskstring 中定义这些磁盘。在 我们的示例中,将使用如下的通配符设置: *.asm_diskstring='/dev/rdsk/c3t19d*s4'. 当 ASM 扫描磁盘时,它将使用该字符串并查找有权打开的任何设备。如果成功地发现相应的设备,ASM 实例上的 V$ASM_DISK 视图将会反映发现的磁盘。在此以后需要注意的是,如果不做特别声明,则通过 ASM 实例而不是数据库实例检查所有视图。在下面的示例中,注意到 NAME 列为空,并且 group_number 被设置 为 0。已经发现但还没有与磁盘组关联的磁盘具有空的名称和值为 0 的 group_number。 select name, path, group_number from v$asm_disk; NAME PATH GROUP_NUMBER ----------------------------- -------------------- ------------ /dev/rdsk/c3t19d5s4 0 /dev/rdsk/c3t19d16s4 0 /dev/rdsk/c3t19d17s4 0 /dev/rdsk/c3t19d18s4 0 磁盘有反映其对于磁盘组的成员关系情况的各种头状态,可选的头状态如下所示: ● 前任(Former):该状态声明磁盘以前是磁盘组的一部分。 ● 候选(Candidate):当磁盘处于该状态时,指示可以将该磁盘添加到磁盘组。 ● 成员(Member):该状态指示磁盘已经是磁盘组的一部分。 ● 预备(Provisioned):该状态类似于候选者状态,磁盘组可以获得处于该状态的磁盘。 89 然而,预备状态指示该磁盘已经经过配置,或者可以使用 ASMLib 获得。ASMLib 是 ASM 的支持库。通 过 ASMLib,使用 ASM 的 Oracle 数据库可以更为有效,并且能够访问正在使用的磁盘组。 在 RAC 环 境 中,各个节点上可能有不同的磁盘路径。 例 如, 节 点 1 将 有 指 向 某个 磁 盘 的 /dev/rdsk/c3t1d4s4,而节点 2 将对相同的设备显示/dev/rdsk/c4t1d4s4 路径。这是普遍存在的情况,不 应该将其视为问题。ASM 不需要磁盘在每个节点上有相同的名称。然而,ASM 需要每个 ASM 实例都可以通过 实例的发现字符串查找到相同的磁盘。根据需要,ASM 实例可以有不同的发现字符串。 3.5.83.5.83.5.83.5.8 ASM ASM ASM ASM 和多路径和多路径和多路径和多路径 I/O 路径一般由启动器端口、结构端口、目标端口和 LUN 组成。I/O 路径的每种排列方式都被认为是 一个独立的路径。动态多路径/故障恢复工具将这些独立的路径聚集为单个的逻辑路径。这种路径抽象提供 了多个主机总线适配器(Host Bus Adapter,HBA)之间的 I/O 负载平衡以及产生 I/O 路径故障时不中断的故 障恢复。多路径(MP)软件需要所有必需的磁盘在每个可用的、符合条件的 HBA 上可见。MP 驱动程序将通过 执行 SCSI 查询命令检测多个路径。多路径软件也提供多路径软件驱动程序。为了支持多路径,实际的 HBA 驱动程序必须服从 MP 驱动程序提供的多路径服务。确保正在考虑的配置经过供应商的证明。多路径工具提 供了如下的优点: ● 为多路径的 LUN 提供了单个的块设备接口。 ● 检测 I/O 路径中的任何组件故障,例如结构端口、通道适配器或 HBA。 ● 发生路径丢失故障时,确保 I/O 重新路由到可用的路径,而不会产生进程中断。 ● 在事件发生时自动重新配置多个路径。 ● 确保尽可能使产生故障的路径重新生效,并且提供自动恢复(failback)功能。 ● 配置多个路径,从而使用各种负载平衡方法最大化性能,例如循环复用(round robin)、最少 I/O 队列或最少服务时间。 多路径软件的示例包括 EMC PowerPath、Veritas DMP、Sun Traffic Manager、Hitachi HDLM 和 IBM SDDPCM。Linux 2.6 有一个基于内核的多路径驱动程序,称为 Device Mapper。此外,一些 HBA 供应商也提 供了多路径解决方案。Oracle 公司没有确认或证明这些多路径工具。 技巧: 虽然 ASM 不提供多路径功能,但是 ASM 可以使用多路径工具,前提是该多路径工具产生 的路径或设备通过 fstat 系统调用返回一个成功的返回码。Metalink Note 294869.1 提 供了关于 ASM 和多路径的更多详细信息。 3.5.93.5.93.5.93.5.9 ASM ASM ASM ASM 磁盘组磁盘组磁盘组磁盘组 一旦发现了磁盘,就可以创建封装一个或多个这些磁盘的磁盘组。磁盘组是 ASM 中最高级别的数据结 构,它类似于 LVM 的卷组。然而,典型的 LVM 卷组和 ASM 磁盘组之间存在一些区别。磁盘组中会隐式地创 建 ASM 文件系统层,该文件系统对于用户是透明的,只可以通过 ASM、连接的数据库以及 10.2 版本中的 ASM 命令行工具访问,并且内在地具有自动化文件级数据分条和镜像功能。在 ASM 磁盘组中创建的数据库文件 将其文件盘区(不要与数据库盘区混淆)平均地分布到磁盘组中的所有联机磁盘,从而提供均匀的 I/O 负载。 磁盘组的创建涉及对将要添加的磁盘进行确认,这些磁盘必须有如下的属性: 90 ● 当前没有被另一个磁盘组使用。 ● 必须没有预先存在的 ASM 头。 ● 不能有 Oracle 文件头。 这种检查和确认方法可防止 ASM 销毁任何当前正在使用的数据设备。只有处于有效头状态(候选、前 任或预备)的磁盘才可以成为磁盘组的成员。使用如下的 SQL 命令创建磁盘组: create diskgroup DATA external redundancy disk '/dev/rdsk/c3t19d5s4', '/dev/rdsk/c3t19d16s4', '/dev/rdsk/c3t19d17s4', '/dev/rdsk/c3t19d18s4'; 来自于 V$ASM_DISKGROUP 的如下输出显示了最近创建的磁盘组: select name, state, type, total_mb, free_mb from v$asm_diskgroup; NAME STATE TYPE TOTAL_MB FREE_MB ----------------- ----------- ------ ---------- ---------- DATA MOUNTED EXTERN 34512 34101 在前面的示例中,使用 4 个磁盘创建 DATA 磁盘组,这些磁盘驻留在存储阵列中,通过该存储阵列在 外部处理冗余。下面的查询显示了 V$ASM_DISK 视图如何反映磁盘在合并到磁盘组中之后的状态改变。 SQL> select name, path, mode_status, state, disk_number from v$asm_disk NAME PATH MODE_ST STATE DISK_NUMBER ------------ -------------------- ------- -------- ----------- DATA_0000 /dev/rdsk/c3t19d5s4 ONLINE NORMAL 0 DATA_0001 /dev/rdsk/c3t19d16s4 ONLINE NORMAL 1 DATA_0002 /dev/rdsk/c3t19d17s4 ONLINE NORMAL 2 DATA_0003 /dev/rdsk/c3t19d18s4 ONLINE NORMAL 3 ASM 将对添加到磁盘组的每个磁盘生成和分配带有序号的磁盘名。然而,如果省略 NAME 子句并且通过 ASMLib 分配标号给磁盘,则使用该标号作为磁盘名。如果省略 NAME 子句并且没有通过 ASMLib 分配标号, 则自动存储管理(Automatic Storage Management)会创建一个默认的名称,其形式为diskgroup_name_####, 其中的####是磁盘号。在执行任何磁盘管理活动时都会使用磁盘名。ASM 磁盘名不同于磁盘/设备名,这样 就可以实现多个 RAC 节点之间的一致性命名,并且防止由于阵列重新分配而产生的磁盘/设备名改变。在 10.2 版本中,磁盘名只在磁盘组中具有唯一性,而在 10.1 版本中,磁盘名在 ASM 实例(或者是 RAC 中的集 群 ASM 实例)中具有唯一性。 成功创建磁盘组后,包括创建日期、磁盘组名和冗余类型的元数据信息存储在 SGA 和 DATA 磁盘组中 每个磁盘的磁盘头中。V$ASM_DISK 视图现在将反映这个磁盘头信息。一旦这些磁盘处于 ASM 管理下,随后 的所有磁盘组安装操作将造成 ASM 重新读取并确认 ASM 磁盘头。安装磁盘组时,无论是在 ASM 启动时的安 装或随后的安装,都建议用户一次性安装所有必需的磁盘组,实现该操作可最小化多个 ASM 磁盘发现扫描 的系统开销。安装磁盘组之后,ASM 使用集群同步服务(Cluster Synchronization Services,CSS)注册磁 盘组名、实例名以及对应的 Oracle Home 路径名。然后,数据库实例使用这些注册数据构建 TNS 连接字符 91 串。数据库实例在后面使用 TNS 连接字符串连接 ASM 实例进行卷管理活动。 ASM 实例也在 V$ASM_DISK 和 V$ASM_DISK_STAT 视图中驻留 I/O 统计信息,这些视图包括执行的读/写 操作、处理的读/写块,以及引发的读/写错误。基于 ASM 的实用程序 asmiostat 类似于 iostat,可用于显 示基于 ASM 磁盘的 I/O 统计信息。来自于 Oracle 的该实用程序可以复制到文件中,并且针对任何 10.2 ASM 实例执行。 在 10.1 版本中,查询 V$ASM_DISK 和 V$ASM_DISKGROUP 是一项代价昂贵的操作,因为每次执行该操作 都涉及执行磁盘发现。为了最小化系统开销并且允许轻量地访问该数据集,10.2 版本中引入了两个新的视 图:V$ASM_DISK_STAT 和 V$ASM_DISKGROUP_STAT,这两个视图等同于 V$ASM_DISK 和 V$ASM_DISKGROUP。然 而,通过内存轮询 V$ASM_ DISK_STAT 和 V$ASM_DISKGROUP_STAT 视图,因此不需要执行磁盘发现。这些新的视图提供了有效的轻量访 问,因此 EM(企业管理器)可以周期性地在磁盘级上查询性能统计信息,并且在磁盘组级上聚集空间使用情 况统计信息,而不会导致大量的系统开销。 技巧: 为了获得精确的实时统计信息,谨慎的方法是查询 V$ASM_DISK 和 V$ASM_ DISKGROUP 视图,但是在峰值工作量期间,应该经过仔细考虑再执行针对这些视图的查 询。 3.5.103.5.103.5.103.5.10 ASM ASM ASM ASM 磁盘组和数据库磁盘组和数据库磁盘组和数据库磁盘组和数据库 单个的 ASM 实例可以服务独立服务器上的一个或多个单实例的数据库。可以在服务器上的所有数据库 之间共享每个 ASM 磁盘组。因此,数据库不可以驻留在不同的服务器上。然而,磁盘和数据库文件可以是 唯一一个磁盘组的一部分。作为选择,一个 Oracle 数据库也可能将它的文件存储在由相同 ASM 实例管理的 多个磁盘组中。允许多个数据库共享一个磁盘组,这样就可以更好地改进磁盘利用情况,并且提高整体吞 吐量。 技巧: 为了降低管理 ASM 和其磁盘组的复杂程度,Oracle 推荐的方法是,一般情况下每个 RAC 集群或单个 ASM 实例维护和管理不超过两个磁盘组。 数据库区域(Database Area)用于存储活动数据库文件,例如数据文件、控制文件、联机重做日志、 以及用于增量备份中的改动跟踪文件。闪回恢复区用于存储恢复相关的文件,例如 当前控制文件和联机重 做日志的多路复用副本、归档的重做日志、备份集以及闪回日志文件。为了向数据库提供较高的可用性, 在数据库创建期间选择闪回恢复区 时,在闪回恢复区中存储控制文件的活动副本和重做日志组的一个成员 集。 注意: 可以根据需要在任何一个磁盘组中创建和放置控制文件或特殊日志文件的额外副本。同 样需要注意的是,这些“区域”实际上都是磁盘组。 92 3333.5.11.5.11.5.11.5.11 ASM ASM ASM ASM 冗余和故障组冗余和故障组冗余和故障组冗余和故障组 对于没有使用外部冗余的系统,ASM 通过故障组提供了自己的内部冗余机制和额外的高可用性。故障 组是磁盘组的一个子集,根据定义,它是一个磁盘集合,可能会由于一个相关组件(例如,控制器或整个阵 列)产生故障而变得不可用。因此,给定磁盘组的两个独立故障组中的磁盘不能共享常见的故障组件。如果 为磁盘组定义故障组,ASM 就会容忍单个故障组中的多个磁盘同时产生的故障。 ASM 使用独特的镜像算法:不镜像磁盘,而是镜像盘区。作为结果,为了在产生故障时提供连续的保 护,只需要磁盘组中的空间容量,而不需要预备一个热后备(hot spare)磁盘。不建议用户创建不同尺寸的 故障组,因为这将会导致在分配辅助盘区时产生问题。ASM 将文件的主盘区分配给磁盘组中的一个磁盘时, 它会将该盘区的镜像副本分配给磁盘组中的另一个磁盘。给定磁盘上的主盘区将在磁盘组中的某个伙伴磁 盘上具有各自的镜像盘区。ASM 确保主盘区和其镜像副本不会驻留在相同的故障组中。磁盘组的冗余可以 有如下的形式:双向镜像文件(至少需要两个故障组)的普通冗余(默认冗余)和使用三向镜像(至少需要 3 个 故障组)提供较高保护程度的高冗余。一旦创建磁盘组,就不可以改变它的冗余级别。为了改变磁盘组的冗 余,必须创建具有适当冗余的另一个磁盘组,然后必须使用 RMAN 还原或 DBMS_FILE_TRANSFER 将数据文件 移动到这个新创建的磁盘组。 下面的示例显示了如何使用故障组创建磁盘组。在该示例中,在具有 4 个内部托盘的存储阵列上部署 ASM 普通冗余,其中每个托盘都有 4 个磁盘。隔离的失败组件是存储托盘,因此故障组的界限就是存储托 盘,也就是说,每个存储托盘将与一个故障组关联。 SQL> create diskgroup DATA_NRML normal redundancy FAILGROUP flgrp1 disk '/dev/rdsk/c3t19d3s4','/dev/rdsk/c3t19d4s4','/dev/rdsk/c3t19d5s4', '/dev/rdsk/c3t19d6s4' FAILGROUP flgrp2 disk '/dev/rdsk/c4t20d3s4','/dev/rdsk/c4t20d4s4','/dev/rdsk/c4t20d5s4', '/dev/rdsk/c4t19ds4' FAILGROUP flgrp3 disk /dev/rdsk/c5t21d3s4','/dev/rdsk/c5t21d4s4','/dev/rdsk/c5t21d5s4', '/dev/rdsk/c5t21ds4' FAILGROUP flgrp4 disk /dev/rdsk/c6t22d3s4','/dev/rdsk/c6t22d4s4','/dev/rdsk/c6t22d5s4', '/dev/rdsk/c6t22ds4'; 创建时具有普通冗余或高冗余的磁盘组包含分别带有两个或 3 个 镜像的文件。选择第一个分配的文 件盘区作为主盘区,而将镜像的盘区称为辅助盘区。在高冗余的情况下将会有两个辅助盘区。这种分为主 盘区和辅助盘区的逻辑分 组称为盘区集。盘区集总是包含完全相同的数据,因为它们是彼此之间的镜像版 本。因此,在将块写入到文件时,并行写入盘区集中的每个盘区。然而,在从磁盘中 读取块时,总是从主 盘区中读取该块,除非不能读取主盘区。需要注意的是,磁盘组(以及故障组)中的每个磁盘都包含接近相 同数量的主盘区和辅助盘区,从而可以在所有磁盘之间均匀地分布读 I/O 活动。这种方式不同于大多数的 逻辑卷管理器,这些管理器具有主磁盘集和镜像磁盘集。 如前所述,故障组用于隔离组件故障,因此需要了解失败的组件。如果不能确定这个失败的组件,或 者失败的组件是磁盘自身(而不是阵列控制器或存储托盘),则可取的方法是在定义普通冗余磁盘组或高冗 余磁盘组时不指定任何故障组。这将导致每个 ASM 磁盘都处于自己的故障组中,并且 ASM 在内部确定磁盘 93 的镜像伙伴关系。如果存储阵列有完全多余的组件,并且有两条路径通向磁盘,则最好不要指定故障组, 而是让 ASM 管理磁盘的镜像伙伴关系。 在前面的示例中,考虑在故障组 flgrp1 中发生磁盘故障的事件,这将引发一次重新平衡操作——使 用来自于伙伴磁盘的冗余盘区副本重新构造失败磁盘的内容(数据盘区),这个伙伴磁盘可以位于故障组 flgrp2 或 flgrp3 中。假设伙伴磁盘是来自于故障组 3 的 c5t21d3s4。在重新平衡期间,如果数据库实例需 要访问其主盘区位于失败磁盘上的盘区,则数据库将从故障组 flgrp3 的适当磁盘中读取镜像副本。一旦重 新平衡完成并且完全重新构造磁盘内容,数据库实例将恢复为只读取主副本。 磁盘驱动器是机械设备,因此它们可能会产生故障。当驱动器产生故障或具有不定时产生的 I/O 错误 时,产生数据库破坏的可能性就会增加。ASM 会主动地处理 I/O 错误,而不考虑使用故障组。对于持久的 I/O 错误,在设备驱动器中进行一些处理尝试后,只会通过信号通知调用者(Oracle I/O 进程)。如果在Oracle 写操作期间引发了持久的磁盘 I/O 错误,ASM 就会从磁盘组中删除受影响的磁盘,从而防止更多的应用程 序故障。如果磁盘丢失导致数据丢失,ASM 将自动卸载磁盘组以保护磁盘组数据的完整性。 3.5.123.5.123.5.123.5.12 Oracle Database 10g Release 2 Oracle Database 10g Release 2 Oracle Database 10g Release 2 Oracle Database 10g Release 2 中新的空间相中新的空间相中新的空间相中新的空间相关列关列关列关列 Oracle Database 10g Release 2 中引入了两个 ASM 的新列,这些新列提供了关于空闲空间使用的更 为精确的信息: ● USABLE_FREE_SPACE:在 10.1 版本中,在 V$ASM_DISKGROUP 中报告的 FREE_MB 值并没有考虑到镜 像。10.2 版本在 V$ASM_DISKGROUP 中引入了称为 USABLE_FREE_SPACE 的新列,该列指示可以“安全地”利 用的空闲空间数量,同时将镜像考虑进来。该列提供了磁盘组中可用空间的更为精确的视图。 ● REQUIRED_MIRROR_FREE_MB:在 V$ASM_DISKGROUP 视图中,随同 USABLE_FREE_SPACE 一起添加了 另一个新列,用于更为精确地指示给定磁盘组中需要多少可用空闲空间才可以在一次或多次磁盘故障后还 原冗余。该列中显示的空间数量考虑了镜像。 3.5.133.5.133.5.133.5.13 集群同步服务集群同步服务集群同步服务集群同步服务 ASM 被设计为使用单个实例和 RAC 10g 集群。即使是采用单个实例的形式,ASM 也需要在可以使用之 前安装和启动集群同步服务(CSS)。在单个实例中,CSS 维持ASM实例和数据库实例之间的同步。CSS 是Oracle 的集群就绪服务(CRS)的一个组件,在每个运行 Oracle Database 10g ASM 的节点上自动安装,并且在服务 器启动时自动启动。在 RAC 10g 环境中,每个 RAC 节点上都会安装完整的 Oracle 集群件(CRS)。 CSS 提供了集群管理和节点监控管理,因此它会内在地监控 ASM 和 ASM 的共享存储组件(磁盘和磁盘 组)。在启动时,ASM 将使用 CSS 注册自身和它所安装的所有磁盘组。这就使 CSS 可以在所有 RAC 节点之间 同步保存磁盘组元数据。创建的任何新磁盘组也会动态地进行注册,并且广播给集群中的其他节点。和数 据库一样,节点间的通信用于同步 ASM 实例中的活动。CSS 用于确保 ASM 实例的健康。通过需要同步的结 构化改动(例如添加磁盘)初始化 ASM 节点间消息。因此,ASM 使用与数据库相同的综合锁管理基础结构, 从而实现有效的同步。 3.5.143.5.143.5.143.5.14 数据库实例和数据库实例和数据库实例和数据库实例和 ASMASMASMASM 数据库实例是标准 Oracle 实例,并且是 ASM 实例的客户端。数据库和 ASM 之间的通信总是在节点内 进行;也就是说,数据库不会联系远程(在 RAC 中)ASM 实例以服务数据库请求。创建 ASM 磁盘组后,现在 94 就可以使用 DBCA 创建数据库。DBCA 有 3 种数据库文件结构方面的选项:文件系统、裸设备或 ASM。 如果选择 ASM,则会列出所有可用的磁盘组(如果已经为 ASM 实例创建了磁盘组)。选择 ASM 和磁盘组 的操作将指示 DBCA 在 ASM 中创建数据库。如果没有任何磁盘组或者需要新的磁盘组,DBCA 就会提供机会 创建新的磁盘组。 注意: ASM 磁盘组可以驻留所有的 Oracle 物理文件,包括控制文件、数据文件、服务器参数文 件和 RMAN 备份文件。然而,Oracle 可执行文件、CRS 文件(OCR 和表决磁盘)以及非数据 库文件不可以驻留在 ASM 磁盘组中。在本章中,所有的 Oracle 物理文件一般都称为数据 库文件。 使用 ASM 存储的活动数据库实例在操作上类似于典型的数据库实例;也就是说,直接执行所有的文件 访问,而没有过多的 ASM 干涉。在创建、删除或打开文件时,数据库实例与 ASM 实例进行交互。此时,从 ASM 实例中读取文件布局,并且使用存储在数据库实例中的盘区图完成所有随后的 I/O 操作。如果存储配 置改变,例如在添加、删除磁盘或磁盘失败时,ASM 实例和数据库实例也会进行交互。 虽然 ASM 层对数据库客户端和服务器上的用户是透明的,但是只可以通过数据库实例和它的实用程序 执行所有数据文件访问。例如,只可以使用 RMAN 执行基于 ASM 的文件的数据库备份。注意,不推荐使用类 似于 Unix dd 命令的实用程序备份或还原 ASM 磁盘组。 ASM 文件的数据库文件级访问(读/写)与 10g 之前的 Oracle 版本类似,不同之处在于将使用 ASM 代码 路径自动处理和管理以“+”开头的任何数据库文件名。然而,通过 ASM 文件,数据库文件访问内在地具有 裸设备的特征,也就是具有核化异步 I/O(Kernelized Asynchronous I/O,KAIO)的非缓冲(直接)I/O。 3.5.153.5.153.5.153.5.15 使用使用使用使用 ASMASMASMASM 进行数据库合并和集群化进行数据库合并和集群化进行数据库合并和集群化进行数据库合并和集群化 在 Oracle Database 10g Release 1 中,不可以通过同一个 ASM 实例管理 RAC 和单实例的数据库,否 则很难实现存储网格体系结构和统一的数据库解决方案。Oracle Database 10g Release 2 增强了 ASM 在 集群环境中的功能,允许每个节点上的一个 ASM 实例管理集群中的所有数据库实例。因此,给定节点上的 ASM 实例现在可以同时管理单个实例或许多 RAC 数据库实例的存储,并且管理一个或多个单实例数据库。 该特性使顾客不再需要维护服务集群中不同数据库类型所需的多个 ASM 实例,从而 DBA 不需要管理单独的 存储池。这个新特性利用 Oracle 集群件,通过较少的代价将多个孤立的数据库合并为由 ASM 管理的一个集 群池或存储。这就从根本上允许顾客通过消除浪费的、过多预备的存储来优化他们的存储利用情况,并且 通过减少数据库存储的整体覆盖区域来节省开支。 一旦创建数据库并且实例处于活动状态,该数据库实例将成为 ASM 的客户端,这一点反映在 V$ASM_CLIENT 视图中。对于数据库实例打开的每个磁盘组,V$ASM_CLIENT 包含相应的一行。在下面的示例 中,V$ASM_CLIENT 显示连接 ASM 的两个数据库,其中每个实例使用两个磁盘组。 注意: 实例 cubs1 是 10.2 RAC 支持的数据库,而实例 sox1 是 10.1.0.4 单实例。 select instance,name, status, software_version, compatible_version 95 from v$asm_client; INSTANCE STATUS SOFTWARE_VRSN COMPATIBLE_VRSN -------- ------------ ------------- --------------- cubs1 CONNECTED 10.2.0.1.0 10.2.0.1.0 cubs1 CONNECTED 10.2.0.1.0 10.2.0.1.0 sox1 CONNECTED 10.1.0.3.0 10.1.0.2.0 sox1 CONNECTED 10.1.0.3.0 10.1.0.2.0 3.5.163.5.163.5.163.5.16 支持支持支持支持 ASMASMASMASM 的数据库进程的数据库进程的数据库进程的数据库进程 在数据库实例中,添加了 3 组进程来支持 ASM 磁盘组和基础结构: ● RBAL:该进程执行磁盘组中所有磁盘的全局打开。 ● ASMB:该进程使用磁盘组名联系 CSS,并且获取关联的 ASM 连接字符串,然后使用该连接字符串 连接 ASM 实例。通过使用这种持久的连接,交换定期的消息以更新统计,并且提供一种推动机制。在需要 ASM 干涉的操作期间,例如通过数据库前台创建文件时,数据库前台会直接连接 ASM 实例以执行该操作。 成功完成文件创建之后,ASM 将数据库文件盘区图发送给 ASMB。此外,ASMB 也将数据库 I/O 统计信息发送 给 ASM 实例。 ● O00x:这是一组从属进程,用于建立 ASM 实例的连接,其中 x是1到10之间的数字。通过这个 连接池,数据库进程可以发送消息给 ASM 实例。例如,打开文件操作会通过从属进程发送打开请求给 ASM 实例。然而,从属进程不适用于长期运行的操作,例如创建文件。从属(池)连接消除了登录到 ASM 实例进 行短期请求的系统开销。这些从属进程在不使用时会被关闭。 3.5.173.5.173.5.173.5.17 大文件和大文件和大文件和大文件和 ASMASMASMASM 大文件特性(本章前面对此进行过介绍)完全适合于 VLDB(非常大型的数据库)和 ASM。用户不需要管理 数百个数据文件,使用大文件将大量减少数据文件的数量,这将改进检查点技术,并且提高打开数据库的 速度,因为只需要执行非常少的文件打开操作。大文件的使用减少了管理大量数据文件的内部系统开销。 通过 ASM,用于外部冗余的大文件可以达到 32TB 的容量,而用于普通冗余/高冗余的大文件可以达到 12TB 的容量,这些都是按照 8K 块尺寸的标准进行考虑。使用大文件时,必须谨慎地评审备份和恢复策略。显而 易见的是,用户不可以对 36TB 的数据文件执行完全数据文件备份,因此在管理大文件时必须执行类似于 RMAN 增量备份和累积备份的操作。 3.5.183.5.183.5.183.5.18 支持支持支持支持 ASMASMASMASM 的数据库的数据库的数据库的数据库 init.orainit.orainit.orainit.ora 参数参数参数参数 数据库实例的 SGA 参数需要稍微进行修改以支持 ASM 盘区图和其他 ASM 信息。如果在数据库实例上使 用 10g 自动内存管理特性,下面的尺寸调整数据可以视为单纯的信息,或者视为用于估量适当 SGA 值的补 充数据。下面是数据库实例上 SGA 尺寸调整的指导原则: ● 进程:增加 16K。 ● Large_pool:增加额外的 600K。 ● Shared_pool:需要额外的内存来存储盘区图。通过如下的查询添加值,从而获得已经在 ASM 中 或将存储在 ASM 中的当前数据库存储大小。接下来,确定使用的(或将要使用的)冗余类型并计算 shared_pool,同时使用总计值作为输入: 96 connect user/pass@ as sysdba select sum(bytes)/(1024*1024*1024) from v$datafile; select sum(bytes)/(1024*1024*1024) from v$logfile a, v$log b where a.group#=b.group#; select sum(bytes)/(1024*1024*1024) from v$tempfile where status='ONLINE'; ● 使用外部冗余的磁盘组:每 100GB 的空间需要 1MB 的额外共享池+2M。 ● 使用普通冗余的磁盘组:每 50GB 的空间需要 1MB 的额外共享池+4M。 ● 使用高冗余的磁盘组:每 33GB 的空间需要 1MB 的额外共享池+6M。 3.5.193.5.193.5.193.5.19 ASM ASM ASM ASM 和数据库部署最佳实践和数据库部署最佳实践和数据库部署最佳实践和数据库部署最佳实践 ASM 提供了即装即用的冗余实现和最佳的性能。然而,应该考虑下面的操作选项以增加性能和/或可用 性: ● 使用两个或更多的 HBA(主机总线适配器)或启动程序实现存储阵列的多条访问路径。 ● 在这些多个 HBA 上部署多路径软件,从而提供 I/O 负载平衡和故障恢复功能。 ● 使用具有多个类似大小和性能的磁盘的磁盘组。包含大量磁盘的磁盘组提供了数据盘区的广泛分 布,这样就允许较高的 I/O 并发性并减少产生的热点。大型磁盘组可以很容易地支持各种 I/O 特征和工作 量,因此可以使用单个(数据库区域)磁盘组驻留数据库文件、日志文件和控制文件。 ● 使用带有 4 个或更多磁盘的磁盘组,并且确保这些磁盘跨越多个后端磁盘适配器。 前面介绍过,Oracle 一般推荐使用两个及两个以下的磁盘组。例如,常见的部署方式可能是一个数据 库磁盘组(如 DATA 磁盘组)中有 4 个或更多的磁盘,这些磁盘跨越所有后端磁盘适配器/定向器,而闪回恢 复区磁盘组中则有 8 到 10 个磁盘。闪回恢复区的大小将取决于存储的内容和存储量,也就是完全数据库备 份、增量备份、闪回数据库日志和归档日志。控制文件的活动副本和每个重做日志组的一个成员存储在闪 回恢复区中。查看 Oracle 出版社出版的 High Availability Architecture and Best Practices Manual 以了解关于这些主题的更多详细信息。 3.5.203.5.203.5.203.5.20 ASM ASM ASM ASM 存储管理和分配存储管理和分配存储管理和分配存储管理和分配 默认情况下,在ASM 构造下创建的数据库将按照 SAME 方法论中指定的方式进行分条和镜像(也就是说, 在磁盘组中的所有磁盘之间均匀地分布和平衡 I/O 负载)。使用 1MB 的条尺寸在每个文件的基础上执行分条, 这不同于在磁盘卷级别上执行分条和镜像的其他 LVM(逻辑卷管理器)。Oracle 声明,1MB 的条深度已经被 证明是 Oracle 数据库的最佳条深度。通过结合磁盘组中均匀分布的盘区,这种最佳的条深度可以防止产生 热点。 ASM 在称为分配单元(AU)的单元中分配空间。ASM 总是创建跨越磁盘组中所有磁盘的单 AU 盘区(不同 于表空间盘区)。对于具有类似尺寸磁盘的磁盘组,在每个磁盘上应该有同等数量的 AU 盘区。数据库文件 划分为多个文件盘区。存在两种类型的 AU 盘区分布:粗粒度分布和细粒度分布。对于粗粒度的分布,每个 粗粒度的文件都被映射到单个的分配单元。使用细粒度的分布时,由8 个 AU 构成一组,每个 AU 提供 128K, 97 交错形成每个粒度。细粒度的分布将大型 I/O 操作划分为可以并行执行的多个 128K I/O 操作,从而有助于 连续的 I/O。对于所有的系统相关文件,作为系统模板的一部分预先定义粗粒度和细粒度属性。 技巧: 重做日志文件和归档日志文件被定义为细粒度的分布,而数据文件则被定义为粗粒度的 分布。 3.5.213.5.213.5.213.5.21 ASM ASM ASM ASM 重新平衡和重新分布重新平衡和重新分布重新平衡和重新分布重新平衡和重新分布 使用传统的卷管理器时,分条文件系统的扩展/增长或收缩一般非常困难。通过使用 ASM,这些磁盘/ 卷改动现在包括分条数据的无缝重新分布(重新平衡),并且可以联机执行。存储配置的任何改动将触发重 新平衡操作。重新平衡操作的主要目标是始终提供磁盘组中所有磁盘之间的文件盘区和空间使用情况的均 匀分布。在每个文件的基础上对所有数据库文件执行重新平衡操作;然而,一些文件可能不需要重新平衡。 Oracle 后台进程 RBAL 通过 ASM 实例管理这种重新平衡操作。重新平衡进程检查每个文件盘区图,并 且重新划分新的 AU 盘区以形成新的存储配置。例如,考虑带有 8 个磁盘的磁盘组以及带有 40 个 AU 盘区的 数据文件(每个磁盘将驻留 5 个 AU 盘区)。当添加两个相同尺寸的新驱动器时,重新平衡数据文件并扩展到 10 个驱动器,其中每个驱动器包含 4 个 AU 盘区。完成该重新平衡操作只需要移动 8 个 AU 盘区,也就是说, 不需要重新分布所有的 AU 盘区,只需要移动最少的 AU 盘区就可以实现均等的分布。下面是 ASM 重新平衡 操作的典型流程: ● 在 ASM 实例上,DBA 添加或删除磁盘组中的磁盘。 ● 调用 RBAL 进程创建重新平衡计划,然后开始协调重新分布。 ● RBAL 将计算执行该任务所需的估计时间和工作,然后发送消息给 ARBx 进程以实际地处理请求。 通过 asm_power_limit 参数直接确定调用的 ARBx 进程的数量。 ● 更新持续操作目录(元数据)以反映重新平衡活动。 ● 将重新定位的每个盘区分配给 ARBx 进程。 ● ARBx 对这些盘区执行重新平衡。锁定、重新定位、解锁每个盘区。这在 V$ASM_ OPERATION 中显示为 Operation REBAL。 重新平衡涉及在物理上移动 AU 盘区。这种移动的影响一般较低,因为一次只对一个 AU 盘区执行重新 平衡;因此,在任何给定时间,每个 ARBx 进程只有一个未完成的 I/O,这应该不会给联机数据库活动带来 负面影响。然而,一般建议用户在非高峰期间调度重新平衡操作。 技巧: init.ora 参数 asm_power_limit 用于影响重新平衡操作的吞吐量和速度。asm _power_limit 的取值范围是 0 到 11,值为 11 表示全速,而值为 0 表示低速。用户应该 谨慎使用值 0,这会关闭自动重新平衡。 也可以使用 alter diskgroup 命令为特定的重新平衡活动设置速度极限值,该值只对特定的重新平衡 任务有效。在下面的示例中,使用值为 0 的速度极限值将指示对于该重新平衡操作不应该发生任何重新平 衡,但是我们将使用值 11 来指示执行全速的重新平衡操作。该设置对于如下方面特别重要:添加或删除具 有外部冗余的存储,然后将重新平衡操作推迟到后面的某个预定时间。 98 "Session1 SQL"> alter diskgroup DATA add disk '/dev/rdsk/c3t19d39s4' rebalance power 11; From another session: "Session2 SQL"> select * from v$asm_operation GROUP OPERA STAT POWER ACTUAL SOFAR EST_WORK EST_RATE EST_MINUTES ----- ----- ---- ------- ------ ------ --------- --------- ----------- 1 REBAL WAIT 11 0 0 0 0 0 1 DSCV WAIT 11 0 0 0 0 0 time passes…………..) OPERA STAT POWER ACTUAL SOFAR EST_WORK EST_RATE EST_MINUTES ------------ --- --- ------- ------ -------- --------- ---------- 1 REBAL REAP 11 2 25 219 485 0 技巧: 如果使用 ASM 删除或添加多个磁盘,则最好一次性添加或删除所有的驱动器。这将减少 执行存储改动所需的重新平衡操作的数量。 ASM 磁盘组的重新平衡是一种异步操作,在后台发送该操作后,控制权将直接返回给 DBA,可以通过 V$ASM_OPERATION 视图查询正在执行的操作的状态。然而,在某些情况下磁盘组操作需要同步执行,也就 是直到重新平衡操作完成才执行。在Oracle Database 10g Release 2中,导致执行重新平衡操作的ASM alter diskgroup 命令现在可以选择指定等待的选项,从而可以执行精确的(连续的)脚本,这些脚本可能依赖于 在执行任何随后的操作之前完成的重新平衡操作产生的空间改动。例如,如果将 100GB 的存储空间添加到 完全满的磁盘组,则直到重新平衡操作完成才能够使用所有的 100GB 存储空间。如果输入新的重新平衡操 作命令,同时一个重新平衡操作命令已经以等待模式运行,则直到磁盘组处于平衡状态或者该重新平衡操 作遇到错误,前面的命令才会返回。 下面的示例举例说明了如何在 SQL 脚本中使用 WAIT 选项。该脚本添加一个新的磁盘/dev/raw/raw6, 等待添加和重新平衡操作完成,然后将控制权返回给脚本。随后的步骤添加一个大型的表空间: #An example script to test WAIT option alter diskgroup data add disk '/dev/raw/raw6' rebalance power 2 wait; #login into database and create a tablespace for the next month's #Order Entry data sqlplus oe_dba/oe1@proddb << EOF create BIGFILE tablespace May_OE datafile size 800 Gb; << EOF 使用外部冗余时,删除和添加磁盘组中的磁盘以非常无缝的方式执行,用户只需要调度重新平衡操作。然 而,使用故障组时,可能需要针对如何删除和添加磁盘进行一些计划和预先考虑。 3.63.63.63.6 使用分区来避免磁盘争用使用分区来避免磁盘争用使用分区来避免磁盘争用使用分区来避免磁盘争用 分区可能是提高与大型表有关的性能的最佳方法。通过访问一个表或索引的较小片段,而不是访问整 个表或索引,分区可以很好地提高效率。这个策略在一个或多个用户访问同一个表的多个部分时特别有效。 如果一个表的分区(片段)位于不同的设备上,吞吐量就会大大增加。分区还可以被独立地备份和恢复(即便 它们正在使用),这样可以减少备份期间可能出现的磁盘 I/O 问题。仅仅当分区被正确实现后,才能体现 99 Oracle 性能提高的良好优点。理解分区的最好方法就是看一个例子。考虑如下的简单示例,根据 deptno 列,dept 表被分成了 3 个分区(片段)。 使用 3 个分区创建表 dept: create table dept (deptno number(2), dept_name varchar2(30)) partition by range(deptno) (partition d1 values less than (10) tablespace dept1, partition d2 values less than (20) tablespace dept2, partition d3 alues less than (maxvalue) tablespace dept3); 在这个例子中,在 dept 表上建立了 3 个独立的分区。获得最佳吞吐量的关键就是把每个分区放置在 不同的物理磁盘上,这样可以同时访问 3 个分区,前提是不使用 ASM。表空间 dept1、dept2、dept3 必须 有放在不同物理磁盘上的物理文件。要记住表空间仅是数据文件在物理磁盘上的信息的逻辑存储区。可以 包含有多个数据文件的一个表空间,但一个数据文件只能关联到一个表空间上。改善磁盘 I/O 分区的关键 就是可以同时访问存放在不同物理磁盘上的分区,或者使用 ASM。 可以把数据添加到表的 3 个分区上: insert into dept values (1, 'ADMIN'); insert into dept values (7, 'MGMT'); insert into dept values (10, 'MANUF'); insert into dept values (15, 'ACCT'); insert into dept values (22, 'SALES'); 当我们从 dept 表中查询时,这个表仍可以被看成一个单独的表: select * from dept; DEPTNO DEPT_NAME 1 ADMIN 7 MGMT 10 MANUF 15 ACCT 22 SALES 我们在前面的示例中查询所有分区里的记录。在下面 3 个例子中,我们分别查询每个分区。 我们只从一个分区中选取数据并且只访问一个分区: select * from dept partition (d1); DEPTNO DEPT_NAME 1 ADMIN 7 MGMT select * from dept partition (d2); 100 DEPTNO DEPT_NAME 10 MANUF 15 ACCT select * from dept partition (d3); DEPTNO DEPT_NAME 22 SALES select * from dept where deptno = 22; DEPTNO DEPT_NAME 22 SALES 注意在最后一个示例中,我们不必访问第一个或第二个分区(分区规避)。对索引分区,同时使用并行 选项,可以使分区功能更强大。 技巧: 为了最小化对一个较大表的磁盘 I/O,应该把表分割在多个分区上,这些分区应该放置在 不同的物理磁盘上。 3.6.13.6.13.6.13.6.1 获得关于分区的更多信息获得关于分区的更多信息获得关于分区的更多信息获得关于分区的更多信息 可以通过访问 user_tables、dba_part_tables 和 user_segments 来获取与分区相关的信息。下面显 示了查询 3 个表的查询例子,以及前一部分的例子的相应输出结果。 select table_name, partitioned from dba_tables where table_name in ('DEPT','EMP'); TABLE_NAME PAR DEPT YES EMP NO 在前面的示例中,par 列表明表是否已被分区。 select owner, table_name, partition_count from dba_part_tables where table_name = 'DEPT'; OWNER TABLE_NAME PARTITION_COUNT KEVIN DEPT 3 在前面和下面的示例中,表 dept 中共有 3 个分区。 select segment_name, partition_name, segment_type, tablespace_name from user_segments; 101 SEGMENT_NAME PARTITION_NAME SEGMENT_TYPE TABLESPACE_NAME EMP TABLE USER_DATA DEPT D1 TABLE PARTITION DEPT1 DEPT D2 TABLE PARTITION DEPT2 DEPT D3 TABLE PARTITION DEPT3 技巧: 表可以很容易地分割成能被访问和/或操纵的独立部分。还可以用一个分区表访问整个 表。访问表 user_tables、dba_part_tables 和 user_segments 可以得到关于已分区表的 更多分区信息。查看第 2 章以了解关于索引分区的额外信息。 3.6.23.6.23.6.23.6.2 其他类型的分区其他类型的分区其他类型的分区其他类型的分区 还有其他类型的分区。主要有范围(range)分区、散列(hash)分区、组合(composite)分区和列表(list) 分区。还有一些与分区相关的多索引类型。我们在前面一节里已经讨论了范围分区,但还有多列范围 (multicolumn range)分区。 1. 1. 1. 1. 多列范围分区多列范围分区多列范围分区多列范围分区 除了使用多列定义范围之外,多列范围分区和范围分区没什么区别。在下面的示例中,我们把数据分 成 4 份,这样就可以在只要查看一个分区的时候避免访问其他分区,同时我们还可以在不妨碍其他分区的 情况下归档其中一个分区中的数据。还可以为了获得更好的 I/O,把数据分到多个表空间中。 create table cust_sales (acct_no number(5), cust_name char(30), item_id number(9), sale_day integer not null, sale_mth integer not null, sale_yr integer not null) partition by range (sale_yr, sale_mth, sale_day) (partition cust_sales_q1 values less than (1998, 04, 01) tablespace users, partition cust_sales_q2 values less than (1998, 07, 01) tablespace users2, partition cust_sales_q3 values less than (1998, 10, 01) tablespace users, partition cust_sales_q4 values less than (1999, 01, 01) tablespace users2, partition cust_sales_qx values less than (maxvalue, maxvalue, maxvalue) tablespace users2); 技巧: 还可以把多个列当作条件为表分区。必须为作为分区键组成部分的所有列指定最大值。 2. 2. 2. 2. 散列分区散列分区散列分区散列分区 通常在进行范围分区而不知如何设置断点时会用到散列分区。它根据指定分区键的散列值把数据分成 指定的分区数。为了得到均匀分布,可以总是把 2 的几次方指定为散列分区数。散列分区仅支持本地索引。 102 可以指定索引的名称和表分区名称,而且以后还可以增加或减少分区的数目(如果发现分区太少或太多时)。 下面的示例给出了有 4 个散列分区的表,这个分区建在分区键 ACCT_NO 上,并被分布在 4 个表空间里。 create table cust_sales_hash ( acct_no number(5), cust_name char(30), sale_day integer not null, sale_mth integer not null, sale_yr integer not null) partition by hash (acct_no) partitions 4 store in (users1, users2, users3, users4); 技巧: 当不知道如何划分一个表,但知道它需要分区并分散保存时,请使用散列分区。 3. 3. 3. 3. 组合分区组合分区组合分区组合分区 有时候一个表会很庞大,并且经常被访问,这样就必须用一种更有效的方法分割它。组合分区其实就 是范围和散列分区的结合体。可以先使用范围分区来进行分区,然后再把分区进一步散列化以分布 I/O。 组合分区支持本地索引和范围分区的全局索引。下面给出了一个可以得到高度作业安全性的组合分区的例 子。 create table orders( ordid number, acct_no number(5), cust_name char(30), orderdate date, productid number) partition by range(orderdate) subpartition by hash(productid) subpartitions 8 (partition q1 values less than (to_date('01-APR-1998', 'dd-mon-yyyy')), partition q2 values less than (to_date('01-JUL-1998', 'dd-mon-yyyy')), partition q3 values less than (to_date('01-OCT-1998', 'dd-mon-yyyy')), partition q4 values less than(maxvalue)); 这个例子创建了基于ORDERDATE列值的范围分区,然后形成了分区q1、q2、q3 和 q4。接着基于PRODUCTID 列的散列值把每个分区分成 8 个子分区。 4. 4. 4. 4. 列表分区列表分区列表分区列表分区 Oracle 9i 为那些真正了解数据的 DBA 或开发人员增加了列表分区。列表分区允许您为每个分区指派 103 具体的列值。如下的代码程序清单后列举了列表分区的一些限制。 create table dept_part (deptno number(2), dname varchar2(14), loc varchar2(13)) partition by list (dname) (partition d1_east values ('BOSTON', 'NEW YORK'), partition d2_west values ('SAN FRANCISCO', 'LOS ANGELES'), partition d3_south values ('ATLANTA', 'DALLAS'), partition d4_north values ('CHICAGO', 'DETROIT')); 列表分区的限制 列表分区存在以下一些限制: ● 只可以在列的列表中指定一个分区键,但它不能是 LOB 列。如果分区键是一个对象类型列,还可 以按照列类型的某个特性进行分区。 ● VALUES 子句中的每个分区值必须在表的所有分区中是唯一的。 ● 不能在索引编排表中使用列表分区。 ● 组成每个分区的值列表的字符串最长可以到 4KB。 ● 所有分区的分区值累计最大数目不能超过 64KB-1。 4. 4. 4. 4. 分区连接分区连接分区连接分区连接 Oracle 还允许只通过它们的分区来连接两个分区表。无需再执行多行和多行之间的连接,现在可以在 每个表上进行分区,然后连接这些结果。为了连接分区,表必须是均分的表,这也意味着: ● 使用相同的分区键对表分区。 ● 必须用相同的分区断点对表分区。 3.6.33.6.33.6.33.6.3 其他分区选项其他分区选项其他分区选项其他分区选项 这一节介绍管理分区的其他一些选项。可以看到适用于表操作的选项同样适用于分区。 ● MODIFY PARTITION partition_name 修改一个表分区的实际物理特性。还可以指定下列任何特 性作为分区的新物理特性:LOGGING、ATTRIBUTE、PCTFREE、PCTUSED、INITRANS、MAXTRANS 以及 STORAGE。 ● RENAME PARTITION partition_name TO new_partition_name 将表分区 PARTITION_NAME 重命名 为 NEW_PARTITION_NAME。 ● MOVE PARTITION partition_name 将表分区 PARTITION_NAME 移至另外一个段。可以将分区数据 移至另外一个表空间,重新集群数据来减少数据碎片,或修改创建时的物理属性: alter table dept move partition d3 tablespace dept4 nologging; 在上面的示例中,d3 分区和所有相应的数据从原先所在的 dept3 表空间移到 dept4 表空间中。 104 技巧: 移动分区时,尽可能使用 nologging 选项以提高速度。 ● ADD PARTITION new_partition_name 把新的分区 new_partition_name 添加到被分区表的头部。 必须指定下列任何一个特性作为分区的新物理特性:logging、attribute、PCTFREE、PCTUSED、INITRANS、 MAXTRANS 和其他存储特性。 ● VALUES LESS THAN(value_list) 为新的分区指定上限。value_list 是一个用逗号分开、与 column_list 相应的文本值的有序列表。value_list 必须远大于表中已有的最大分区所对应的分区上限。 ● EXCHANGE PARTITION 这个功能强大的选项允许用户转换分区、将子分区转换为未分区的表、或 者将未分区的表转换为分区。如果归档旧的范围分区,并且希望在删除这些范围分区之前导出它们,则该 选项就非常有用。同样,该选项对于将增量数据快速加载到已有分区表中也非常有用。 ● DROP PARTITION partition_name 从分区表中删除分区 partition_name 以及其中的数据。 alter table dept drop partition d3; 技巧: 删除一个表分区后,该分区的本地索引(而不是其他分区的本地索引)也会被删除,全局 索引(在整个表上的索引)也将不可用(除非您愿意在后面重新构建该索引)。如果使用 了全局索引,那么如果删除了表的分区就需要重建它们。 ● TRUNCATE PARTITION partition_name 删除表中一个分区中的所有行。下面的示例给出了对 D1 分区的截取。注意,您必须在该操作后重新构建索引。 Alter table dept truncate partition d1; Alter index dept_idx rebuild partition d1; ● SPLIT/MERGE PARTITION partition_name_old 创建两个新分区,每个新分区都有一个新段、新 的物理特性和新的初始盘区。与旧的分区相关联的段已经废弃(合并操作与它正好相反)。下面的示例展示 了如何将 D1 分区分割成 D1 A 分区和 D1 B 分区,以 deptno=5 为条件。注意,在该操作后必须重建索引。 Alter table dept split partition d1 at (5) into ( partition d1a tablespace dept1, partition d1b tablespace dept2); SEGMENT_NAME PARTITION_NAME SEGMENT_TYPE ------------ -------------- ----------------- DEPT D1A TABLE PARTITION DEPT D1B TABLE PARTITION DEPT D2 TABLE PARTITION Alter index dept_idx rebuild partition d1a; Alter index dept_idx rebuild partition d1b; 用户可以获得两个分区,并且使用合并的方式将这两个分区组合成为一个分区。下面的示例展示了把 D1 A 和 D1 B 两个分区合并为 D1 分区的例子。注意,在该操作后必须重建索引。 Alter table dept merge partition d1a, d1b into partition d1; SEGMENT_NAME PARTITION_NAME SEGMENT_TYPE 105 ------------ -------------- ----------------- DEPT D1 TABLE PARTITION DEPT D2 TABLE PARTITION Alter index dept_idx rebuild partition d1; ● UNUSABLE LOCAL INDEXES 将与这个 partition_name 相关的所有本地索引分区标记为不可用。 ● REBUILD UNUSABLE LOCAL INDEXES 重建与 partition_name 相关的不可用本地索引分区。 ● UNUSABLE 把索引或索引分区标记为不可用。在使用一个不可用的索引之前,必须重建或删除再 重新创建该索引。同样,如果一个分区被标记为不可用,索引的其他分区仍然有效,如果语句没有访问不 可用分区,那么可以执行请求该索引的语句。还可以在重建它之前分割或重命名这个不可用分区。 ● REBUILD PARTITION 重建索引的一个分区。还可以使用这个选项把一个索引分区移到其他表空 间内,或者改变一个创建时物理特性。 3.73.73.73.7 使用索引分区使用索引分区使用索引分区使用索引分区 索引分区与分区表拥有同样的优点。如果正确执行,则 通过访问索引的小片断而不是整个表上的索 引可以增加性能。索引包括本地索引和全局索引,有前缀索引或无前缀索引。一个本地索引已经经过分区; 每个片断就是 一个本地索引。全局索引是非分区索引,在分区前使用。在有前缀索引中,索引最左边的部 分是分区键;对于无前缀索引,需要较大的代价来访问,因为没有索引分 区键。如果删除具有全局索引的 表的分区,则对应的全局索引会失效。如果删除具有本地索引的表的分区,则也会删除本地索引。 在 Release 9i 和早期的版本中,提供了会话参数 SKIP_UNUSABLE_INDEXES 来允许用户禁用标志为不 可用的索引和索引分区的错误报告。在 Release 10g 中,这是一个初始化参数,并且默认取值为 TRUE。如 果不希望数据库选择新的执行计划以避免使用不可用的段,则应该设置该参数为 FALSE。 下面就是一个本地分区的索引(最常见的类型)的例子。索引的名字是 dept_index,建立在表 dept 的 deptno 列上。这个索引被分割成 3 个片断(d1、d2、d3),而这 3 个片断位于被相关数据的位置分割开的 3 个表空间上(dept1、dept2、dept3)。只要 dept1~dept3 是与不同物理磁盘上的数据文件相对应的表空间, 这就保证了从表的一个分区和与它相关联的索引分区上访问信息不会访问同一个磁盘,而是两个物理磁盘。 create index dept_index on dept (deptno) local (partition d1 tablespace dept2, partition d2 tablespace dept3, partition d3 tablespace dept1); Index Created. 可以通过访问 dba_indexes 来得到与分区索引相关的信息: select index_name, partition_name, tablespace_name from dba_indexes where index_name = 'DEPT_INDEX' order by partition_name; INDEX_NAME PARTITION_NAME TABLESPACE_NAME DEPT_INDEX D1 DEPT2 DEPT_INDEX D2 DEPT3 DEPT_INDEX D3 DEPT1 106 技巧: 被分区后的索引(本地索引)也应该是有前缀的;分区键应该是索引的前导。 3.83.83.83.8 导出分区导出分区导出分区导出分区 可以很容易地导出分区。如果表中的数据被仔细地分 段,可以导出一个分区中所有的新信息。该操 作只对某些数据集有效,这些数据集使用一些类型的渐增列值作为分区键。例如,如果根据日期进行分区, 则所有新的 数据将进入最新的分区。然而,如果数据按照用户名或其他一些普通的标识符分区,则不一定 能导出一个分区中所有的新信息。这样就不必导出没有修改或已经导出 的分区中的数据。通过 EXPORT 命 令并把 owner.table. partition_name 值赋给要被导出的表,就可以只导出这个分区。 exp user/pass file=tab.dmp tables=(owner.table.partition_name) 技巧: 如果归档旧的数据,则在导出分区之前,可以考虑将该分区交换为更详细的表名。通过 这种方式,可以只导回该表以方便后面引用,并且潜在地避免将其返回到分区表。 3.3.3.3.9999 消除碎片消除碎片消除碎片消除碎片 碎片会阻碍数据库的空间管理,但总的说来,一个段中 的盘区量的多少总是会影响数据库性能。同 样也有盘区的数目从来都不会影响性能的说法。拥有很多跨多个数据文件的不连续盘区的位图索引就是一 个大的性能问 题。通常情况下,本地托管的表空间已经很大程度地减少了与盘区相关的问题。对于大多数 DBA(而不是所有 DBA)来说,如果正确地配置存储,则重组已经成为过去的做法。但是,如果您仍然需要处 理经常发生的重组,则可以通过一些方法来执行该活动,同时最小化停机时间。 可以按照下面的步骤来避免与盘区管理相关的性能问题: ● 如果知道段将增长到多大尺寸,则使用本地托管的统一盘区表空间。 ● 在字典托管的表空间中使用统一的盘区大小(除了 SYSTEM 表空间外)。 ● 使盘区尺寸是数据库块尺寸的倍数。 ● 把增长过大的表放在有合适盘区大小的表空间中。 ● 通过使用 ASSM 避免 FREELIST 争用。 ● 使用统一的盘区大小重建或分割表空间。 建议您最好定期监控数据库,查找增长到最大盘区数量(超过 1000)的段,然后适当地管理这些段。 select segment_name, segment_type, extents, bytes from dba_segments a, dba_tablespaces b where a.extents > 1000; SEGMENT_NAME SEGMENT_TYPE EXTENTS BYTES ORDER TABLE 2200 220000000 ORDER_IDX1 INDEX 1200 120000000 CUSTOMER TABLE 7000 70000000 107 技巧: 定期使用查询 DBA_SEGMENTS 通常可以保证对象不会建立太多的盘区(不使用 ASM 的情 况)。早期捕捉问题是避免后期性能问题的关键。正确地把对象存放在有合适的统一盘区 大小的表空间里也很关键。 3.9.13.9.13.9.13.9.1 使用正确的盘区大小使用正确的盘区大小使用正确的盘区大小使用正确的盘区大小 当我们从表中读取数据时,通常可以通过索引进行一次 ROWID 操作或者全表扫描(除了索引组织表)来 访问。在多数情况下,通过 ROWID 访问是首选方法。这是因为 ROWID 方法允许数据库确定记录所在的确切 数据块,从而跳过这个段上的任何盘区分配信息。简单说就是 ROWID 操作不会关心段中究竟有多少盘区。 数据库块尺寸一般从 4K 到 32K。 因此,无论段中有多少盘区,只要盘区大小是数据库块尺寸的倍数,全 表扫描就会执行相同数量的读操作。因此,如果使用大小是数据块尺寸倍数的盘区,是否仍然 需要担心盘 区的总数?确实需要,但是原因与以往不同。按照这种方式考虑,用户拥有的盘区越多,则必须管理的盘 区也就越多,即使通过较快的方法对其进行管 理。因此,我的个人经验是,如果您有增长到超过 4096 个 盘区的段(假设使用本地托管的表空间),则可以考虑将其移动到盘区大小更适合于段大小的表空间。如果 您有 15GB 的表,则使用 200MB 的盘区大小比使用 1MB 的盘区大小明显更为有效。出于单独加载数据的目的, 您将节省后端的处理时间,因为数据库不需要在加载过程中分配同等数量的盘区。 3.9.23.9.23.9.23.9.2 创建一个新表空间并把数据移到其中创建一个新表空间并把数据移到其中创建一个新表空间并把数据移到其中创建一个新表空间并把数据移到其中 如果表变得很大,可以新建一个新表空间并把数据移到这个新的位置中,以减少盘区的数量。在下面 的示例中,customer 表被分成 100 个盘区,每个盘区为 1MB。可以通过 DBA_EXTENTS 视图查询到该盘区信 息。 select SEGMENT_NAME, BYTES from DBA_EXTENTS where SEGMENT_NAME = 'CUSTOMER'; SEGMENT_NAME BYTES CUSTOMER 1048576 CUSTOMER 1048576 CUSTOMER 1048576 CUSTOMER 1048576 ..etc. 首先,创建一个表空间,然后在 new_10M_dat 表空间中创建一个叫做 customer1 的新客户表(该表空 间有 10MB 的盘区,可以更好地适应其增长): CREATE TABLE CUSTOMER1 TABLESPACE NEW_10M_DAT AS SELECT * FROM CUSTOMER; 在确认建立表 customer1 之后,删除原先的表和它的所有索引: DROP TABLE CUSTOMER; 现在可以重新命名新表,然后建立它的相关索引(根据表的大小,重建索引可能非常耗费时间): 108 RENAME CUSTOMER1 TO CUSTOMER; 新的 customer 表的尺寸现在只有 10 个 10MB 的盘区。如果客户表增长得太快,可以把数据移到一个 有着更大的统一盘区大小的表空间里,以适应表的增长。尽管仍然要重建客户表的索引(这个方法的一个缺 点),但这能保证这个表不会从数据库物理存储中丢失,直到创建新表。还可以使用 COPY 命令,这样可以 避免使用回滚段。也可以使用 NOLOGGING 选项(从 8i 版本开始)以避免重做问题。下面列出的两个示例显示 了使用 NOLOGGING 特性的例子。 下面的示例中显示了使用 NOLOGGING 创建表: create table orders_temp as select * from orders nologging; Table created. 下面是使用 NOLOGGING 创建索引的示例: Create index ot_idx1 on orders_temp (order_no) nologging; Index created. 技巧: 在重建一个有问题的表时,使用 NOLOGGING 选项可避免重做。 3.9.33.9.33.9.33.9.3 导出和重新导入表导出和重新导入表导出和重新导入表导出和重新导入表 可以通过导出操作,在新的表空间中手动地创建表,然后用 ignore=y 选项重新导入表,从而做到重 定位表。不要忘记包含索引、授权和约束(在导出和导入时把它们的导入参数设定为 Y),确保已经为导入 操作创建了一个足够大的回滚段(如果没有使用自动撤消)。通过使用那些用于修改表空间位置的索引文件, 就可以很方便地把索引移到一个有较大盘区尺寸的表空间里。可以通过设定 COMMIT=Y 来最小化所需的回滚 区域(在导入表时提交),然后还可以把缓冲设得更大,这样可以增加导入的速度。下面的程序描述了这项 操作。 Export the CUSTOMER Table Import the CUSTOMER table with indexfile=[file_name] Modify indexfile file to create table in new location. (Optional: Modify index locations also) Drop the CUSTOMER Table Create the table from the modified script. Import the CUSTOMER Table (Ignore=y) 在导出时请不要使用 compress=y 的 压缩选项。它可能是减少碎片的一个方法,该选项虽然可用,但 还是应避免使用该选项。首先,如果仍在使用一个字典托管的表空间,就会破坏统一的盘区分配。其 次, 如果使用了本地托管的表空间,这也会让人迷惑,因为数据库中的对象视图可能报告最初盘区的值比实际 的大小要大很多。使用导出/导入可能比前面一节介绍的使用 SQL 重新创建表要快,但如果在您想执行导入 之前,导出文件中发生了什么问题,就会出现错误。因此首先在新的表空间中建立一个表是一步关键的操 作,因为导入操作通常都会把表放回到原来导出的地方。 109 技巧: 如果要加快对表的导入操作,可以使用 indexes=n 选项,避免在导入操作时创建索引。 因为已经有了修改表位置的脚本文件,这样就可以在导入重新创建的索引后执行这个文 件。 3.9.43.9.43.9.43.9.4 正确设定比例以避免链化现象正确设定比例以避免链化现象正确设定比例以避免链化现象正确设定比例以避免链化现象 在表中创建行时,将数据写入到块并向该行提供一个 ROWID。ROWID 标识数据在磁盘上的位置。如果 更新某行,则将改动写入到磁盘上的相同位置。行的 ROWID 不变。当数据块没有足够空间来保存一行或该 行最近的修改结果时,就会发生行链化现象。一个链化行通常存在于多个块而不是一个块上。为同一行访 问多个块,这样的操作非常消耗性能。如果要检查是否碰到了链化问题,可以执行 Oracle 所提供的创建 chained_rows 表的 utlchain.sql 脚本。utlchain.sql 文件是 Oracle 自带的文件,位于 ORACLE_HOME 的 /rdbms/admin 子目录下。也可以使用企业管理器,或者在 STATSPACK 或 AWR Report 中查找“fetch by continued row(获取连续行)”以检测链化行。必须每周检查一下是否有链化现象,并立即解决出现的问题。 如果要分析表(这个例子中的 customer)中链的数量,可以执行下面的查询语句。 ANALYZE TABLE CUSTOMER LIST CHAINED ROWS; 然后,运行下面的查询,访问 CHAINED_ROWS 表来检查 customer 表上的链化现象: select HEAD_ROWID from CHAINED_ROWS where TABLE_NAME = 'CUSTOMER'; 如果没有返回任何行,就表明没有任何链化问题。如果存在了链化问题,查询将返回所有链化行的 head_rowid。也可以对 CHAINED_ROW 表执行 select “count(*)”以查找链化行的数量。在视图 V$SYSSTAT 中,table fetch continued row 同样可以表示链化行。 为了避免行链化现象,可以正确地设定 PCTFREE(为更新一个块所保留的空间)。在创建表的时候就可 以设定这个参数,默认值为 10(为更新操作而保留的 10%的空闲空间),但如果表上经常发生更新操作,这 个值就必须设得大一点。 顺便提及的是,如果您有很少进行更新或完全不进行更新的表,则可以设置 PCTFREE 为相对较小的值, 以确保更多的行适合于块,从而节省表中的空间。 技巧: 可以通过访问 CHAINED_ROWS 表来发现链化问题。可以设定正确的 PCTFREE 来避免链化问 题。 3.9.53.9.53.9.53.9.5 自动段空间管理自动段空间管理自动段空间管理自动段空间管理 在 Oracle Database 的 9i 版本中,Oracle 引入了自动段空间管理(Automatic Segment Space Management),作为在本地托管表空间中使用空闲列表的备选方法。这种管理段中空闲空间的系统利用位图 来跟踪块中可用于行插入的空闲空间的数量。在启用 ASSM 的情况下不再使用空闲列表,因此可以最大程度 110 地减少数据库所需的总时间和资源。 在 10g 版本中,数据库进一步扩展了 ASSM 的特性集,并且为 ALTER TABLE 和 ALTER INDEXES 语句提 供了新的子句。SHRINK SPACE 子句从根本上合并段中的空闲空间,并且释放未使用的空间以减少段。这些 操作可以改进对该段执行的查询的性能,并且比通过导出/导入或移动/重命名操作减少段尺寸更容易实现。 参考关于该特性的文档以了解相关限制和约束。 ASSM 应该能够改进段中块管理的整体性能,但是在将空闲空间位图用于块使用的体系结构中,ASSM 可能会降低性能。小型表(小于 1000 行)的全表扫描需要获得比非 ASSM 表空间更多的缓冲区。因此,如果 按照段的大小组织表空间,则只应该对具有大型段媒介的表空间使用 ASSM。 ASSM 具有显著地改进块管理性能的潜力,但是需要进行仔细的研究,因为在不同数据库版本之间分散 着一些故障,这些故障可能在特定的情况下影响到您的工作。在对您的系统实现 ASSM 之前,确保研究在表 空间中使用的段类型,并且根据您的数据库版本检查对这些段类型执行操作的相关问题。大多数故障都有 相应的补丁,可以应用这些补丁以解决问题。 技巧: 使用 ASSM 代替 FREELISTS、FREELIST GROUPS 和 PCTUSED。 3.9.63.9.63.9.63.9.6 重建数据重建数据重建数据重建数据库库库库 尽管您不会经常执行该操作,但看起来越来越多的人将该操作视为进一步执行操作的必要步骤。重建 数据库有时是因为用户决定将数据库移动到新的平台(例如 Linux),另一种情况则是 10g 版本中的一些新 特性最好在“新”数据库中实现。然而,需要注意的是,重建数据库是非常大型的步骤,因此极为耗费时 间(例如花费整个周末的时间),并且只应该由高级 DBA 来完成。重建数据库不能保证改进性能。因此,应 该预先计划、测试,并且在确实需要时才执行重建数据库的操作。为了重建数据库,高级 DBA 应该可以按 照下面的步骤来做: (1) 完成一个全数据库导出。 (2) 完成一个全映像备份,如下所示: ● 数据库文件 ● 控制文件 ● 联机重做日志文件 ● init.ora 文件 (3) 使用 CREATE DATABASE 命令重建数据库。 (4) 确保有一个足够大的回滚段和一个临时表空间,用来处理导入数据库和创建索引。 (5) 导入整个数据库。 详细内容可以参考 DBA Administrators’Guide 一书。 111 3.103.103.103.10 增加日志文件尺寸和增加日志文件尺寸和增加日志文件尺寸和增加日志文件尺寸和 LOG_CHECKPOINTLOG_CHECKPOINTLOG_CHECKPOINTLOG_CHECKPOINT _INTERVAL_INTERVAL_INTERVAL_INTERVAL 以提以提以提以提高速度高速度高速度高速度 如果想使大量的 INSERT、UPDATE 和 DELETE 操作速度更快,可以增大日志文件大小,并确保这些文件 在最快的磁盘上。以前,如果设置 LOG_CHECKPOINT_INTERVAL 参数,用以在日志切换之前执行定点检查, 我们还可以增大这个参数,但现在已经不提倡使用它,当前默认值都为0(这表明基于重做日志的切换已满)。 Oracle 依 赖于联机重做日志来记录事务处理。每次数据库中发生一次事务处理,联机重做日志文件 中就会增加一个条目。如果增大分配给日志的空间,就可以提高性能。没有 提交的事务同样会生成日志条 目,因为它们生成撤消记录,这些撤消记录也写入到重做日志。可以在一个大的批处理事务中查看日志轮 选(logs spin)。但当想做些修改的时候,请一定要记住以下几点技巧: ● 当数据库启动或将要停机时,日志文件必须联机并可用(有一些选项可以让数据库立刻停止运 行)。 ● 联机重做日志可以循环使用,而脱机重做日志文件可以自动创建(如果激活归档功能)。 ● 脱机日志就是已关闭以用于归档和备份的日志。 ● 最少应该有两个联机重做日志文件。如果联机重做日志文件丢失,则推荐使用联机重做日志文件 多路复用(额外的副本)。 ● 应该在创建数据库时指定初始的日志文件数目和它们的大小。 ● 可以使用 ALTER DATABASE 命令开启或关闭归档日志。 ● 检查点就是将重做日志中已提交的事务写入到数据库的时段。 3.10.13.10.13.10.13.10.1 确定重做日志文件的大小是否存在问题确定重做日志文件的大小是否存在问题确定重做日志文件的大小是否存在问题确定重做日志文件的大小是否存在问题 在此我们将提到两个可能出现的问题。首先提到的就是批处理任务,该任务可能没有足够的完整重做 空间来完成,或是因为速度快,以致联机重做日志在归档到脱机重做日志前即已切换(使用了所有的重做日 志,并且开始再次写入第一个重做日志)。联机重做日志只有在归档(启用归档时)后才可以被重写,因此 DML 和 DDL 活动必须等待,直到有可用的联机日志。在操作系统级别上,按它们最近的更新日期和时间列 出联机重做日志,您可以判断它们切换的是否频繁。还可以用查询 V$LOG_HISTORY 来得到最近的 100 个日 志交换记录。如果增加了联机重做日志的大小,就可以为那些执行大型的 INSERT、UPDATE 和 DELETE 操作 的批处理任务提供足够的空间。较好的解决办法是增加联机重做日志的数目,这样就可以在有很频繁的日 志切换(很小但却非常多的联机重做日志)时提供足够的空间。 第二个要考虑的问题就是那些要长时间运行的任务,这些任务可能要花大量的时间来切换联机重做日 志。当整个任务正好只需用到一个联机重做日志时,这种长时间运行的任务可能非常快。对于联机事务处 理(OLTP)类型的环境来说,最好使用较小的重做日志。我的经验是每半个小时(不考虑可以缩短这段时间的 一些长时间运行的批处理操作)就切换一次联机重做日志。通过在操作系统级别上监控联机重做日志发生的 日期和时间(或查询 v$log_history),就可以确定增加联机重做日志的大小或数目,从而设置一个优化的 切换时间间隔。 下面的查询显示了两个日志切换的间隔时间,这样我们可以方便地判断系统是否存在问题。 select b.recid, to_char(b.first_time,'dd-mon-yy hh:mi:ss') start_time, a.recid, to_char(a.first_time,'dd-mon-yy hh:mi:ss') end_time, round(((a.first_time-b.first_time)*25)*60,2) minutes 112 from v$log_history a, v$log_history b where a.recid = b.recid+1 order by a.first_time asc / 3.10.23.10.23.10.23.10.2 确定日志文件的大小和检查点的时间间隔确定日志文件的大小和检查点的时间间隔确定日志文件的大小和检查点的时间间隔确定日志文件的大小和检查点的时间间隔 可以在操作系统级别上检查联机重做日志文件的大小或查询 V$LOG 和 V$LOGFILE 表,以此来确定这些 文件的大小。下面列出的查询中显示了关于重做日志的显示信息: select a.member, b.* from v$logfile a, v$log b where a.group# = b.group#; MEMBER GRP# THRD# BYTES MEMBERS STATUS /disk1/log1a.ora 1 1 2048000 2 INACTIVE /disk1/log2a.ora 2 1 2048000 2 CURRENT /disk2/log1b.ora 1 1 2048000 2 INACTIVE /disk2/log2b.ora 2 1 2048000 2 CURRENT (partial columns listed only...) 这个查询结果输出了两组日志文件。每一组都有两个日志文件(一个主文件和一个多路复用文件)。包 含在文件/disk1/log1a.ora 和/disk2/log1.ora 里的数据其实都相同(多路复用文件用于提供可用性和可 恢复性)。 技巧: 增加日志文件的大小,从而增加处理大型 INSERTS、DELETES 和 UPDATES (DMLs)操作的 比例。 1. 1. 1. 1. 其他有其他有其他有其他有帮助的重做日志命令帮助的重做日志命令帮助的重做日志命令帮助的重做日志命令 使用 ALTER DATABASE ADD LOGFILE. . .命令创建较大的日志,然后删除较小的日志,以此来增加额 外的日志。需要注意的是,基于在 init.ora 文件中为 CHECKPOINT_ INTERVAL 指定的块数量,检查点的时间间隔会强加一个检查点。因此,如果增加联机重做日志文件的尺寸, 则要确保也增加检查点的时间间隔。为了多路复用联机重做日志文件(创建一个镜像副本),可以使用如下 的命令将日志文件添加到已有的组: alter database add logfile member '/disk2/log1b.ora' to group 1; alter database add logfile member '/disk2/log2b.ora' to group 2; 可以用下面的命令删除某个联机重做日志成员。 alter database drop logfile member '/disk2/log2b.ora'; 如果要添加一个新的联机重做日志组,可使用下面的命令: alter database add logfile group 3 ('/disk1/log3a.ora') size 10M; 113 要删除整个日志组(所有副本),可使用下面的命令: alter database drop logfile group 1; 注意: 不可以删除重做日志文件组,这样做会造成重做线程包含少于两个的重做日志文件组。 要切换日志文件,可以用下面的命令: alter system switch logfile; 技巧: 如果计划写入大量信息,可将重做日志放在最快的磁盘上。试着把重做日志放在磁盘的 外圈部分(最快的部分)。 技巧: 在正常的业务条件下,如果低于每半个小时切换一次日志(不包括不频繁的批处理任务), 就增加联机重做日志大小。如果在处理大型批处理任务时频繁进行切换,就增大联机重 做日志的数目。 2. 2. 2. 2. 其他初始参数其他初始参数其他初始参数其他初始参数 也必须了解一下如下一些 init.ora 参数,这些参数对联机日志文件的性能有一定的影响: ● LOG_ARCHIVE_DEST_n 带有归档前缀(arch)的目录位置。这个位置用来写入归档日志的额外副本 (如果重做日志已填满,并只在 ARCHIVELOG 模式下归档)。如果有足够的空间,该参数就可以在发生归档错 误时提供保护。 ● LOG_ARCHIVE_MIN_SUCCEED_DEST 如果使用 LOG_ARCHIVE_DEST_ n,则可以设置为 1 到 10 之间的 值。如果使用 LOG_ARCHIVE_DEST 和 LOG_ ARCHIVE_DUPLEX_DEST,则可以设置为值 1 或 2。该参数指可成功 写入到重做日志中的归档的最小数目。如果将其设置为 2,则有两个强制性的归档目的地,类似于设置 LOG_ARCHIVE_DEST1 和 LOG_ARCHIVE_DEST2。如果将其设置为 1,则 LOG_ARCHIVE_DUPLEX_DEST 就是可选的 全力服务归档位置,而不是强制性的位置。 ● DB_WRITER_PROCESSES 指的是当一个数据库写入器不够时,把读数据块从 SGA 写入到磁盘的数 据库写入器的数目。 ● DBWR_IO_SLAVES 如果不能使用多个进程(或者没有异步 I/O 却又想模拟该操作),可以用 DBWR_IO_SLAVES 把负载分布到多个从属 I/O 中。如果 DB_ WRITER _PROCESSES>1,则不可以使用该参数。 3.113.113.113.11 闪回恢复闪回恢复闪回恢复闪回恢复 闪回恢复区是用于维护磁盘上的恢复信息以改进恢复时间的新机制。10g 版本中的这个新特性极大地 改进了功能,用户可以从数据库问题中更快速地恢复,但是需要许多额外的磁盘空间来实现。出于这个原 因,许多人倾向于使用较为缓慢的、较为廉价的存储设备来维护闪回恢复区。根据您的实现方式,这可能 114 会对您的环境引入一些潜在的性能问题。Oracle 建议用户在另一个磁盘阵列上有用于闪回恢复的相等尺寸 的磁盘组: ● 如果将较慢的执行磁盘用作闪回恢复区,则重做操作的性能会受到负面影响,因为所有的重做活 动都由最慢的执行设备控制。 ● 考虑是否需要有非常大的闪回恢复区,并且找出最适合于您的闪回需求或预算的闪回恢复区尺 寸。 ● 当备份和其他活动利用闪回恢复区时,大型的闪回恢复区就会降低写性能。 不要在较大的范围内实现闪回恢复,而是考虑使用较小的闪回恢复区,其中带有两个用于重做的磁盘 组。这看起来更类似于有两个归档日志目的地。该配置有如下的优点: ● 由于闪回恢复区频繁地只用于归档和闪回数据库写入,因此它不会带给磁盘过多的负担。 ● 该配置仍然会维护冗余的重做区域。 3.123.123.123.12 增加恢复的可能性增加恢复的可能性增加恢复的可能性增加恢复的可能性::::在每次批处理后提交在每次批处理后提交在每次批处理后提交在每次批处理后提交 如果要提高恢复大型批处理过程的可能性,可以在每次批处理过程之后执行 COMMIT 操作。尽管这样 减缓了处理过程,但是在必须要恢复时,这就节省了时间。一个大型的批处理进程实际取决于您的系统有 多大,但一个会占用十多个小时的任务必定可以被分成一些小的、周期性提交的任务,这样就不必在任务 执行期间出现错误时回滚整个任务。 3.133.133.133.13 使用回滚段使用回滚段使用回滚段使用回滚段 回滚段保持了在一次更新或删除期间内的数据快照(前像)。如果初始参数 UNDO_ MANAGEMENT=MANUAL,那么就可以像 Oracle 前面的版本一样使用回滚段(在本节里要提到)。如果初始参数 UNDO_MANAGEMENT=AUTO,就使用撤消表空间管理功能(在下一节将讲到)。 当回滚事务时,就要用到数据快照。如果把您的数据库设置为使用回滚段,请为各个回滚段保留多个 表空间,这样用户就不会在同一个表空间(通常也就是相同的物理磁盘)中出现争用现象: Tablespace1/disk1: rbseg1 Tablespace2/disk2: rbseg2 Tablespace3/disk3: rbseg3 Tablespace4/disk4: rbseg4 Tablespace1/disk1: rbseg5 Tablespace2/disk2: rbseg6 Tablespace3/disk3: rbseg7 Tablespace4/disk4: rbseg8 3.13.13.13.13.13.13.13.1 避免回滚段之间的争用避免回滚段之间的争用避免回滚段之间的争用避免回滚段之间的争用 原则上讲,您不会希望有多个用户同时使用一个回滚段。尽管通常人们都建议回滚段的数目应该能保 证每个回滚段上能同时进行 4 个事务处理,但您还是应该基于您的系统需要来设定回滚段的数目,而不是 用上面所讲的指导原则。 115 3.13.23.13.23.13.23.13.2 监控回滚段的等待和争用监控回滚段的等待和争用监控回滚段的等待和争用监控回滚段的等待和争用 可以使用 Oracle 的企业管理器来检查回滚段的碎片和使用情况。v$视图同样也可以用来监控回滚段 的等待和争用状况。如下所示的 SQL 语句同样也可以用来显示回滚信息: select a.name, b.extents, b.rssize, b.xacts, b.waits, b.gets, optsize, status from v$rollname a, v$rollstat b where a.usn = b.usn; NAME EXTENTS RSSIZE XACTS WAITS GETS OPTSIZE STATUS SYSTEM 4 540672 1 0 51 ONLINE RB1 2 10240000 0 0 427 10240000 ONLINE RB2 2 10240000 1 0 425 10240000 ONLINE RB3 2 10240000 1 0 422 10240000 ONLINE RB4 2 10240000 0 0 421 10240000 ONLINE 检查输出结果可以确定是否需要增加回滚段。对所有的回滚段而言,如果 xacts(活动事务)经常超出 1(或者您选择的每个回滚段的用户限度),就需增加回滚段的数目,以避免可能出现的争用。如果 waits 大 于 0,也要增加回滚段的数目来避免争用。 3.13.33.13.33.13.33.13.3 增加回滚段增加回滚段增加回滚段增加回滚段 如果需求很高,可以通过添加回滚段来增加回滚段的数量;例如,向 rollback2 表空间添加一个叫做 rb9 的回滚段,如下所示: Create rollback segment rb9 tablespace rollback2 storage (initial 1M next 2M); 技巧: 可以试着把每个回滚段的活动用户数目保持为 1。为此,可以通过监控每个回滚段上用 户的数目,并根据需要增加回滚段来做到这点。这样可以最小化等待和争用。 3.13.43.13.43.13.43.13.4 把大的事务隔离到它们自己的回滚段上把大的事务隔离到它们自己的回滚段上把大的事务隔离到它们自己的回滚段上把大的事务隔离到它们自己的回滚段上 当使用回滚段时,请记住批处理过程可能会要求一个大的回滚段。可以设置 SET TRANSACTION USE ROLLBACK SEGMENT ROLLBIG 来满足大的事务需要。如果您用了自动撤消模式,就不能这样做。下面给出了 一个示例。同样还要注意的是,要在 SET TRANSATION 语句之前标上 COMMIT,即使这是会话的第一个命令。 commit; set transaction use rollback segment rb_big; delete from big_table; commit; 技巧: 如果错误地对一个给定的 UPDATE、INSERT、DELETE 或批处理任务使用了 SET TRANSATION USE ROLLBACK SEGMENT ROLLBIG 命令,就可能导致回滚段变成碎片,而且可能会使回滚 段对于相应的表空间来说显得过大。SET TRANSACTION 命令必须用在 COMMIT 语句或者是 116 段对于相应的表空间来说显得过大。SET TRANSACTION 命令必须用在 COMMIT 语句或者是 一个回滚进程之后。 根据事务需要,回滚段的大小可以动态扩展至它所在的表空间的全部可用空间大小(事务不可以跨越 多个回滚段)。在回滚段上使用 OPTIMAL 存储选项可以动态地把已扩展的回滚段缩小到指定大小。尽管大的 事务前应有 SET TRANSACTION 语句,但最好还是设置一下 OPTIMAL 选项,这样回滚段就不会因为随意扩展 而不能回到它的初始大小,同时释放磁盘空间。使用如下的语句可以把一个回滚段设置为 OPTIMAL: alter rollback segment rb1 storage (optimal 15M); 注意,还可以在创建回滚段时使用 OPTIMAL 存储设置。 还可以用如下的命令来强制一个回滚段收缩到它的优化设置或者是指定大小: alter rollback segment rb1 shrink; alter rollback segment rb1 shrink to 15M; 技巧: 不要指望设定 OPTIMAL 来频繁收缩回滚段,因为这个过程是以消耗性能为代价的。 OPTIMAL 设置很少处于激活状态。同时,注意到您不能把回滚段收缩到比其开始两个盘 区的组合值还小。 3.13.53.13.53.13.53.13.5 更简便的方法更简便的方法更简便的方法更简便的方法::::UNDOUNDOUNDOUNDO 表空间表空间表空间表空间 在 9i 中,Oracle 提出了一个在数据库中管理 ROLLBACK 或 UNDO 数据的新办法。这个办法就是在数据 库里创建 UNDO 表空间,这样就大大简化了对这些事务的管理。在回滚段中,撤消块会根据需要被新的事务 重写。这就会让 DBA 产生疑惑,因为他们要保证有足够大的回滚段可用于事务,从而避免执行大的事务时 发生 Snapshot Too Old 错误,并且保证有足够的回滚段来维护数据库中的常规活动,从而最小化回滚段段 头争用。 为了利用自动撤消管理,可以在数据库中创建 UNDO 类型的表空间: create undo tablespace UNDOTBS1 datafile '/u01/oradata/prod/undotbs1_01.dbf' extent management local uniform size 256k; 接下来,修改初始文件以指定 3 个新的参数: ● UNDO_MANAGEMENT=AUTO ● UNDO_TABLESPACE=UNDOTBS1 ● UNDO_RETENTION = <# of minutes> 警告: 如果将 UNDO_MANAGEMENT 设置为 AUTO,并且没有设置 UNDO_ TABLESPACE,则使用 SYSTEM 表空间(避免这样做)。 UNDO_RETENTION 初始参数用来指定撤消数据被提交之后保留在表空间中的时间值(秒)。这是使用撤消 117 表空间的真正优点,因为不同于传统的回滚段,数据库至少会尝试为长期运行的查询维护较早版本的数据。 另一个优点是,如果您遇到没有维护足够撤消信息的问题,只需要向表空间添加更多的空间,并且增加 UNDO_RETENTION 的值。 然而,需要注意的是,如果表空间上没有足够的保存撤消记录的空间,它就会根据需要重新使用那些 未终止的撤消空间,而不考虑 UNDO_RETENTION 设置值。如果使用自动撤消管理,则不可以使用特定的回滚 段。 3.13.63.13.63.13.63.13.6 监控监控监控监控 UNDOUNDOUNDOUNDO 空间空间空间空间 V$UNDOSTAT 视图可用于监控实例中当前事务使用 UNDO 空间的情况。视图中的每行列出了每隔十分钟 从实例中收集到的统计信息。每行都表示了在过去的 24 小时里每隔十分钟 UNDO 空间的使用情况、事务量 和查询长度等信息的统计快照。 由于回滚段和撤消段可在同一个数据库中使用,V$ROLLSTAT 视图也可以显示 UNDO 表空间中撤消段的 信息,前提是在自动撤消管理模式下。UNDO 表空间的实际盘区信息可以从数据库的 DBA_UNDO_EXTENT 视图 中得到。如果设置 UNDO_MANAGEMENT 为 AUTO,DBA 就不可以管理撤消段。如果创建 UNDO 表空间,则可以在 UNDO 表空间中创建回滚段,但是并不推荐用户这样操作。使用撤消段时,它们在 UNDO 表空间中实现为回 滚段。这就是 V$ROLLSTAT 的工作原理。 3.143.143.143.14 结束有问题的会话结束有问题的会话结束有问题的会话结束有问题的会话 如果碰到了临时的资源需求高峰时,可以用操作系统的 KILL 命令停止一些低级别的、有问题的会话。 但首先,您必须找到是哪些用户正在运行那些消耗系统资源的任务。下面的命令可以帮助您找到并去掉有 问题的用户: select sid, serial# from v$session where username = 'BADUSER' 下面是查询输出: SID SERIAL# 5 33 用下面的命令 KILL 这个会话: alter system kill session '5,33'; 还可以用如下的代码来确定哪个回滚段在处理事务,以及相应的用户和 SQL 语句。您还可以修改一下 输出结果,以方便查看。 select a.name, b.xacts, c.sid, c.serial#, c.username, d.sql_text from v$rollname a, v$rollstat b, v$session c, v$sqltext d, v$transaction e where a.usn = b.usn and b. usn = e.xidusn and c.taddr = e.addr 118 and c.sql_address = d.address and c.sql_hash_value = d.hash_value order by a.name, c.sid, d.piece; name xacts sid serial# username sql_text RB1 1 5 33 USER1 delete from test1; RB2 1 7 41 USER9 update orders set items = 5 where orderno = 555; 前面的输出还显示了当前使用回滚段的用户。如果也选择 WAITS 列,则输出显示了有多少个用户在使 用或等待同一个回滚段。 技巧: 把大的事务隔离在它们自己的回滚段中,但是在 10g 版本中,使用 UNDO 表空间可以简化 撤消管理。 3.153.153.153.15 不要在不要在不要在不要在 SYSTEMSYSTEMSYSTEMSYSTEM 或或或或 SYSAUXSYSAUXSYSAUXSYSAUX 表空间中执行排序表空间中执行排序表空间中执行排序表空间中执行排序 若将初始参数 SORT_AREA_SIZE、HASH_AREA_SIZE 或 PGA _AGGREGATE_ TARGET 设得太小而无法在内存 中排序,这样可能导致 SYSTEM 表空间里的碎片。发生碎片的原因可能是临时表空间用来对磁盘排序,而不 是对内存排序。在设置系统时,一定要记住临时表空间有如下特点: ● 如果没有指定临时表空间或表空间组,则用户的临时段存储在数据库默认的临时表空间中;如果 全部没有指定,则存储在 SYSTEM 表空间中。可以使用 ALTER USER 或在 CREATE USER 命令中另行指定。 ● 在内存不足以处理整个数据集时就要用到该空间。 ● 它存储由如下语句生成的临时段: Create Index select.... Order By select.... Distinct select.... Group By select.... Union select.... Intersect select.... Minus Unindexed Joins Some Correlated Subqueries 为了减少碎片发生的可能,可以配置初始参数 SORT_AREA_SIZE 或 PGA_ AGGREGATE_TARGET(应该在 10g 版本中使用该初始参数)来减少与上述 SQL 命令相关的磁盘排序(可以在第 4 章里看到更多调整初始参数的 内容)。同时,还要创建具有统一盘区大小的临时表空间。这样,可以使用下面的语句将用户定向到这个临 时表空间: SQL> ALTER USER username TEMPORARY TABLESPACE temp1; 同样,使用 ALTER DATABASE 语句,为将来创建的所有用户设置默认临时表空间为 TEMP1。 119 临时表空间同时还可以采取本地托管。如果您使用的数据库支持本地托管的表空间,则应让您的临时 表空间采用本地托管,这样可以减少磁盘排序操作时的磁盘争用。本地托管的临时表空间使用临时文件 (tempfile)。临时文件附带了一些可以提高性能的特征,但使用时也需要特别注意。如果使用命令 CREATE TEMPORARY TABLESPACE,临时表空间就采取本地托管,而不可以采用字典托管。只有在表空间名后使用 TEMPORARY 关键字时,才可以使用字典托管的临时表空间。然而,已经不再使用该语法。 本地托管的临时表空间有如下特性: ● 不会产生撤消信息,因此也不会因这些操作而产生对磁盘的影响。 ● 不会带有字典托管的临时表空间所导致的相同字典盘区开销。 ● 不会在创建时分配整个临时文件。也就是说您可以增加一个 2GB 的临时文件,但直到使用时,它 才会真正占用 2GB 的大小。 ● 不像其他的数据文件一样在控制文件中被维护。因此,使用 RMAN 这样的实用程序无法控制它们。 但是没有人会关心这一点。如果没有备份临时表空间,并且该表空间崩溃或损坏,则只要重新创建一个新 的临时表空间即可。 3.163.163.163.16 在不同磁盘和控制器上存放多个控制文件在不同磁盘和控制器上存放多个控制文件在不同磁盘和控制器上存放多个控制文件在不同磁盘和控制器上存放多个控制文件 控制文件存储了与启动、关闭和归档相关的信息。由于您的系统至少需要一个很好的控制文件才能正 常运行,所以必须在不同的磁盘和控制器上保存 3 份控制文件的副本(如果可能的话)。如果在数据库打开 的情况下碰巧删除了所有的控制文件,则可以使用 ALTER DATABASE BACKUP CONTROLFILE 命令生成新的控 制文件。如果数据库关闭并且控制文件遗失,则可以使用 CREATE CONTROLFILE 语句重新创建控制文件。然 而,从头开始重新创建控制文件需要大量的工作,很容易产生错误,并且会丢失大量有价值的、关键的信 息(例如,RMAN 备份的最新备份信息)。可以运行如下的查询来查看当前的控制文件。 select name, value from v$parameter where name = 'control_files'; NAME VALUE control_files /disk1/ora10/ctl1.ora, /disk2/ora10/ctl2.ora, /disk3/ora10/ctl3.ora 3.173.173.173.17 对写操作频繁的数据使用裸设备来提高对写操作频繁的数据使用裸设备来提高对写操作频繁的数据使用裸设备来提高对写操作频繁的数据使用裸设备来提高 I/OI/OI/OI/O 裸设备就是未经格式化的磁盘分区(物理磁盘的一部分),Oracle 可以对它进行读和写操作,而没有 UNIX/IO 缓冲开销(裸设备的内容不是由操作系统管理)。尽管裸设备可以提高性能,但在其他方案流行时, 它们也变得不那么吸引人。使用裸设备能提高性能的说法大都是来自硬件销售商代表。现在并没有广泛地 使用裸设备,这是因为很少有使用了裸设备就可提高性能的可信证据。在我的测试中,裸设备在增加了维 护代价的同时提高了 5~10%的性能。但对于大型的数据仓库而言,裸设备可以提供极佳的性能,无疑要进 行研究。在 Oracle 9.2 中,如果使用集群文件系统,那么 Oracle RAC 不再需要使用裸设备。但是根据需 要,在 10gR2 版本中支持裸设备。 3.17.13.17.13.17.13.17.1 使用裸设备的好处使用裸设备的好处使用裸设备的好处使用裸设备的好处 当出现如下情况时,应选择使用裸设备(特别是对数据仓库而言): 120 ● 如果 I/O 在您的系统上是主要问题,并且 CPU 经常处于空闲状态。 ● 如果可以在您的平台上使用异步 I/O。 ● 如果您有可变的磁盘分区(可以容易地将磁盘分片)。这样,裸设备可以用于写操作频繁、顺序访 问的数据和重做日志文件,但不能用在备份程序中。 3.17.23.17.23.17.23.17.2 使用裸设备的缺点使用裸设备的缺点使用裸设备的缺点使用裸设备的缺点 尽管使用裸设备优点有很多,但也存在有很多缺点: ● 对裸设备的管理代价较高。由硬件供应商提供的大多数常用操作系统的备份程序不支持裸设备。 ● 如果 I/O 没有瓶颈问题,裸设备就不能起多大帮助作用。 ● 如果无法使用可变的磁盘分区,您就必须为一个文件分配比它所需更大的空间,这样肯定会造成 空间浪费(非常常见)。 ● 如果想在生产环境中使用裸设备,Oracle 建议应该在使用裸设备之前先仔细测试备份和恢复。 3.183.183.183.18 磁盘磁盘磁盘磁盘 I/OI/OI/OI/O 的其他注意事项和提示的其他注意事项和提示的其他注意事项和提示的其他注意事项和提示 在我们查看其他磁盘 I/O 操作时,还要注意下面的注意事项和提示: ● 频繁的批处理需要更大的回滚空间,重做空间和临时表空间。 ● 频繁的 DML 处理(INSERT、UPDATE 和 DELETE)需要更大的回滚空间、重做空间和临时表空间。 ● 对大型表的频繁访问需要更多的 CPU 和内存以及更大的临时表空间。 ● 调整性能较差的系统需要更多的 CPU 和内存以及更大的临时表空间。 ● 大量较好均衡的磁盘和控制器通常可以提高性能(减少了 I/O 争用)。 ● 增大磁盘容量也可以加快备份和恢复操作,这是因为可以在磁盘上保存一个备份的副本,而不是 在磁带上保存。 ● 最后,如果能使用 EMC 和/或固态磁盘方案,这仍是一种提高 Oracle 的 I/O 性能的最好方法。 3.193.193.193.19 设计阶段需要注意的问题设计阶段需要注意的问题设计阶段需要注意的问题设计阶段需要注意的问题 如果您正在设计或更新一个新系统,还要考虑到以下几点: ● 硬件最大的磁盘容量是多大? ● 可用的磁盘大小有多大? ● 数据库初始大小应为多大? ● 数据库将来可能会有多大,其增长度是多少? ● 是否有针对数据库文件或 OS 的 RAID(分段)级别? ● 将使用何种恢复办法? ● 哪个归档方法将用于存储历史信息? ● 系统中应该多久报告一次输出? ● 需要使用哪种开发空间? ● 应该安装什么软件,需要多大的空间才能有效发挥其功能? ● 需要安装什么系统实用程序,需要多大的空间才能有效发挥其功能? ● 需要安装哪种邮件系统? ● 要采用哪种数据传输方式? 121 ● ASM 是否可行?如果可行,学习并设计使用 ASM。 ● 批处理过程有哪些需求,是否有专门的用户查询? ● 如何访问数据会造成潜在的热点? 技巧: 如果您的系统正处于设计阶段,一定要找到与使用当前和未来系统相关的尽可能多的信 息。不要仅考虑到 Oracle 数据库的需求—— 调查一下其他会影响 Oracle 数据库性能的 软件和应用程序。 3.203.203.203.20 技巧回顾技巧回顾技巧回顾技巧回顾 ● 尝试避免将磁盘阵列中的某个逻辑设备划分为多个文件系统。该操作看起来可以提供灵活性,但 它也会增加必须管理的数据文件位置的数量。 ● 使用磁盘阵列改进性能,并且保护数据不受磁盘故障的影响。使用允许您维护组织所需可用性的 适当 RAID 级别和技术解决方案。不要认为已经“足够好”,这样您就会在临晨两点时因为丢失某个磁盘而 感到悔恨。 ● 把关键的 Oracle 数据文件分开放置,这样可以确保磁盘和磁盘缓存争用不会成为瓶颈。把经常 连接的表的数据和索引分开放置,这样,即使是最糟糕的表连接也不会导致磁盘争用。 ● 查询 V$FILESTAT 和 V$DATAFILE 可以观察如何有效地均衡数据文件。使用 V$TEMPFILE 和 V$TEMPSTATS 监控临时表空间。 ● 可以把数据文件移到不经常被访问的磁盘,或者把表移到另一个磁盘上的不同表空间中,这样就 可以解决磁盘争用问题。 ● 使用企业管理器中的数据库文件度量可以确定在每个数据库文件上发生的 I/O。将大量使用的数 据文件移动到单独的文件系统以分布 I/O。查看第 5 章以了解额外的信息。 ● 字典托管的表空间已经不再使用,有许多新的选择,它们一般很少使用。 ● 如果要最小化单个大型表的磁盘 I/O,可以把表划分到多个分区中,这些分区可以存放在不同的 物理磁盘上。 ● 虽然 ASM 不提供多路径功能,但它利用了多路径工具,前提是该多路径工具产生的路径或设备从 fstat 系统调用中返回一个成功的返回码。Metalink Note 294869.1 提供了关于 ASM 和多路径的更多信息。 ● 为了获得精确的实时统计信息,明智的方法是查询 V$ASM_DISK 和 V$ASM _DISKGROUP 视图,但 在峰值工作量期间运行针对这些视图的查询时,需要保持谨慎。 ● init.ora 参数 asm_power_limit 用于影响重新平衡操作的吞吐量和速度。asm_power _limit 的取值范围是 0 到 11,值为 11 表示全速,而值为 0 表示低速。用户应该谨慎使用值 0, 这会关闭自动重新平衡。 ● 如果使用 ASM 删除或添加一些磁盘,最好是一次性添加或删除所有的驱动器。这将减少存储改动 所需的平衡操作的数量。 ● 访问 DBA_TABLES、DBA_PART_TABLES 和 DBA_SEGMENTS 可以得到更多关于已分区表的信息。 ● 分区表可把多个列当作分区的条件。 ● 删除一个表分区可能会导致其本地索引被删除(但不是其他本地分区索引)和全局索引(整个表上 的索引)不可用。如果要删除表的分区,不要使用全局索引。 ● 已分区的索引(本地索引)也应该加上前缀(分区键是索引的开始部分)。 ● 在重建一个有问题的表时,使用 NOLOGGING 可以提高性能。第 11 章介绍了使用 NOLOGGING 的示 例。 122 ● 访问 CHAINED_ROWS 表可以查找链化问题。正确地设置 PCTFREE 可以避免链化现象。 ● 添加一些较大的日志文件并删除较小的日志文件,可以加快大型 INSERT、UPDATE 和 DELETE 语句 的执行速度。 ● 试着把当前每个回滚段的用户数目保持为 1。为此,您可以通过监控每个回滚段上的用户数目和 根据需要来添加回滚段。此举将最小化等待和争用。 ● 如果您的系统正处于设计阶段,一定要找到与当前和未来系统使用相关的尽可能多的信息。不要 只考虑到 Oracle 数据库的需要;调查其他会影响 Oracle 数据库执行性能的软件和应用程序。 3.213.213.213.21 参考文档参考文档参考文档参考文档 TUSC DBA Guide, 1988–2006 DBA Reference Guide (Oracle Corporation) 第第第第 6666 章章章章 使用使用使用使用 EXPLAINEXPLAINEXPLAINEXPLAIN 和和和和 STORED OUTLINES(STORED OUTLINES(STORED OUTLINES(STORED OUTLINES(针对针对针对针对 DBADBADBADBA 和开发人员和开发人员和开发人员和开发人员)))) 查找和修补有错误的查询时在很大程度上要借助于合适的工具。不同的场合需要使用不同的工具。本 章谈到的 Oracle 提供的实用程序包括有 SQL TRACE、TKPROF、EXPLAIN PLAN 和 STORED OUTLINE(也称为 PLAN STABILITY)。在 Oracle 10g 中,这些工具得到了增强,包括增加了 DBMS_MONITOR 程序包和 TRCSESS。 DBMS_MONITOR 程序包的作用是对 SQL 跟踪选项进行集中和扩展。TRCSESS 则是一个命令行工具,开发人员 和 DBA 可以借助该工具将多个跟踪文件中的信息合并到一个输出文件中。 本章主要内容: ● 使用 SQLTRACE/TKPROF 的简单步骤 ● SQL TRACE 输出部分 ● 跟踪更复杂的查询,了解如何确定有用信息来提高性能 ● DBMS_MONITOR——10g 版本的新特性 ● TRCSESS——10g 版本的新特性 ● 使用 EXPLAIN PLAN ● 阅读 EXPLAIN PLAN:从头至尾或从尾至头 ● 使用 DBMS_XPLAN ● 另一种 EXPLAIN PLAN 方法:父/子树结构方法 ● 开发工具中的跟踪 ● 表 PLAN_TABLE 中一些重要的列 ● 跟踪错误和未记录入档(documented)的 init.ora 参数 ● 构建和运用 STORED OUTLINES ● 使用 STORED OUTLINES(PLAN STABILITY)迁移基于规则的优化器中的 SQL 6.16.16.16.1 Oracle Oracle Oracle Oracle 的的的的 SQL TRACESQL TRACESQL TRACESQL TRACE 实用程序实用程序实用程序实用程序 利用 Oracle 的 SQL TRACE 实用程序可以对指定的查询、批处理进程和整个系统做时间统计。它可以 帮助我们彻底地找到系统中可能存在的瓶颈。SQL TRACE 有如下的功能: 123 ● SQL TRACE 运行这个查询并输出一个所执行的 Oracle 查询(或一系列查询)的统计信息。 ● SQL TRACE 帮助开发人员分析查询的每个部分。 通常情况下,Oracle 的 SQL TRACE 实用程序在一个跟踪文件里记录了数据库的所有活动(特别是查询)。 这个跟踪文件由 Oracle SQL TRACE 生成,但它非常难于读取,因此需要用 TKPROF 实用程序把它转换成可 以阅读的格式。 6.1.16.1.16.1.16.1.1 对简单查询使用对简单查询使用对简单查询使用对简单查询使用 SQL TRACESQL TRACESQL TRACESQL TRACE 的简单步骤的简单步骤的简单步骤的简单步骤 下面列出了如何设置并运行 Oracle 的 SQL TRACE 实用程序的步骤: (1) 设置下面的 init.ora 参数(SPFILE 用户将需要使用 ALTER SYSTEM 命令改变这些参数): TIMED_STATISTICS = TRUE MAX_DUMP_FILE_SIZE = unlimited (also see metalink article 108723.1) USER_DUMP_DEST = /oracle/admin/ora9i/udump 参数 TIMED_STATISTICS 允许在系统中执行跟踪操作。USER_DUMP_DEST 参数指定了文件所在位置,而 MAX_DUMP_FILE_SIZE 参数则指定了“设备级中最小物理块尺寸”块中最大的文件大小。这也就是文件能增 大的最大值;如果已经到达最大值,将忽略所有后续记录的数据,它们不会被写入到跟踪文件里,从而可 能会丢失。所有这 3 个参数都可以用 ALTER SYSTEM 命令(对整个系统)来设置,当下一个用户登录时就会起 作用,但不会对当前已登录到系统的用户起作用。可以使用 ALTER SESSION(对一个单独的会话)命令在会 话级别上设定 TIMED_STATISTICS 和 MAX_DUMP_FILE_SIZE。 (2) 对 SQL*Plus 会话启用 SQL TRACE(这样就可以开始对会话进行跟踪): alter session set SQL_TRACE true; 其实还有其他开始和终止跟踪会话的方法,我们将在本章后面会谈到。 (3) 运行需跟踪的查询: Select table_name, owner, initial_extent, uniqueness from ind2 where owner || '' = 'SCOTT' ; --Note: An index on "OWNER" is suppressed (4) 对 SQL*Plus 会话禁用 TRACE: alter session set SQL_TRACE false; 实际可以无需停止跟踪就可以检查这个跟踪文件,但最好还是停止跟踪。运行 SQL TRACE 后,您的输 出文件类似于如下(SID 通常包括在跟踪文件名中): orcl_ora_3372.trc 124 技巧: 在 init.ora 中设置 TIMED_STATISTICS = TRUE 可以收集时间统计。同样,在 10g 版本中, 初始参数 SQL_TRACE 已经不再使用(查看附录 A 以了解更多信息)。 查找已生成的跟踪文件是整个跟踪过程中最轻松的操作。输出文件被命名为 TRACE 会话的进程 ID 号, 并在文件名称中包括这个数字。看一下文件的日期和时间将很容易地发现自己是否是进行跟踪的唯一用户。 在前面的例子里,19544 就是所跟踪会话的进程 ID 号。跟踪的文件名字可能是 ora%或 ora_%,这取决于 执行的跟踪所在的操作系统,而且文件应该出现在初始参数 USER_DUMP_DEST 所设置的文件夹下。找到这个 文件的另外一个方法就是在里面放置一个标记(比如执行查询:SELECT ′Rich1′ FROM DUAL);然后使用 文件搜索实用程序(如 grep 或 Windows 搜索)来找到这个文本。 可以使用如下的查询,在同一个会话中运行,获得包含在跟踪文件名中的数字(假设您可以查看 V$视 图)。 Select spid, s.sid,s.serial#, p.username, p.program From v$process p, v$session s where p.addr = s.paddr and s.sid = (select sid from v$mystat where rownum=1); 注意: 不要忘记向没有适当权限的用户授予在 v_$process、v_$session 和 v_$mystat 上进行选 择的权限。 在命令行中运行 TKPROF,把 TRACE 文件转换成可阅读的格式(该命令将通过 ora_19554.trc 跟踪文件 在当前目录中创建文件 rich2.prf,并且将作为系统管理员登录到数据库,以获得 EXPLAIN Plan 输出): tkprof ora_19554.trc rich2.prf explain=system/manager TKPROF 实用程序把 SQL TRACE 所生成的 TRACE 文件转换成可阅读的格式。可以对先前建立的 TRACE 文件运行TKPROF 程序,或者在创建TRACE 文件的程序仍然在运行时也可以运行TKPROF。表6-1 列出了TKPROF 的一些语法。 TKPROF 的语法如下所示: tkprof tracefile output_file [sort = parameters] [print=number] [explain=username/password] [waits=yes|no] [aggregate=yes|no] [insert=filename] [sys=yes|no] [table=schema.table] [record=filename] 表 6-1 命令行选项 变 量 定 义 Tracefile 这就是包含了 SQL_TRACE 统计信息的 TRACE 文件名 Output_file TKPROF写入其输出的文件名 print = number 包含在输出结果中的语句数目。如果没有包含这条语句,TKPROF 将 在输出中列出所有的语句 Explain = username/password 在 TRACE 文件里对用户的 SQL 语句运行 EXPLAIN PLAN。这个选项可 125 以创建自己的 plan_table,这样用户就需要有创建表和创建表所需 空间的特权。在 TKPROF 运行结束时会删除这个表。确保使用分析光 标(运行查询)的用户的用户名/密码,从而确保由正确的用户进行解 释。查看 Metalink note:199081.1 以了解更多信息 insert = filename 这个选项生成脚本来创建表并为每个所跟踪的 SQL 语句存储 TRACE 文件统计 record = filename 这个选项将生成一个保存用户所有 SQL 语句的文件 Sys = yes|no 这个选项可以在输出结果里不显示请求递归 SQL 语句的用户(由 SYS 用户执行)。默认值为 YES。递归的 SQL 通常包括由内部调用和表的 维护,比如在进行一个插入操作时将扩展添加到表中 sort = parameters 有大量的排序选项可用:FCHCPU(获取的 CPU 时间)、FCHDSK(获取的 硬盘读取)、FCHCU 和 FCHQRY(获取的内存读取)、FCHROW(取出的行 数)、EXEDSK(执行期间的磁盘读取数);EXECU 和 EXEQRY(执行期间 的内存读取数)、EXEROW(执行时处理的行数)、EXECPU(执行的 CPU 时间)、PRSCPU(分析 CPU)和 PRSCNT(分析时间) waits=yes/no 任何等待事件的记录概要 aggregate=yes/no 如果值为 NO,则 TKPROF 不会组合相同 SQL 文本的多个用户 table=schema.table TKPROF在将执行计划写入到输出文件之前临时放置它们的表 下文展示了使用这些选项的一些简单例子。 运行 TKPROF 并列出最前面的 5 个 CPU 时间(取出+执行+分析)结果: tkprof ora_19554 rich2 explain=system/manager sort=(FCHCPU,EXECPU,PRSCPU) print=5 运行 TKPROF 并忽略掉所有递归语句: tkprof ora_19554 rich2 explain=system/manager sys=no 运行 TKPROF 并建立一个表,向其中插入通过跟踪得来的记录: tkprof ora_19554.trc rich2.prf explain=system/manager insert=insert1.ins 下面的程序清单显示了 insert1.ins 的部分输出结果。 REM Edit and/or remove the following CREATE TABLE REM statement as your needs dictate. CREATE TABLE tkprof_table (date_of_insert DATE ,cursor_num NUMBER ,depth NUMBER ,user_id NUMBER ,parse_cnt NUMBER ...etc... ,sql_statement LONG ); INSERT INTO tkprof_table VALUES (SYSDATE, 1, 0, 5, 0, 0, 0, 0, 0, 0, 0 , 1, 0, 0, 0, 0, 0, 1, 0 126 , 0, 0, 0, 0, 0, 0, 0, 4294877296 , 'alter session set sql_trace true'); INSERT INTO tkprof_table VALUES (SYSDATE, 1, 0, 5, 1, 450648, 471000, 0, 46, 2, 1 , 1, 0, 0, 0, 0, 0, 0, 0 , 2, 10015, 10000, 0, 685, 4, 1, 50000, 'select count(*) from emp'); INSERT INTO tkprof_table VALUES (SYSDATE, 1, 0, 5, 1, 0, 0, 0, 0, 0, 1 , 1, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 7481000, 'alter session set sql_trace false'); 运行 TKPROF 并建立一个可以显示跟踪会话的文件: tkprof ora_19554.trc rich2.prf explain=system/manager record=record1.sql 下面的程序清单显示了 record1.sql 文件的输出: alter session set sql_trace true ; select count(*) from emp ; alter session set sql_trace false ; 技巧: TKPROF 实用程序可以把跟踪输出结果转换成可阅读的格式。如果没有运行 TKPROF,将很 难阅读 TRACE 的输出结果。通过指定 explain=username/ password(如上所示),我们就可以得到 EXPLAIN 的执行路径,另外还有这个查询的执行 统计情况。 技巧: 如果要使用多个排序参数,只要在命令行里重复 sort =参数就可以,比如 tkprof source_file out_file sort=parm1 sort=parm2。 select TABLE_NAME, OWNER, INITIAL_EXTENT, UNIQUENESS from IND2 where OWNER = 'SCOTT'; count cpu elapsed disk query current rows Parse: 1 1 2 0 0 0 Execute: 1 0 0 0 0 2 0 Fetch: 2 69 113 142 430 0 36 这就是一个执行计划(没有使用索引): TABLE ACCESS (FULL) OF ′IND2′ 上面的结果将显示有 142 次磁盘读取(物理读取),共 430 次读取(query+current)。内存读取的数量 就是总读取量减去磁盘读取量,即 288 次内存读取(430-142)。相对于物理读取来说,如此高的磁盘读取 数字也是一个潜在的问题,除非运行的是一个数据仓库或者经常需要全表扫描的查询。这个执行计划也说 明全表扫描是我们可能遇到的潜在问题之一。 技巧: 所跟踪的查询如果有大量物理读取,通常表明缺少索引。disk 列显示了物理读取次数(通 常都是没有使用索引的情况),而把 query 列加到 current 列所得到的值就表明总的块读 127 取量(物理读取包括在这个数量中)。一个查询如果有大量查询读取和较少的磁盘读取, 就说明使用了索引。但如果查询读取过高,就表明存在一个差的索引或差的表连接序列。 一个查询如果有大量的当前读取量,通常表明它是一个大的 DML(UPDATE、INSERT、DELETE) 查询。 下面的程序清单显示了我们重新运行所跟踪的查询(重启系统后)时发生的情况,现在已在 OWNER 表上 使用了一个索引。 select table_name, owner, initial_extent, uniqueness from ind2 where owner = 'SCOTT' ; (The index on "OWNER" is not suppressed) 下面的程序清单输出了文件 rich2.prf 的内容。通常经常访问数据的查询有 0 个磁盘读取。第一次 运行查询时,将总是会有磁盘读取。 select table_name, owner, initial_extent, uniqueness from ind2 where owner = 'SCOTT' ; count cpu elapsed disk query current rows Parse: 2 0 0 0 0 0 0 Execute: 2 0 0 0 0 0 0 Fetch: 4 6 6 0 148 0 72 下面显示了这个执行计划(使用了索引): TABLE ACCESS (BY ROWID) OF 'IND2' INDEX (RANGE SCAN) OF 'IND2_1' (NON-UNIQUE) 如果所跟踪的查询输出结果中只有内存读取(与查询相关的读取),那就表明使用了索引。 技巧: 当前在 10gR1 版本中有一个程序故障(在 10gR2 版本中修正);该程序故障导致“ORA-922: missing or invalid option”错误,并且将如下的消息记录到 TKPROF 报告文件中:“Error in CREATE TABLE of EXPLAIN PLAN table: SCOTT.prof$plan_table.”。在 Metalink Note 293481.1 中描述了具体的解决方法。为了实现该问题的解决方案,需要遵循如下的步骤: (1) 在需要放置解释表的模式中运行$ORACLE_HOME/rdbms/admin/utlxplan.sql 脚本。 (2) 运行带有 TABLE 选项的 tkprof: SQL> @?/rdbms/admin/utlxplan.sql ... tkprof EXPLAIN = ... TABLE = PLAN_TABLE ... 6.1.26.1.26.1.26.1.2 TRACE TRACE TRACE TRACE 输出部分输出部分输出部分输出部分 TRACE 实用程序由很多部分组成,包括 SQL 语句、统计、信息和 EXPLAIN PLAN。我们将在下面的几节 128 中逐个讨论这些部分。 1. SQL1. SQL1. SQL1. SQL 语句语句语句语句 TKPROF 语句的第一个部分就是 SQL 语句。这个语句必须跟所执行的语句完全一样。如果在这条语句里 有一些提示或注释,在输出时也必须保留这些内容。这样对您查看不同会话的输出非常有帮助。如果想找 到制造了麻烦的语句,也能据此搜索到该语句。记住,有些 Oracle Forms 的语句都是动态生成的,因此这 个查询的一部分(特别是 WHERE 子句的谓词)就被显示为绑定变量(:1)而不是实际的文本。 2. 2. 2. 2. 统计部分统计部分统计部分统计部分 该部分包括了对这个 SQL 语句以及为了满足该语句而生成的所有递归 SQL 语句的统计。该部分由 8 个 列组成,第一列就是对数据库的调用类型。有3 种调用方式:分析(Parse)、执行(Execute)和取出(Fetch)。 每种调用方式都生成一行单独的统计。Parse 是 SQL 语句自身被放入内存中(共享池的库缓存)的方式, 或者它也可以用于重用确切的光标。Execute 是实际执行语句的方式,而 Fetch 方式则是获取从结果中获 得的数据。其他 7 列就是对每种调用的统计。表 6-2 对此作了总结。 表 6-2 统计部分 列 定 义 Count 这个类型的调用次数 Cpu 这条语句中所有这种类型调用的总 CPU 时间。如果没有把 init.ora 文件里的参数 TIMED_STATISTICS 设为 TRUE,这个统计值和 elapsed 的统计值都是 0 Elapsed 这个调用的总消耗时间 Disk 为满足这次调用而从磁盘检索的数据块数目。这也就是物理读取数 Query 进行这个类型调用时从内存检索的数据缓冲区数目。SELECT 语句通常在这个模式下 检索缓冲区。这也就是一致 get 数 Current 进行这个类型调用时从内存检索的数据缓冲区数目。在这个模式下,UPDATE、INSERT 和 DELETE 通常访问缓冲区,而 SELECT 语句也会使用少量的缓冲区。这也是数据库 块 gets 数 Rows 这条语句处理的总行数。SELECT 语句所处理的行数将出现在 Fetch 统计行中。 Insert、update 和 delete 都出现在 Execute 行中 3. 3. 3. 3. 信息部信息部信息部信息部分分分分 信息部分包含了有关分析和执行调用中丢失的库缓存的数量的信息。如果这个丢失率很高,这就是说共享 池的大小出了问题。应当对库缓存的命中率和重载率进行检查。这部分显示了最近分析这条语句的用户的 用户名。还有一些关于当前优化器模式设置的信息。 4. 4. 4. 4. 行源操作部分行源操作部分行源操作部分行源操作部分 行源操作部分列出了交叉引用行操作中涉及的行的数目。这个输出结果类似于如下。 Rows Row Source Operation ------- --------------------------------------------------- 1 TABLE ACCESS FULL DUAL 129 技巧: 注意,跟踪文件是系统在某一个特定时刻的实时反映。相反,EXPLAIN PLAN(下文将详细 讲到)是在分析 TKPROF 程序清单时生成的,有一定的延迟。行源操作程序清单也是跟踪 文件的一部分,并且可以用来查看是否有数据库对象在执行跟踪后发生了变化。 5. EXPLAIN PLAN5. EXPLAIN PLAN5. EXPLAIN PLAN5. EXPLAIN PLAN 我发现这部分的 TKPROF 信息最有用。这一部分的第一列就是执行计划每一行语句所处理的行数。在 这里可以看到语句到底有多差。与 EXPLAIN PLAN 中每行语句处理的行数相比,如果 fetch 统计中的行数较 低,就得重新查看一下这条语句了。 执行计划里也有可能只用一行语句就处理了大量的行(相比剩余行而言)。这可能是由全表扫描或使用 了差的索引所引起的。 6.1.36.1.36.1.36.1.3 更复杂的更复杂的更复杂的更复杂的 TKPROFTKPROFTKPROFTKPROF 输出输出输出输出 下面的程序清单显示了一个较复杂的使用 TRACE 跟踪的查询。 select Item_Item_Id, InitCap(Item_Description) from Item where Item_Classification = 1 and Item_Item_Id Between 1000000 And 2700000 and Item_Item_Id Not In (Select Invitem_Item_Id from Inventory_Item where Invitem_Location_Id = '405') call count cpu elapsed disk query current rows Parse 1 0.00 0.00 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 27 20.87 21.24 0 4408 0 399 Totals 29 20.87 21.24 0 4408 0 399 Misses in library cache during parse: 0 Optimizer hint: CHOOSE Parsing user id: 106 (C12462) Rows Execution Plan 0 SELECT STATEMENT OPTIMIZER HINT: CHOOSE 572 FILTER 598 TABLE ACCESS (BY ROWID) OF 'ITEM' 599 INDEX (RANGE SCAN) OF 'ITEM_PK' (UNIQUE) 278790 INDEX (RANGE SCAN) OF 'INVITEM_PK' (UNIQUE) 表 6-3 列出了在查看 TKPROF 输出时发现的一些问题。 表 6-3 查看 TKPROF 输出时可发现的问题 问 题 解 决 措 施 130 分析数字太大 应该增大 SHARED_POOL_SIZE 磁盘读取量太高 没有使用索引或根本就不存在索引 query 和/或 current 列值(内存读取)太 高 索引位于低基数的列上(由一个值组成了表中大部分记录的列; 比如 y/n 字段)。删除/限制索引,或使用直方图或位图索引或许 可以提高性能。表连接顺序或连接索引的顺序不好也会发生这个 情况 分析所需要的时间太多 可能是开放游标的数量有问题 EXPLAIN PLAN 里某一行语句要处理的行 数相对于其他行语句而言太多 这可能表明有一个索引对唯一键(一个列上的唯一值)进行了较 差的分布。还可能说明语句写得太差 在分析期间库缓存里 Misses 值大于 1 这表明需要重载这条语句。可以增大 init.ora 文件中的 SHARED _POOL_SIZE 值,或者执行一次较好的共享 SQL 任务 6.1.46.1.46.1.46.1.4 深入探讨深入探讨深入探讨深入探讨 TKPROFTKPROFTKPROFTKPROF 输出输出输出输出 通过比较 TKPROF 输出和实际对象的物理特征,我们就可以了解到 Oracle 的工作原理。看一下 CUSTOMER 表,它包含了超过 100 000 条记录,分布在 1 000 多个块上。通过查询 DBA_TABLES 和 DBA_EXTENTS,我们 可以看到已分配的块(1536)和已经使用的块(1382),如下面的程序清单所示。 select sum(blocks) from dba_segments where segment_name = 'CUSTOMER'; SUM(BLOCKS) ----------- 1536 select blocks, empty_blocks from dba_tables where table_name = 'CUSTOMER'; BLOCKS EMPTY_BLOCKS ---------- ------------ 1382 153 如果查看统计 CUSTOMER 表上所有记录的查询的 TKPROF 输出(如下面的程序清单所示),就可以看到该 查询执行了一个全表扫描,因为这次查询是在表启动后第一次访问。同时还要注意到,所访问的块(大多数 是物理磁盘访问)要比表的总块数(详见前文的查询)稍微高一些。所有的 1387 个查询块读取(除了 4 块)都 是磁盘读取(磁盘读取是查询的子集,查询读取是一致模式下磁盘和内存读取的总和)。 SELECT COUNT(*) FROM CUSTOMER; call count cpu elapsed disk query current rows ------- ------ -------- -------- ------ ------ ------- ----- Parse 1 3505.04 3700.00 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 2 1101.59 18130.00 1383 1387 15 1 131 ------- ------ -------- -------- ------ ------- ------- ------ total 4 4606.63 21830.00 1383 1387 15 1 Misses in library cache during parse: 1 Optimizer goal: ALL_ROWS Parsing user id: 5 Rows Row Source Operation ------ --------------------------------------------------- 1 SORT AGGREGATE 114688 TABLE ACCESS FULL CUSTOMER 如果我们第二次运行这个查询(如下面的程序清单所示),就会有很大的变化。如果我们再看一下统计 CUSTOMER 表上所有记录的查询的 TKPROF 输出,我们看到它仍然执行了全表扫描,但现在却只有很少的磁 盘读取,这是因为多数需要被访问的块已经被缓冲到内存里。1387 个查询块读取中的大部分都是内存读取 (只有 121 个是磁盘读取)。 SELECT COUNT(*) FROM CUSTOMER call count cpu elapsed disk query current rows ------- ------ ------- ------- ------ ------- ------- -------- Parse 1 0.00 0.00 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 2 901.29 2710.00 121 1387 15 1 ------- ------ ------- ------- ------ -------- ------- -------- total 4 901.29 2710.00 121 1387 15 1 Misses in library cache during parse: 0 Optimizer goal: ALL_ROWS Parsing user id: 5 Rows Row Source Operation -------- --------------------------------------------------- 1 SORT AGGREGATE 114688 TABLE ACCESS FULL CUSTOMER 技巧: 全表扫描是 Oracle 控制的从内存中首先清出的内容之一(在运行全表扫描后,它们将归 为近期最少使用项(LRU),这是因为它们效率非常低,通常还占用了大量的内存。 6.1.56.1.56.1.56.1.5 使用使用使用使用 DBMS_MONITOR(10gDBMS_MONITOR(10gDBMS_MONITOR(10gDBMS_MONITOR(10g 的新特性的新特性的新特性的新特性)))) 在具有连接池或共享服务器的多层环境中,一个会话可以跨越多个进程,甚至跨越多个实例。 DBMS_MONITOR 是在 Oracle 10g 中引入的内置的程序包,通过该程序包可以跟踪从客户机到中间层、再到 后端数据库的任何用户的会话,从而可以较为容易地标识创建大量工作量的特定用户。DBMS_MONITOR 取代 了传统的跟踪工具,例如 DBMS_ SUPPORT。需要具有 DBA 角色才可以使用 DBMS_MONITOR。 端对端的应用程序跟踪可以基于如下: ● 会话:基于会话 ID(SID)和序列号。 132 ● 客户端标识符 :允许跨越多个会话设置跟踪 。 基 于 登 录 ID 指 定 终 端 用 户 。 使 用 DBMS_SESSION.SET_IDENTIFIER 过程设置该值。 ● 实例:基于实例名指定给定的实例。 ● 服务名:指定一组相关的应用程序。使用 DBMS_SERVICE.CREATE_SERVICE 过程设置该值。 ● 模块名:开发人员在其应用程序代码中使用 DBMS_ APPLICATION _INFO.SET _MODULE 过程设置该 值。使用该名称表示执行的模块或代码。 ● 操作名:开发人员在其应用程序代码中使用 DBMS_ APPLICATION _INFO.SET _ACTION 过程设置该 值。使用该名称表示模块执行的操作。 最后 3 个跟踪选项在层次上关联;不可以在没有指定模块名和服务名的情况下就指定操作名,但是 可以只指定服务名,或者只指定服务名和模块名。 1. 1. 1. 1. 基于会话基于会话基于会话基于会话 IDIDIDID 和序列号设置跟踪和序列号设置跟踪和序列号设置跟踪和序列号设置跟踪 为了基于会话 ID 和序列号设置跟踪,首先确定需要跟踪的会话的 SID 和序列号: Select sid,serial#,username from v$session; SID SERIAL# USERNAME ---------- ---------- ------------------------------ 156 3588 SCOTT 142 1054 SYS 为了启用跟踪,可执行如下语句: SQL> exec dbms_monitor.session_trace_enable(156,3588,TRUE,FALSE); 第三个参数用于等待(默认为 TRUE),第四个参数用于绑定变量(默认为 FALSE)。 为了关闭跟踪,可执行如下语句: SQL> exec dbms_monitor.session_trace_disable(156,3588); 为了跟踪当前的会话,可设置 SID 和 SERIAL#为空: SQL> exec dbms_monitor.session_trace_enable(null,null); 2222. . . . 基于客户端标识符设置跟踪基于客户端标识符设置跟踪基于客户端标识符设置跟踪基于客户端标识符设置跟踪 为了基于表示用户的客户端标识符设置跟踪,可运行如下语句: SQL> exec dbms_session.set_identifier('bryan id'); 为了验证客户端标识符,可执行如下语句: select sid,serial#,username, client_identifier 133 from v$session where client_identifier is not null; SID SERIAL# USERNAME CLIENT_IDENTIFIER ---------- ---------- ------------------------ ------------------ 156 3588 SCOTT bryan id 现在就可以为这个客户端标识符设置跟踪: SQL> exec dbms_monitor.client_id_trace_enable('bryan id',true,false); 第二个参数用于等待(默认为 TRUE),第三个参数用于绑定变量(默认为 FALSE)。 为了禁用这个客户端标识符跟踪,可执行如下语句: SQL> exec dbms_monitor.client_id_trace_disable('bryan id'); 3. 3. 3. 3. 设置服务名设置服务名设置服务名设置服务名////模块名模块名模块名模块名////操作名的跟踪操作名的跟踪操作名的跟踪操作名的跟踪 为了使用操作名,必须有对应的模块名和服务名。为了使用模块名,必须有服务名。对全局范围内针 对某个数据库的服务名、模块名和操作名的给定组合启用跟踪,除非为过程指定了实例名。服务名由用于 连接到服务的连接字符串确定。 Oracle 数据库表示为作为服务的客户端;也就是说,数据库代表客户端执行相应的操作。数据库可以 有一个或多个与其关联的服务。例如,可以有一个数据库,该数据库带有两个用于 Web 客户端的不同服务: 用于购买书籍的客户端的 book.us.acme.com,以及用于购买软件的客户端的 soft.us.acme.com。在该示例 中 , 数 据 库 名 是 sales.acme.com,因此服务名并不基于数据库名。服务名由初始参数文件中的 SERVICE_NAMES 参数指定。服务名默认为由数据库名(DB_NAME 参数)和域名(DB_DOMAIN 参数)组成的全局数 据库名。 为了启用服务名的跟踪,可执行如下语句: SQL> exec dbms_monitor.serv_mod_act_trace_enable(service_name=>'ebk2'); 这将跟踪服务名为 ebk2 的所有会话。 为了启用服务名、模块名和操作名组合的跟踪,可执行如下语句: SQL> exec dbms_monitor.serv_mod_act_trace_enable(service_name=>'ebk2', - module_name=>'salary_update', action_name=>'insert_item'); 为了禁用前面代码中的跟踪,可使用过程 SERV_MOD_ACT_TRACE_DISABLE,如下说是: SQL> exec dbms_monitor.serv_mod_act_trace_disable(service_name=>'ebk2', - module_name=>'salary_update', action_name=>'insert_item'); 为了跟踪整个数据库或实例,可执行如下语句(不推荐这样操作): execute DBMS_MONITOR.DATABASE_TRACE_ENABLE(waits => TRUE, binds => FALSE, - 134 instance_name => 'ebk1'); 技巧: 使用 DBMS_MONITOR 时,请确保在完成操作时禁用跟踪;否则,将会跟踪满足指定条件的 每个会话。 4. 4. 4. 4. 启用跟踪视图启用跟踪视图启用跟踪视图启用跟踪视图 查看 DBA_ENABLED_TRACES 和 DBA_ENABLED_AGGREGATIONS 视图,可以看到启用的跟踪和收集的统计信 息。可以使用这些视图确保已经禁用的所有跟踪选项。 6.1.66.1.66.1.66.1.6 使用使用使用使用 TRCSESSTRCSESSTRCSESSTRCSESS 将多个跟踪文件保存到一个文件中将多个跟踪文件保存到一个文件中将多个跟踪文件保存到一个文件中将多个跟踪文件保存到一个文件中(10g(10g(10g(10g 的新特性的新特性的新特性的新特性)))) 使用这个 Oracle 10g 的新特性可以有选择性地从多个跟踪文件中提取跟踪数据,并且将这些跟踪数 据基于会话 ID 或模块名等标准保存到一个跟踪文件中。这个命令行实用程序在连接池和共享服务器配置中 特别有用,在这些配置中,每个用户请求可能都以一个单独的跟踪文件结束。使用 TRCSESS 可以获得属于 某个用户会话的统一的跟踪信息。 可以根据如下一些标准创建这个统一的跟踪文件: ● 会话 ID ● 客户端 ID ● 服务名 ● 操作名 ● 模块名 命令语法如下: trcsess [output=] [session=] [clientid=] [service=] [action=] [module=] [trace_file] output= output destination default being standard output. session= session to be traced. (SID and SERIAL#) clientid= clientid to be traced. service= service to be traced. action= action to be traced. module= module to be traced. trace_file = list of trace file names, separated by spaces, which need to searched by the trcsess command. If no files are listed, then all the files in the current directory will be searched. The wild card character, *, may be used in the file names. 其中 output= 输出目的地默认值(作为标准输出) session= 跟踪的会话(SID 和 SERIAL#) clientid= 跟踪的客户端 ID service= 跟踪的服务 135 action= 跟踪的操作 module= 跟踪的模块 trace_file= 以空格分隔的跟踪文件名的列表,trcsess 命令需要搜索该列表。如果没有列出任何文 件,则搜索当前目录中的所有文件。在文件名中可以使用通配符*。 1. 1. 1. 1. 示例示例示例示例 1111 这是来自于前面的小节“使用 DBMS_MONITOR”中的一个示例,其中 service_name = ebk2、module= salary_update、action = insert_item。进入 user_dump_dest 目录并运行如下的命令: trcsess output=combo.trc service="ebk2" module="salary_update" - action="insert_item" 这将搜索满足前面标准的所有跟踪文件,并且将创建名为 combo.trc 的统一的跟踪文件。 现在可以对 combo.trc 运行 TKPROF: tkprof combo.trc output=combo_report sort=fchela 2. 2. 2. 2. 示例示例示例示例 2222 设置客户端 ID: SQL> exec dbms_session.set_identifier('ebk3'); 对该客户端 ID 启用跟踪: SQL> EXECUTE DBMS_MONITOR.CLIENT_ID_STAT_ENABLE(ebk3); 跟踪这个客户端 ID,然后执行如下的命令(从 user_dump_dest 目录中): trcsess output=combo2.trc clientid=ebk3 *.trc Trcsess 将对所有跟踪文件检查指定的客户端 ID。现在可以对 combo2.trc(合并的跟踪文件)运行 TKPROF。 3. 3. 3. 3. 示例示例示例示例 3333 在第一种情况中,当前目录中的所有跟踪文件用作输入,并且将创建一个跟踪文件(combo3.trc),该 文件具有 session=17.1988 的所有跟踪信息。 trcsess output=combo3.trc session=17.1988 trcsess output=combo4.trc session=17.1988 ebk2_ora_0607.trc ebk2_ora_0125.trc 在当前情况中,只有列出的两个 trc 文件用作输入,并且根据两个列出的跟踪文件创建一个跟踪文件 (combo4.trc),该文件具有 session=17.1988(注意,17.1988 是.)的所有跟踪信息。 136 6.1.76.1.76.1.76.1.7 单独使用单独使用单独使用单独使用 EXPLAINPLANEXPLAINPLANEXPLAINPLANEXPLAINPLAN 开发人员可以使用 EXPLAIN PLAN 命令来查看 Oracle 优化器用来执行 SQL 语句的查询执行计划。这个 命令对提高 SQL 语句的性能很有帮助,因为它不用真正地执行 SQL 语句—— 它只是列出了所要使用的计划, 并把这个执行计划插入到一个 Oracle 表中。在使用 EXPLAIN PLAN 命令之前,执行该命令的 Oracle 账户必 须先执行一个 utlxplan.sql 文件(与 catalog.sql 位于相同的目录下,通常是在 ORACLE_HOME/rdbms/admin 文件夹下)。 这个脚本将创建一个 PLAN_TABLE 表,EXPLAIN_PLAN 命令以记录形式向这个表插入新的查询执行计划。 可以查询或查看这个表,以判断是否有 SQL 语句需要被修改成不同的执行计划。Oracle 也提供了很多对这 个计划表的查询,如 utlxpls.sql 和 utlxplp.sql(每个都能运行,但 utlxplp.sql 面向并行查询)。下面 显示了一个 EXPLAIN PLAN 示例(在 SQL*Plus 下执行)。 问:为什么使用 EXPLAIN 而不用 TRACE? 答:不是执行这条语句;它只展示执行了这条语句后将会有什么后果。 问:什么时候使用 EXPLAIN 而不用 TRACE? 答:当这个查询可能会运行特别长的时间时。 图 6-1 演示了运行 TRACE 以及 EXPLAIN 过程的对比: 图 6-1 运行 TRACE 和 EXPLAIN 的过程 问:如何使用 EXPLAIN? 137 答:(1) 找到这个脚本;通常位于 ORACLE_HOME/rdbms/admin 文件夹下: "utlxplan.sql" (2) 在 SQLPLUS 中执行脚本 utlxplan.sql: @utlxplan(以将运行 EXPLAIN 计划的用户运行该脚本) 用户执行这个脚本后将创建一个 PLAN_TABLE 表。可以创建自己的 PLAN_TABLE,但一定要使用 Oracle 的语法格式或类似格式。 (3) 对将被优化的查询执行 EXPLAIN PLAN: explain plan for select CUSTOMER_NUMBER from CUSTOMER where CUSTOMER_NUMBER = 111; Explained. (4) 为需要优化的查询执行 EXPLAIN PLAN(这条语句里使用了标记): explain plan set statement_id = 'CUSTOMER' for select CUSTOMER_NUMBER from CUSTOMER where CUSTOMER_NUMBER = 111; 技巧: 如果有多位开发人员填充 PLAN_TABLE,请使用 SET STATEMENT_ID = 'your_identifier'。 我很少使用 SET STATEMENT_ID 语句。我一般是对查询执行 EXPLAIN,查看输出结果,然 后立即从表 PLAN_TABLE 删除掉记录。我会一直这样修改查询,直到看到一个我中意的执 行计划。然后就运行这个查询来看看性能是否有所提高。但如果有多位开发人员或 DBA 都在使用这个 PLAN_TABLE,一定要使用 SET STATEMENT_ID(区分大小写)标识语句。 (5) 从 PLAN_TABLE 里选择输出: select operation, options, object_name, id, parent_id from plan_table where statement_id = 'CUSTOMER'; Operation Options Object_Name ID Parent select statement 0 Table Access By ROWID Customer 1 Index Range Scan CUST_IDX 2 1 技巧: 使用 EXPLAIN 而不是 TRACE,就不用为运行这个查询而等待。EXPLAIN 不用实际运行这个 查询就可以显示查询的路径。一般只对那些多查询的批处理任务使用 TRACE,以找到在 138 查询就可以显示查询的路径。一般只对那些多查询的批处理任务使用 TRACE,以找到在 这个批处理任务里哪些查询很慢。 技巧: 可以使用 Oracle 提供的 utlxpls.sql 和 utlxplp.sql 查询来对 PLAN_TABLE 进行查询, 而不用自己写一个查询或对输出进行格式转换。 对简单查询的另一个 EXPLAIN 例子 这一节介绍如下的简单过程:运行查询,然后检查执行计划以了解如何处理查询的相关信息。 (1) 在查询之前嵌入 EXPLAIN 语句,然后运行这个查询: explain plan set statement_id = 'query 1' for select customer_number, name from customer where customer_number = '111'; (2) 查询 PLAN_TABLE 表来检索 EXPLAIN 的输出结果: 可以执行一条 SQL 语句来检索并查看信息。Oracle 文档里提供了两个脚本,显示在步骤(2)和(3)里, 并分别提供了上述 EXPLAIN PLAN 命令的结果。注意这个例子与前面例子的不同之处。列 customer_number 是一个已索引的数字型字段,在这个例子里因为数据类型不匹配而受到限制(111 处于引号之中,强制执行 一个 to_char 操作)。在第一个例子里,我把 customer_number 列正确地当作数字型字段处理(111 没有处 于引号之中)。有时,优化器能够智能地执行这种转换,但是在使用 Pro*C 或其他类似的编码时,优化器就 可能无法执行这种转换。 select operation, options, object_name, id, parent_id from plan_table where statement_id = 'query 1' order by id; Operation Options Object_Name ID Parent_ID select Statement 0 Table Access Full Customer_Information 1 0 (3) 检索更直观更方便阅读的 EXPLAIN 输出结果: select lpad(' ', 2*(level-1)) || operation || ' ' || options || ' ' || object_name || ' ' || decode(id, 0, 'Cost = ' || position) "Query Plan" from plan_table start with id = 0 and statement_id = 'query 1' connect by prior id = parent_id and statement_id = 'query 1'; 139 输出如下所示: Query Plan select statement Cost=220 Table Access Full Customer 6.1.86.1.86.1.86.1.8 EXPLAIN PLAN EXPLAIN PLAN EXPLAIN PLAN EXPLAIN PLAN———————— 从上至下读取和从下至上读取从上至下读取和从下至上读取从上至下读取和从下至上读取从上至下读取和从下至上读取 从上至下读取或是从下至上读取取决于从 PLAN_TABLE 表中检索信息的查询的编写方式。这也正是很 多人对采取什么方式读取结果意见不统一的原因(两个方式都对)。如下的程序清单显示了用来检索信息的 查询的执行顺序。在这个例子里,结果是从上至下读的:必须从最内侧往最外侧读。这个程序清单显示了 一个可以解决全部问题的方法。 SQL 语句应该放在 EXPLAIN PLAN 语句的 FOR 子句之后。 delete from plan_table; explain plan set statement_id = 'SQL1' for select to_char(sysdate, 'MM/DD/YY HH:MI AM'), to_char((trunc((sysdate -4 -1), 'day') +1), 'DD-MON-YY') from bk, ee where bk_shift_date >= to_char((trunc(( sysdate - 4 - 1), 'day') + 1), 'DDMON- YY') and bk_shift_date <= to_char((sysdate - 4), 'DD-MON-YY') and bk_empno = ee_empno(+) and substr(ee_hierarchy_code, 1, 3) in ('PNA', 'PNB', 'PNC', 'PND', 'PNE', 'PNF') order by ee_job_group, bk_empno, bk_shift_date / select LPad(' ', 2*(Level-1)) || Level || '.' || Nvl(Position,0)|| ' ' || Operation || ' ' || Options || ' ' || Object_Name || ' ' || Object_Type || ' ' || Decode(id, 0, Statement_Id ||' Cost = ' || Position) || cost || ' ' || Object_Node "Query Plan" from plan_table start with id = 0 And statement_id = 'SQL1' connect by prior id = parent_id and statement_id = 'SQL1' / Query Plan 1.0 SELECT STATEMENT SQL1 Cost = 2.1 SORT ORDER BY (7 th ) 3.1 FILTER (6 th ) 4.1 NESTED LOOPS OUTER (5 th ) 5.1 TABLE ACCESS BY ROWID BK (2 nd ) 6.1 INDEX RANGE SCAN I_BK_06 NON-UNIQUE (1 st ) 140 5.2 TABLE ACCESS BY ROWID EE (4 th ) 6.2 INDEX UNIQUE SCAN I_EE_01 UNIQUE (3 rd ) 6.1.96.1.96.1.96.1.9 阅读阅读阅读阅读 EXPLAIN PLANEXPLAIN PLANEXPLAIN PLANEXPLAIN PLAN 使用前面的 EXPLAIN PLAN 命令,逐个讲解其中的每个步骤。表 6-4 左边列中的数字表示每个步骤。 它们的顺序与运行时候的顺序一样。 技巧: 从上至下读还是从下至上读完全取决于从 PLAN_TABLE 表里检索信息的查询的编写方式。 两种读取查询的方法都对,只要检索信息的查询的写法正确就行。 表 6-4 阅读 EXPLAIN PLAN 步 骤 动 作 6.1 这是 I_BK_06 的索引范围扫描。这是第一步。索引位于列 BK_SHIFT_DATE 上。这一步将对这 个索引执行一次扫描并产生两个日期之内的 ROWID 列表 5.1 检索了 BK 表上的行 6.2 扫描 I_EE_01 索引。这个索引位于列 EE_EMPNO 上。利用前一步检索到的 BK_EMPNO,将对这 个索引进行扫描来检索 ROWID,生成与 BK_EMPNO 匹配的 EE_EMPNO 列表 5.2 检索表 EE 的所有行 4.1 嵌套循环。连接两个列表,并组合成一个列表 (续表) 步 骤 动 作 3.1 过滤器。用到了 WHERE 子句的其余条件 2.1 SORT_ORDER_BY。根据 ORDER BY 子句定义的顺序对剩余的行排序 1.0 告诉我们语句的类型 1. 1. 1. 1. 启用启用启用启用 AUTOTRACEAUTOTRACEAUTOTRACEAUTOTRACE 生成 EXPLAIN PLAN 和利用 SQL*Plus 对查询性能进行统计,还有一个更简便的方法。AUTOTRACE 和 EXPLAIN PLAN 两者之间的主要区别就是,AUTOTRACE 确实执行了查询(用 TRACE 的方法),然后自动查询计 划表,而 EXPLAIN PLAN 没有这样做。AUTOTRACE 命令产生了类似的信息,如下面的程序清单所示。要使用 AUTOTRACE,用户必须是 PLUSTRACE 角色(运行 plustrce.sql,通常位于 ORACLE_HOME/ rdbms/admin 文件 夹下)。 SET AUTOTRACE ON select count(last_name) from emp where name = 'branches'; 输出如下所示: COUNT(LAST_NAME) ---------------- 0 Execution Plan 141 ---------------------------------------------------------- Plan hash value: 141239332 ---------------------------------------------------------------------- | Id |Operation |Name |Rows |Bytes |Cost(%CPU)| Time | -------------------------------------------------------------- | 0 |SELECT STATEMENT | | 1 | 8 | 1 (0) | 00:00:01| | 1 | SORT AGGREGATE | | 1 | 8 | || |* 2 | INDEX RANGE SCAN |EMP_NAME_IX | 1 | 8 | 1 (0) | 00:00:01| ---------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("LAST_NAME"='BRANCHES') Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 0 consistent gets 0 physical reads 0 redo size 0 bytes sent via SQL*Net to client 0 bytes received via SQL*Net from client 0 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed AUTOTRACE 选项也可以为查询提供 EXPLAIN PLAN 和统计信息。AUTOTRACE 提供了很多 TRACE 和 TKPROF 统计信息,如磁盘读取(physical read)和总读取(consistent reads+db block gets)。 技巧: 如果在设置 AUTOTRACE 时出现了 Unable to verify plan table format or existence 错误,必须使用 utlxplan.sql 脚本来创建一个计划表。 警告: 如果 AUTOTRACE 不能用于查询系统视图,可能是因为用户没有查看底层对象的权限。 表 6-5 显示了其他 AUTOTRACE 选项。 表 6-5 AUTOTRACE 选项 选 项 功 能 SET AUTOT ON 打开 AUTOTRACE 的简便方法 SET AUTOT ON EXP 仅显示 EXPLAIN PLAN SET AUTOTRACE ON STAT 仅显示统计信息 142 SET AUTOT TRACE 不显示查询的输出结果 2. 2. 2. 2. 在分区模式下使用的在分区模式下使用的在分区模式下使用的在分区模式下使用的 EXPLAIN PLANEXPLAIN PLANEXPLAIN PLANEXPLAIN PLAN 表分区会产生不同的 EXPLAIN PLAN 输出结果(如下面的程序清单所示)。在这个程序清单里,我们用 一个分区索引把表分成 3 个部分。从广义上讲,表分区就是放在数据库中不同位置里的表。可以参阅第 3 章,了解到与分区相关的更多内容。 create table dept1 (deptno number(2), dept_name varchar2(30)) partition by range(deptno) (partition d1 values less than (10), partition d2 values less than (20), partition d3 values less than (maxvalue)); insert into dept1 values (1, 'DEPT 1'); insert into dept1 values (7, 'DEPT 7'); insert into dept1 values (10, 'DEPT 10'); insert into dept1 values (15, 'DEPT 15'); insert into dept1 values (22, 'DEPT 22'); create index dept_index on dept1 (deptno) local (partition d1, partition d2 , partition d3 ); 现在生成一个 EXPLAIN PLAN,强制对头两个分区进行全表扫描,如下面的程序清单所示。 explain plan for select dept_name from dept1 where deptno || '' = 1 or deptno || '' = 15; 当根据这个计划表选择时,必须选择另外两列 partition_start(起始分区)和 Partition_stop(结尾 分区)。全表扫描则需访问所有的分区。 Select operation, options, id, object_name, partition_start, partition_stop from plan_table; 输出如下所示(全表扫描): 143 OPERATION OPTIONS ID OBJECT_NAME PARTITION_START PARTITION_STOP SELECT STATEMENT PARTITION CONCATENATED 1 1 3 TABLE ACCESS FULL 2 DEPT1 1 3 前面一个例子显示了在 DEPT1 表上执行了一次全表扫描。将扫描所有 3 个分区。起始分区为 1,结尾 分区为 3。 接下来,下面的程序清单里的 EXPLAIN PLAN 是对分区 2 的索引范围扫描: explain plan for select dept_name from dept1 where deptno = 15; Explained. 现在生成了对分区 2 的索引范围进行扫描的 EXPLAIN PLAN,如下面的程序清单所示。 select operation, options, id, object_name, partition_start, partition_stop from plan_table; 输出如下所示(索引范围扫描): OPERATION OPTIONS ID OBJECT_NAME PARTITION_START PARTITION_STOP SELECT STATEMENT TABLE ACCESS BY LOCAL INDEX ROWID 1 DEPT1 2 2 TABLE ACCESS RANGE SCAN 2 DEPTIDX 2 2 这个输出表明所访问的表 OR 索引的唯一分区是第二个分区。这是因为 deptno=15 的记录只存在于表 DEPT1 的第二个分区上。DEPTNO 列也被检索,这个值同样在索引的第二个分区上。 技巧: 通过运行 EXPLAIN PLAN,就可以访问 PLAN_TABLE 表中的 PARTITION _START 和 PARTITION_STOP 列以查看分区情况。 3. 3. 3. 3. 在不使用在不使用在不使用在不使用 TRACETRACETRACETRACE 的情况下查找大的硬盘和的情况下查找大的硬盘和的情况下查找大的硬盘和的情况下查找大的硬盘和////或内存读取量或内存读取量或内存读取量或内存读取量 除了跟踪查询外,还有没有别的方法可以查找出有问题的磁盘和内存读取信息吗?当然有!使用 V$SQLAREA,我们可以在系统里找到有问题的查询。下面的程序清单显示了如何找到这些有问题的查询。在 这个查询里,我们想检索出磁盘读取量大于 10 000 的查询(没有使用或限制索引)。如果您的系统非常大, 144 还可以把这个值定得高一些。 select disk_reads, sql_text from v$sqlarea where disk_reads > 10000 order by disk_reads desc; DISK_READS SQL_TEXT 12987 select order#,columns,types from orders where substr(orderid,1,2)=:1 11131 select custid, city from customer where city = 'CHICAGO' 这个输出结果表明有两个有问题的查询引起了大量磁盘读取。第一个就是使用 SUBSTR 函数使 ORDERID 函数上的索引受到限制;第二个显示了 CITY 上缺少索引。 在如下所示程序清单的查询里,我们检索内存读取量大于 200 000 的查询(过度使用索引的查询问题)。 如果系统过大,可能就必须把这个值设得更大。 select buffer_gets, sql_text from v$sqlarea where buffer_gets > 200000 order by buffer_gets desc; BUFFER_GETS SQL_TEXT 300219 select order#,cust_no, from orders where division = '1' 输出结果表明有一个查询造成了过量的内存读取(300 219 个数据块读入内存中)。在 DIVISION 上的索 引有一个低的基数 1,因此在此表中只有一个分区。此处发生的情况时读取整个索引,然后读取整个表。 而且为了提高性能,必须对这个语句进行限制(如果不会添加额外的分区,则应该永久删除这个语句)。 技巧: 访问 V$SQLAREA 表可以得到经常跟踪查询获取的统计信息。可以在第 12 章里看到访问 V$sqlarea 表的更多内容。 6.1.106.1.106.1.106.1.10 使用使用使用使用 DBMS_XPLANDBMS_XPLANDBMS_XPLANDBMS_XPLAN 在 Oracle 9i 和以后的版本中,Oracle 提供了查看 Explain Plan 的较为容易的方法。现在可以使用 DBMS_XPLAN 查询执行计划。使用该程序包的一些注意事项如下: ● 该程序包自动查询 PLAN_TABLE 中的最后一个计划。 ● 该程序包使用 TABLE()函数和另一个管道函数。 ● 文本截取操作可能存在问题。 ● 该程序包将在计划后提供额外的信息,包括如下: 145 • 如果是当前计划表,则突出显示过滤器和连接条件。 • 当使用计划表的老版本时,显示警告消息。 下面是 DBMS_XPLAN 的一个示例: Select * from table (dbms_xplan.display); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------ ------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes| Cost | Pstart| Pstop | ------------------------------------------------------------------------ | 0 | UPDATE STATEMENT | | 328 | 2296 | 2 | | | | 1 | UPDATE | JOURNAL_LINE | | | | | | | 2 | PARTITION RANGE ALL | | | | | 1 | 4 | | 3 | TABLE ACCESS FULL | JOURNAL_LINE | 328 | 2296 | 2 | 1 | 4 | ------------------------------------------------------------------------ 11 rows selected 6.1.116.1.116.1.116.1.11 另一种另一种另一种另一种 EXPLAIN PLANEXPLAIN PLANEXPLAIN PLANEXPLAIN PLAN 输出方法输出方法输出方法输出方法::::构建树结构构建树结构构建树结构构建树结构 尽管有很多人认为先前的一些 EXPLAIN PLAN 方法即已足够,但还是有些人想使用一些更逻辑的方法, 以紧密结合查询的父/子关系以及相关的树结构。对于这些人来说,这种使用 EXPLAIN 的方法更直观,如下 所示。 (1) 下面就是将对其执行 EXPLAIN 命令的查询: explain plan set statement_id = 'SQL2' for select cust_no ,cust_address ,cust_last_name,cust_first_name ,cust_mid_init from customer where cust_phone = '3035551234'; (2) 下面就是这个方法中使用的查询: select LPAD(' ',2*(LEVEL-1))||operation "OPERATION", options "OPTIONS", DECODE(TO_CHAR(id),'0','COST = ' || NVL(TO_CHAR(position),'n/a'), object_name) "OBJECT NAME", id ||'-'|| NVL(parent_id, 0)||'-'|| NVL(position, 0) "ORDER", SUBSTR(optimizer,1,6) "OPT" from plan_table start with id = 0 146 and statement_id = 'SQL2' connect by prior id = parent_id and statement_id = 'SQL2'; (3) 输出结果如下所示: OPERATION OPTIONS OBJECT NAME ORDER OPT ------------------- -------- ------------ ---------- ---------- SELECT STATEMENT COST = 2 0-0-2 ALL_RO TABLE ACCESS BY INDEX ROWID CUSTOMER 1-0-1 INDEX RANGE SCAN IX_CUST_PHONE 2-1-1 ANALYZ 注意一下被引入的两个新列: ● ORDER 这个列包括 ID、父 ID 和在执行计划里这一步骤的位置。ID 标识了这个步骤,但并没有 说明执行的顺序。父 ID 表明了这个步骤的父步骤。位置信息说明了父 ID 相同的子操作的执行顺序。 ● OPT 这列包含了优化器的当前模式。 (4) 构建执行树。 根据所述的执行计划,可以构建一个执行树,这样可以更好地了解 Oracle 如何处理语句。为了构建 这个树,只要从第(1)步开始,找到那些父步骤为(1)的所有子步骤,并把它们画入树中即可。重复这一操 作,直到画入了全部的步骤。图 6-2 显示了根据这个例子中查询的执行计划所构建的执行树。 (5) 解释执行计划。 要了解 Oracle 如何处理语句,必须了解 Oracle 按什么顺序处理这些步骤,并在每一步里执行了什么。 执行顺序由这些步骤的父/子关系决定。基本上总是先执行子步骤(至少一次),然后由此转换到父步 骤。当一个父步骤有很多子步骤时,子步骤就按照步骤位置(也就是执行计划里 ORDER 列所显示的第三个数 字的顺序来执行)。当构建执行树时,对于父步骤来说较低位置的子步骤应该从左到右排列,执行树会从左 到右、从下到上进行读取。 图 6-2 执行树 6.1.126.1.126.1.126.1.12 另一个使用树的例子另一个使用树的例子另一个使用树的例子另一个使用树的例子 这一节介绍如下的简单过程:使用树方法,然后查看如何处理查询的相关信息。 (1) 下面就是将对其执行 EXPLAIN 的查询: 147 select a.cust_last_name, a.cust_first_name, a.cust_mid_init, b.order_desc, b.order_create_dt from order_hdr b, customer a where cust_phone = :host1 and b.cust_no = a.cust_no and b.order_status = 'OPEN'; (2) 下文展示了执行计划: OPERATION OPTIONS OBJECT NAME ORDER OPT SELECT STATEMENT COST = n/a 0-0-0 ALL_RO NESTED LOOPS 1-0-1 TABLE ACCESS BY ROWID ORDER_HDR 2-1-1 INDEX RANGE SCAN IX_ORDER_STATUS 3-2-1 TABLE ACCESS BY ROWID CUSTOMER 4-1-2 INDEX UNIQUE SCAN PK_CUSTOMER 5-4-1 (3) 图 6-3 显示了多表的执行树。 图 6-3 多表的执行树 (4) 确定查询的执行计划顺序。 这条语句有 5 个步骤。首先执行子步骤(3)。由于它是一个范围扫描,就向步骤(2)返回了 0、1 或许 多 ROWID。根据所返回的每一个 ROWID,步骤(2)会访问 order 表,获得所需求的数据,把数据返回给步骤 (1)。对于步骤(2)所检索到的每一行数据,步骤(1)会把 CUST_NO 传递给步骤(5)。步骤(5)使用客户号来执 行一次唯一扫描并获得 ROWID。这个 ROWID 从步骤(5)返回到步骤(4)。如果没有找到任何 ROWID,步骤(4) 将会告诉步骤(1)丢弃这个特殊的行。如果找到一个 ROWID,步骤(4)通过 ROWID 访问了这个表,并检索数 据。得到数据后,如果电话号码正确的话,它就会将数据返回给步骤(1)。在这里来自步骤(2)的结果将和 步骤(3)里的行进行合并,然后返回给用户。如果电话号码不正确,步骤(4)将不会返回任何行,步骤(1) 将会丢弃这个行。 (5) 检查性能。 这是不是一个好的表访问顺序呢?在多数订单系统里都会有大量的客户,在具体时间段内也会有许多 延迟交货订单。一般情况下,必须处理所有延迟交货订单,从每份订单中获得数据,再根据这些数据访问 148 CUSTOMER 表,最后只需留下与每份订单相对应客户的电话号码。为了简化这个过程,我们可以先根据电话 号码访问 customer 表,这样在第一步里就会过滤掉很多记录,从而可以提高性能。具体该如何做呢?看一 下下面的修改。 (6) 改变性能(改动了驱动表): select /*+ ORDERED */ a.cust_last_name, a.cust_first_name, a.cust_mid_init, b.order_desc, b.order_create_dt from customer a, order_hdr b where cust_phone = :host1 and b.cust_no = a.cust_no and b.order_status = 'OPEN'; (7) 决定新的执行计划: OPERATION OPTIONS OBJECT NAME ORDER OPT SELECT STATEMENT COST = n/a 0-0-0 ALL_RO NESTED LOOPS 1-0-1 TABLE ACCESS BY ROWID CUSTOMER 2-1-1 INDEX RANGE SCAN IX_CUST_PHONE 3-2-1 TABLE ACCESS BY ROWID ORDER_HDR 4-1-2 AND-EQUAL 5-4-1 INDEX RANGE SCAN IX_ORDER_CUST 6-5-1 INDEX RANGE SCAN IX_ORDER_STATUS 7-5-2 (8) 检查部分调整的查询的性能。 为什么表顺序会发生改变?这是因为使用 ORDERED 提示强制执行表驱动顺序。通常情况下,在基于成 本的优化器里,会首先根据表和索引统计来判断它。如果没有,可以使用 ORDERED 提示来得到相同的结果。 这是不是一个很好的表访问顺序?这个表顺序非常好,因为可以首先执行对 CUSTOMER 部分的查询, 而且可能只需向 ORDER 部分的查询返回一行。 AND-EQUAL 是否是优化方案?在这个例子里,不是。为什么要在 ORDER_STATUS 索引上的 1000 个 ROWID 和 CUST_NO 索引的所有 ROWID 中进行搜索,以找到相互匹配的记录呢?我们应该做的是选择并利用这两个 索引中最具备唯一性的索引,或是建立对应于 CUST_NO 和订单状态的复合索引。您应该做的是对驱动表进 行修改。现在,我们必须让 Oracle 停止使用订单状态索引,以完全调整这个查询。 (9) 下面列出了完全调整后的查询(ORDER_STATUS 上的索引将会受限制): select /*+ ORDERED */ a.cust_last_name, a.cust_first_name, a.cust_mid_init, b.order_desc, b.order_create_dt from customer a, order_hdr b where cust_phone = :host1 and b.cust_no = a.cust_no and b.order_status || '' = 'OPEN'; 149 (10) 下面列出了调整后的执行计划: OPERATION OPTIONS OBJECT NAME ORDER OPT SELECT STATEMENT COST = n/a 0-0-0 RULE NESTED LOOPS 1-0-1 TABLE ACCESS BY ROWID CUSTOMER 2-1-1 INDEX UNIQUE SCAN PK_CUSTOMER 3-2-1 TABLE ACCESS BY ROWID ORDER_HDR 4-1-2 INDEX RANGE SCAN IX_ORDER_STATUS 5-4-1 要确定 Oracle 如何处理 SQL 语句,必须为这个语句生成并解释它的执行计划。访问可以生成 SQL 执 行计划的工具,并详细了解如何执行计划里的信息和构建执行树,开发人员或 DBA 就可以研究由不同的 SQL 代码所产生的大量不同的 EXPLAIN PLAN,这样可以很快地学会如何调整和开发高质量的 SQL。 6.1.136.1.136.1.136.1.13 在开发产品中利用在开发产品中利用在开发产品中利用在开发产品中利用 TRACE/EXPLAINTRACE/EXPLAINTRACE/EXPLAINTRACE/EXPLAIN 发现有问题的查询发现有问题的查询发现有问题的查询发现有问题的查询 尽管可以在 SQL*Plus 命令行里输入 ALTER_SESSION SET SQL_TRACE TRUE 命令来跟踪 SQL 语句,但这 对于开发产品来说就显得不是很足够了。该方案的一个缺点就是不能跟踪表单或报告;必须从表单和报告 里复制出代码,然后利用 SQL*Plus 运行它。如果不知道该跟踪哪一条语句,这个过程可能非常耗时。 还有一个方式可以对表单的执行进行跟踪。如果使用的是 Oracle Forms 的早期版本,比如 Forms 3.0, 则可以在命令行里输入-s;如果使用的是 Forms(4 至 6 版本),则可以在命令行里输入 statistics = yes。 这样就可以跟踪每一个表单。最近的 Oracle Forms 和 Oracle Reports 版本可以从表单或报告的内部进行 跟踪。请参考 Forms 或 Reports 文档来得到使用这些功能的帮助信息。Oracle 程序通常通过菜单项来提供 这些信息。也可以使用 DBMS_MONITOR 跟踪这些产品。 技巧: 还可以在 Developer 产品中使用 TRACE。为跟踪表单,可直接在命令行里设置 statistics=yes,或者把跟踪程序嵌入到实际的触发器里,以控制跟踪操作的开关。 6666.1.14.1.14.1.14.1.14 PLAN_TABLE PLAN_TABLE PLAN_TABLE PLAN_TABLE 表中的重要列表中的重要列表中的重要列表中的重要列 下面列出了表 PLAN_TABLE 里一些很重要的列的描述: ● STATEMENT_ID 在 EXPLAIN PLAN 语句里指定 STATEMENT_ID 参数的值。 ● TIMESTAMP 执行 EXPLAIN PLAN 语句所出现的日期和时间。 ● REMARKS 与每步 EXPLAIN PLAN 相关的注释(可以达到 80 字节)。如果需要在 PLAN_TABLE 的任何 行添加或修改注释,则可使用 UPDATE 语句来修改 PLAN_TABLE 表的行。 ● OPERATION 在这步操作所执行的内部操作名称。在为这个语句生成的第一行里,这个列包含了 下面 4 个值的一个: DELETE、INSERT、SELECT 或 UPDATE,这取决于这个语句的类型。 ● OPTIONS 在 OPERATION 列里所描述操作的变体形式,Oracle Server Tuning 一书的附录 A 中介 绍了有关该列的信息。 150 技巧: 表 PLAN_TABLE 的 OPERATION 和 OPTIONS 列对调整查询非常重要。OPERATION 列显示了所 执行的实际操作(包括连接类型),而 OPTIONS 列告诉您什么时候执行(可能需要索引)全 表扫描。 ● OBJECT_NODE 用来引用对象的数据库链接名(表或视图名称)。对于使用了并行查询选项的本地 查询来说,这个列描述了执行操作的输出顺序。 ● OBJECT_OWNER 拥有包含表或索引的模式的用户名。 ● OBJECT_NAME 索引或表的名称。 ● OBJECT_INSTANCE 对象在最初语句里出现时与对象所处位置相对应的数字。按从左到右、从外 到内的顺序计数,考虑到最初的语句文本。注意,视图扩展会产生不可预期的数字。 ● OBJECT_TYPE 提供与对象相关描述信息的修改器,例如,索引的 NON-UNIQUE。 ● OPTIMIZER 优化器的当前模式。 ● ID 分配给执行计划中每一步骤的数字。 技巧: ID 列显示了处理语句的顺序。调整 SQL 语句的基本规则之一就是改动查询,这样可以改 动这个查询里步骤的顺序 ID。对查询里每个步骤的执行顺序的改动通常会提高或降低查 询的性能。使用 HINTS(请参阅第 7 章)将会强制使用不同的语句顺序来执行查询,通常 也会使查询变得更快或更慢。 ● PARENT_ID 对此 ID 步骤的输出结果执行操作的下一步执行步骤的 ID。 技巧: PARENT_ID 列非常重要,因为它显示了 EXPLAINPLAN 中两个步骤之间的依赖性。如果 EXPLAIN PLAN 的一部分有 PARENT_ID,这就表明这条语句必须在指定的 PARENT_ID 步骤 之前执行。 ● POSITION 对那些拥有相同 PARENT_ID 的步骤的处理顺序。 ● OTHER 用户认为有用的针对执行步骤的其他信息。 ● OTHER_TAG OTHER 列的内容。 ● COST 利用优化器的基于成本的方法所估算的操作成本。这个列的值并没有任何特别重要的可供 测试的单元;它只是用来与执行计划的成本相比的权重值。 ● CARDINALITY 基于成本的方法所估算这个操作的访问行数。 ● BYTES 基于成本的方法估算这个操作所访问的字节数。 技巧: 当估测应如何调整查询时,BYTES 列就特别重要。如果使用了索引并且字节数非常大时, 这就表明使用全表扫描会更有效率(也就是说,读取索引和数据的成本要比只在全表扫描 里检索数据的成本更大)。同时,字节数还可以帮助我们判断哪个表应该在查询时先被访 问(驱动表),因为有些表可能会限制其他表需要的字节数。在第 9 章可以看到关于如何 选择驱动表的更多技巧。 技巧: 记住查询中的 COST 和 BYTES 值都是估算出来的。成本或字节数估算值较高的查询可能比 估算值较低的查询运行得快。 151 6.1.156.1.156.1.156.1.15 Oracle Oracle Oracle Oracle 支持的一些有用的程序包支持的一些有用的程序包支持的一些有用的程序包支持的一些有用的程序包 可以使用 DBMS_SYSTEM 程序包来得到其他用户会话的信息,然后通过这些信息来跟踪这些会话。首先, 必须从 V$SESSION 里得到用户的信息。然后把这个信息传递给开始跟踪的过程,如同下面的程序清单所示。 select sid, serial# from v$session where username = 'SCOTT'; 输出如下所示: SID SERIAL# 9 190 1 row selected. 可以使用如下的程序包来跟踪用户名(必须键入用户会话的 SID 和 SERIAL#)。 execute dbms_system.set_sql_trace_in_session(9,190,TRUE); PL/SQL procedure successfully completed. 还可以对正在使用 DBMS_SESSION 程序包的会话执行 TRACE。这个程序包在跟踪含有存储过程的查询时 特别有用,也可用于 PL/SQL 代码中。 execute DBMS_SESSION.SET_SQL_TRACE (TRUE); PL/SQL procedure successfully completed. 6.1.166.1.166.1.166.1.16 适用于未记录入档的适用于未记录入档的适用于未记录入档的适用于未记录入档的 TRACETRACETRACETRACE 操作的初始参数操作的初始参数操作的初始参数操作的初始参数 专家们可以查看 X$KSPPI 表。下面的程序清单简单地列出了一些 init.ora 里未记录入档的 TRACE 参 数(10g 版本比 9i 版本多出了 9 个参数),查看附录 A 以了解更多信息。注意,Oracle 并不支持这个产品里 未记录入档的功能。 select ksppinm "Parameter Name", ksppstvl "Value",ksppstdf "Default" from x$ksppi x, x$ksppcv y where x.indx = y.indx and ksppinm like '/_%trace%' escape '/'; Parameter Name Value Default ------------------------------ ------------------------------ --------- _trace_files_public FALSE TRUE _ksi_trace TRUE _trace_processes ALL TRUE _trace_archive FALSE TRUE _trace_events TRUE _trace_buffers ALL:256 TRUE _trace_flush_processes ALL TRUE _trace_file_size 65536 TRUE _trace_options text,multiple TRUE _dump_trace_scope global TRUE 152 _trace_navigation_scope global TRUE _dlmtrace TRUE _ges_trace TRUE TRUE _db_block_trace_protect FALSE TRUE _trace_buffer_flushes FALSE TRUE _trace_multi_block_reads FALSE TRUE _trace_cr_buffer_creates FALSE TRUE _trace_buffer_gets FALSE TRUE _trace_pin_time 0 TRUE _trace_buffer_wait_timeouts 0 TRUE _db_mttr_sim_trace_size 256 TRUE _db_mttr_trace_to_alert FALSE TRUE _kkfi_trace FALSE TRUE _px_trace 0 TRUE _xt_trace none TRUE _ku_trace none TRUE _smm_trace 0 TRUE _px_compilation_trace 0 TRUE _stn_trace 0 TRUE _olap_continuous_trace_file FALSE TRUE _optimizer_trace none TRUE _xpl_trace 0 TRUE _kql_subheap_trace 0 TRUE _rowsrc_trace_level 0 TRUE _xsolapi_source_trace FALSE TRUE 35 rows selected 技巧: X$ksppi 表只能由 SYS 用户访问。在第 13 章里可以了解到如何访问 X$表和使用这些参数。 不要在没有 Oracle 公司技术指导的的情况下使用这些未记录入档的参数。同时在 Oracle 的不同版本里,这些视图里的布局和列名称都可能会发生变动。 1. 1. 1. 1. 在在在在 OracleOracleOracleOracle 里跟踪错误可以得到更多信息里跟踪错误可以得到更多信息里跟踪错误可以得到更多信息里跟踪错误可以得到更多信息 这节中,我们看一个未入档的 TRACE 功能。在使用这些未入档的 init.ora 参数之前,请联系一下 Oracle 公司。要跟踪会话的错误,可以修改并监控这个会话(如下所示)或者在 init.ora 文件里设置一个事件(第 13 章里有更详细的内容)。可以运行如下的查询来跟踪会话的错误(通常是跟踪一个 4031 错误)。这些查询 在 user_dump _dest 目录下建立了一个 TRACE 文件,并在文件里写入了一些错误信息。 使用如下命令: alter session set events=′4031 trace name errorstack level 4 ′; 技巧: 跟踪查询可以提高性能,但使用基于这些未入档的 TRACE init.ora 参数(前面讨论的) 的 TRACE 功能,可以让您深入地了解如何解决 Oracle 里的错误。 153 2. 2. 2. 2. 通过启用事件来跟踪通过启用事件来跟踪通过启用事件来跟踪通过启用事件来跟踪 我们还可以使用下面的命令来跟踪会话: SQL> Alter session set events '10046 trace name context forever, level 1'; Session altered. level 的值(上面的命令里是 1)可以为 1(常规的跟踪)、4(跟踪绑定变量)、8(跟踪等待状态)或 12(常 规的跟踪、跟踪绑定变量和跟踪等待状态)。关于绑定变量和等待状态的信息将写入到跟踪文件里,但当用 TKPROF 格式化这个报告时会把它们忽略掉。前面的命令输出的跟踪文件与如下的程序清单相类似: SELECT SYSDATE FROM DUAL WHERE SYSDATE IN ( :b1 ) END OF STMT PARSE #4:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=0 BINDS #4: bind 0: dty=12 mxl=07(07) mal=00 scl=00 pre=00 oacflg=03 oacfl2=1 size=8 offset=0 bfp=0ddcc774 bln=07 avl=07 flg=05 value="11/19/2000 19:25:47" WAIT #1: nam='SQL*Net message to client' ela= 0 p1=1413697536 p2=1 p3=0 可以用下面的命令来关掉事件跟踪: SQL> Alter session set events '10046 trace name context off'; Session altered. 6.1.176.1.176.1.176.1.17 使用存储纲要使用存储纲要使用存储纲要使用存储纲要 直到现在,执行计划的主要用途都是作为一个调整工具,以帮助判断 Oracle 如何在运行期间处理查 询。另外有一个 STORED OUTLINES(存储纲要)新功能,可以允许在查询运行的任意时间内使用先前决定好 的执行计划,与这个查询在哪儿运行无关。人们有时候会把 STORED OUTLINES 当作存储执行计划,但事实 上并不是这样。然而,Oracle 存储了一些提示—— 可以让数据库用更精确的方式执行查询—— 在记录会 话里备份并存储这个执行计划。 Oracle 使用 STORED OUTLINES 来备份查询的执行计划,这个过程有点类似于使用 SQL*PLUS 里的 EXPLAIN PLAN 功能。首先,告诉 Oracle 为那些将使用 ALTER SESSION 命令的查询保存纲要,然后对这些 STORED OUTLINES 的会话进行设置。接着,执行这个查询,以在纲要里存储您想要的信息(通常只在会话的 基础上完成,这样可以不影响到其他用户)。最后,如果可以接受这个执行计划,就可以把它存放到数据库 里,并能让任何人使用它。下面的部分讨论了如何操作的更详细步骤。 技巧: Oracle 公司喜欢把 STORED OUTLINES 看作 PLAN STABILITY。可以在 Oracle 的 PLAN STABILITY 文档里找到使用 STORED OUTLINES 的具体内容。 1. 1. 1. 1. 设置设置设置设置 Stored OutlinesStored OutlinesStored OutlinesStored Outlines 但是,就像 Oracle 所提供的其他新功能一样,使用 STORED OUTLINES 的设置过程也非常麻烦。在保 154 存纲要或使用已存储的纲要前,必须要设置很多用户和会话特权。否则,它们不能运行。 下面就是使用 STORED OUTLINES 所需要的特权: ● CREATE ANY OUTLINE ● EXECUTE_CATALOG(使用 DBMS_OUTLN 程序包) ● PLUSTRACE(如果应用,使用 AUTOTRACE) 在使用之前,STORED OUTLINES 还需要使用基于成本的优化器和很多具体的会话参数(环境设置): ● QUERY_REWRITE_ENABLED = TRUE ● STAR_TRANSFORMATION_ENABLED = TRUE ● OPTIMIZER_FEATURES_ENABLE = 10.1.0(举例来说) ● USE_STORED_OUTLINES = TRUE(使用已有的 STORED OUTLINES) ● CREATE_STORED_OUTLINES = TRUE(创建或编辑 STORED OUTLINES) ● USE_PRIVATE_OUTLINES = TRUE(使用专用纲要,只用于当前的会话) 2. 2. 2. 2. 如何如何如何如何存储纲要存储纲要存储纲要存储纲要 像其他的大多数特性一样,Oracle 把纲要保存到内部数据库表里,这些表的内容可以通过系统视图 (USER_*,ALL_*和 DBA_*)来查看。当然,只有很少的一些有特权的 DBA 才可以查看 DBA 视图,而 ALL_*视 图显示了用户可以看到的所有对象(可以不拥有)的信息,而USER_*视图显示了当前用户所拥有的对象信息。 为了简单起见,我们只看一下 USER_*视图。STORED OUTLINES 使用的主要视图有: ● USER_OUTLINES ● USER_OUTLINE_HINTS USER_OUTLINES 的内容如下所示: NAME CATEGORY USED ------------------------------ ----------------------- --------- TIMESTAMP VERSION --------------------------------------------------------------------- SQL_TEXT ------------------------------------------------------------------------ SIGNATURE -------------------------------- SYS_OUTLINE_020213193254787 DEFAULT UNUSED 13-FEB-02 9.0.1.2.0 select id from s_emp where id = 1 NAME CATEGORY USED TIMESTAMP ------------------------------ ------------- ----------- --------- VERSION 155 ---------------------------------------------------------------- SQL_TEXT ------------------------------------------------------------------------ SIGNATURE COMPATIBLE ENABLED FORMAT ------------------------ ------------ -------- ------ KGB_OUTLINE DEFAULT UNUSED 02-JAN-06 10.2.0.1 select * from emp E64F36A7F73BECFE2C61CBAA5781982F COMPATIBLE ENABLED NORMAL USER_OUTLINES_HINTS 中的少量程序清单(可能显示多个行)如下所示: NAME NODE STAGE JOIN_POS ------------------------------ ---------- ---------- ---------- HINT ------------------------------------------------------------------------ SYS_OUTLINE_020213193254787 1 3 0 NO_EXPAND SYS_OUTLINE_020213193254787 1 3 0 ORDERED SYS_OUTLINE_020213193254787 1 3 0 NO_FACT(S_EMP) NAME NODE STAGE JOIN_POS ------------------------------ ---------- ---------- ---------- HINT -------------------------------------------------------------- -------- KGB_OUTLINE 1 1 0 NO_EXPAND(@"SEL$1" ) KGB_OUTLINE 1 1 0 LEADING(@"SEL$1" "EMP"@"SEL$1") KGB_OUTLINE 1 1 0 NO_STAR_TRANSFORMATION(@"SEL$1" ) 可以在自己的模式里创建纲要表(这是个好主意)。如果没有,这些纲要将被存储到 SYSTEM 表空间里(这 通常是个错误)。利用 DBMS_OUTLN_EDIT.CREATE_EDIT_TABLES 过程里的命令可以在当前模式里创建纲要表, 命令如下所示: exec dbms_outln_edit.create_edit_tables 3. 3. 3. 3. 创建和使用存储纲要创建和使用存储纲要创建和使用存储纲要创建和使用存储纲要 有两种 STORED OUTLINES:会话特有的专用(private)类型和可以影响整个数据库的公共(public)类 型。使用哪一种纲要由会话参数 USE_PRIVATE_OUTLINES 的设置决定:如果参数被设为 TRUE,则使用专用 纲要。通常,除非要生成优化的执行计划,否则最好还是使用专用纲要。专用纲要可以用 CREATE OR REPLACE 156 PRIVATE OUTLINE 命令以公共方式存储;公共纲要可以使用 CREATE OR REPLACE OUTLINE …FROM PRIVATE … 命令来由专用纲要创建。这个过程又叫做编辑,可以把一个已有的专用纲要复制成公有纲要。只要适合, Oracle 会自动使用这些 STORED OUTLINES,而不用向执行该命令的用户显示。 技巧: Oracle 可以对每个查询运用 STORED OUTLINES 来查询执行计划。为了对某个查询执行该 功能,这个查询必须与原先存储的查询完全相同。稍许的改变都会导致 Oracle 认为这两 个查询不一样,从而不使用这个纲要。这个规则就跟游标共享一样,Oracle 用于通过共 享池分析查询。 尽管可以使用 SQL*PLUS 来对纲要进行编辑或更新,但我们不建议这样做,因为这非常困难。还有一 个简单的更新方法,那就是 Oracle Enterprise Manager 里提供的纲要编辑器。在第 5 章里的 Enterprise Manager 部分可以找到更详细的内容。 建立纲要有很多方法。把会话参数 CREATE_STORED_OUTLINES 设为 TRUE(当然前提是每个参数都设置正 确),就可以为每个执行的查询输出一个纲要(每个生成纲要的名字会加上 SYS 前缀),这个过程与使用 TRACE 监控整个会话相似。还有一个更好(更容易控制)的方法就是使用 CREATE OUTLINE 命令来得到某个查询的纲 要,如下面的程序清单所示: create or replace outline pb_outline on select e.last_name, e.salary from s_emp e where userid = 'lngao'; 在这个例子里,pb_outline 就是创建的纲要。这个方法的最大好处就是,可以控制要发生的事情以及 为纲要设置可用的名称。 Oracle 提供了很多处理 STORED OUTLINES 的程序包。在使用 STORED OUTLINES 时,可以对 DBMS_OUTLN 和 DBMS_OUTLN_EDIT 程序包研究其他的可能性。与 Oracle 的其他程序包不一样的是,这两个程序包并不专 属于 SYS 用户,尽管它们可以在 SQL*PLUS 里描述,但它们的源代码却不能从 USER_SOURCE 视图中得到(与 其他程序包不同,其他程序包至少可以看到一些头信息)。也不能直接维护这些视图的基层表(它们都是系 统表)。 4. 4. 4. 4. 删除存储纲要删除存储纲要删除存储纲要删除存储纲要 当不再需要存储纲要或者存储纲要影响到性能时,怎样删除它们呢?使用 DBMS_ OUTLN 程序包里的 DROP_UNUSED 过程来删除。下面的命令显示了如何删除所有没有用过的纲要: execute dbms_outln.drop_unused 要删除已经使用过的纲要,首先使用 DBMS_OUTLN.CLEAR_USED 过程,它可以接收纲要名称(来自 USER_OUTLINES 视图),并且一次只能针对一个纲要运行。可以写一段简单的 PL/SQL 程序来删除所有不使 用的纲要。 157 要判断纲要是否被使用过,可以检查 USER_OUTLINES 里的 USED 列。还可以用 V$SQL 视图查询 OUTLINE_CATEGORY 列,这样就可以看到仍在缓存中的内容,如下面的程序清单所示。 SELECT OUTLINE_CATEGORY, OUTLINE_SID FROM V$SQL WHERE SQL_TEXT = 'portion of query%' 6.1.18 6.1.18 6.1.18 6.1.18 使用使用使用使用 Plan Stability(Plan Stability(Plan Stability(Plan Stability(存储纲要存储纲要存储纲要存储纲要)))) 对于使用基于规则的优化器(RBO)开发的应用程序,通常需要执行相当多的工作来确保应用程序的性 能令人满意。使用 Plan Stability 时,通过保持运行在存储纲要中 RBO 下的 SQL 的性能,可以平稳地从 RBO 迁移到查询优化。在必要时,可以在新的环境中使用这些存储纲要维护 SQL 语句的性能。遵循如下的 步骤进行设置: (1) 以 SYS 用户的身份对涉及的每个模式运行如下的语句: GRANT CREATE ANY OUTLINE TO schema; (2) 在每个模式中运行如下语句: ALTER SESSION SET CREATE_STORED_OUTLINES = rbo; 运行应用程序足够长的时间,创建重要的 SQL 语句的存储纲要。 (3) 结束步骤(2)后,运行如下语句: ALTER SESSION SET CREATE_STORED_OUTLINES = FALSE: 接下来可以将存储纲要用于第(2)步中运行的任何 SQL。如果在查询优化下只有一些 SQL 语句存在问 题,则可以有选择性地只对这些有问题的 SQL 语句使用存储纲要。对于每条有问题的语句,改变存储纲要 的类别: ALTER OUTLINE outline_name CHANGE CATEGORY TO keepoutline; 然后改变会话以使用这种类别的纲要: ALTER SESSION SET USE_STORED_OUTLINE = keepoutline; 使用 user_outlines 视图获得特定存储纲要的 outline_name。 STORED OUTLINSTORED OUTLINSTORED OUTLINSTORED OUTLINESESESES 示例示例示例示例 本章最后演示一下如何使用 STORED OUTLKINES。先在下面的程序清单里列出代码;然后显示查询执行 后的结果。 --table s_emp contains the following structure 158 --and contains a unique index on userid /* SQL> desc s_emp; Name Null? Type ------------------------------------------ ID NOT NULL NUMBER(7) LAST_NAME NOT NULL VARCHAR2(25) FIRST_NAME VARCHAR2(25) USERID VARCHAR2(8) START_DATE DATE COMMENTS VARCHAR2(255) MANAGER_ID NUMBER(7) TITLE VARCHAR2(25) DEPT_ID NUMBER(7) SALARY NUMBER(11,2) COMMISSION_PCT NUMBER(4,2) */ analyze table s_emp compute statistics; alter session set query_rewrite_enabled = true; alter session set use_stored_outlines = true; alter session set star_transformation_enabled = true; --first create the public outline without a hint (user the index on userid) create or replace outline pb_outline on select e. last_name, e.salary from s_emp e where userid = 'lngao'; --create storage tables for private outlines --OL$, OL$HINTS and OL$NODES exec dbms_outln_edit.create_edit_tables; create private outline pr_outline from pb_outline; --edit the ol$hints table --use a full table scan rather than the index just to see if this works update ol$hints set hint_text = 'FULL(E)' where hint# = 6; commit; --resynch stored outline definition --alter system flush shared_pool; --or create private outline pr_outline from private pr_outline; --this is probably a better option 159 --to test the new private outline alter session set use_private_outlines = true; set autotrace on; select e.last_name, e.salary from s_emp e where userid = 'lngao'; set autotrace off; --make your changes permanent create or replace outline pb_outline from private pr_outline; --use the new public outline alter session set use_private_outlines = false; 运行这段代码后,输出的结果如下面的程序清单所示。 SQL> @outlines DOC>SQL> desc s_emp; DOC> DOC> Name Null? Type DOC> ------------------------------------------ DOC> ID NOT NULL NUMBER(7) DOC> LAST_NAME NOT NULL VARCHAR2(25) DOC> FIRST_NAME VARCHAR2(25) DOC> USERID VARCHAR2(8) DOC> START_DATE DATE DOC> COMMENTS VARCHAR2(255) DOC> MANAGER_ID NUMBER(7) DOC> TITLE VARCHAR2(25) DOC> DEPT_ID NUMBER(7) DOC> SALARY NUMBER(11,2) DOC> COMMISSION_PCT NUMBER(4,2) DOC>*/ Table analyzed. Session altered. Session altered. Session altered. Outline created. Table created. Outline created. 1 row updated. Commit complete. Session altered. LAST_NAME SALARY ------------------------- ---------- 160 LNGAO 1450 10.2 版本中的自动跟踪输出如下所示: Execution Plan ---------------------------------------------------------- Plan hash value: 79371356 ---------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time ----------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 17 | 3 (0) | 00:00:01 |* 1 | TABLE ACCESS FULL | S_EMP | 1 | 17 | 3 (0) | 00:00:01 ----------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("USERID"='lngao') Note ----- - outline "PR_OUTLINE" used for this statement Statistics ---------------------------------------------------------- 96 recursive calls 173 db block gets 40 consistent gets 0 physical reads 6188 redo size 334 bytes sent via SQL*Net to client 370 bytes received via SQL*Net from client 1 SQL*Net roundtrips to/from client 2 sorts (memory) 0 sorts (disk) 0 rows processed Outline created. Session altered. SQL> spool off 6.26.26.26.2 技巧回顾技巧回顾技巧回顾技巧回顾 ● 在 init.ora 中设置 TIMED_STATISTICS = TRUE 将启用时间统计的集合。同样,在 10g 版本中, 初始参数 SQL_TRACE 已经不再使用。 ● TKPROF 实用程序把跟踪输出转换为可阅读的格式。如果不运行 TKPROF,将很难读懂 TRACE 的输 出结果。指定 explain = username/password,除了这个查询的执行统计,我们还可以获得 EXPLAIN 执行 路径。 161 ● 如果要使用多个排序参数,只要在命令行里重复 sort = parameter 就可以,比如 tkprof source_file out_file sort = parm1 sort = parm2。 ● 所跟踪的查询如果有大量物理读取,通常表明缺少索引。 ● 所跟踪的查询输出如果只有内存读取,就说明使用了一个索引。 ● 当前在 10gR1 中有一个程序故障(在 10gR2 中已经修正):“ORA-922: missing or invalid option”,并且如下的消息被记录到 TKPROF 报告文件中:“Error in CREATE TABLE of PLAN table: SCOTT.prof$plan_table. ORA-922: missing or invalid option”。 ● 使用 DBMS_MONITOR 时,确保在完成操作后禁用跟踪;否则,将跟踪满足指定条件的每个会话。 ● 跟踪文件是系统在某一个特定时刻的实时反映。相反,执行计划是在分析 TKPROF 清单时生成的, 这也就有了一点延迟。行源操作程序清单也是跟踪文件的一部分,并且可以用来查看是否有数据库对象在 跟踪执行后发生了变化。 ● 如果有很多开发人员或 DBA 都在使用 PLAN_TABLE,则一定要使用 SET STATEMENT_ID 标识语句。 ● 使用 EXPLAIN 而不是 TRACE,这样就不用为运行这个查询而等待。EXPLAIN 不用真正运行这个查 询就可以显示查询的路径。一般只对那些多查询的批处理任务使用 TRACE,然后找到在这个批处理任务中 哪些查询很慢。 ● 可以使用 Oracle 提供的 utlxpls.sql 和 utlxplp.sql 查询来对 PLAN_TABLE 进行查询,而不用自 己写查询或对输出进行格式化。 ● 从上至下读还是从下往上读完全取决于从 PLAN_TABLE 表里检索信息的查询的编写方式。 ● AUTOTRACE 选项也可以提供一个查询的 EXPLAIN PLAN。AUTOTRACE 提供了很多 TRACE 和 TKPROF 统计信息,如磁盘读取(physical reads)和内存读取(consistent reads+db block gets)。 ● 如果在设置 AUTOTRACE 时出现了“Unable to verify plan table format or existence”错误, 必须使用 utlxplan.sql 来创建计划表。 ● AUTOTRACE 不能用于查询系统视图,因为用户没有查看底层对象的权限。 ● 通过运行 EXPLAIN PLAN,就可以访问 PLAN_TABLE 表里的 PARTITION_START 和 PARTITION_STOP 列来查看分区。 ● 访问 V$sqlarea 表可以得到经常通过跟踪查询获取的统计信息。 ● 还可以在 Developer/2000 产 品 里 使 用 TRACE。为跟踪表单 ,可直接在命令行里设置 statistics=yes。 ● 表 PLAN_TABLE 的 OPERATION 和 OPTIONS 列在调整查询时是最重要的列。这个 OPERATION 列显示 了实际执行的操作(包括连接类型),而 OPTIONS 列告诉您什么时候执行可能需要索引的全表扫描。 ● 表 PLAN_TABLE 的 ID 列显示了处理语句时的顺序。调整 SQL 语句的基本规则之一就是改动查询, 这样可以改动这个查询里步骤的顺序 ID。对查询里步骤的执行顺序的改动通常会提高或降低查询的性能开 销。 ● 表 PLAN_TABLE 的列 PARENT_ID 非常重要,因为它显示了 EXPLAINPLAN 的两个步骤的依赖性。如 果 EXPLAIN PLAN 的一部分有 PARENT_ID,这就表明这条语句必须在指定的 PARENT_ID 步骤之前执行。 ● 当估测应如何调整查询时,表 PLAN_TABLE 的 BYTES 列就特别重要。如果使用了索引并且 BYTES 值非常大,这就表明全表扫描将更有效率(也就是说,读取索引和数据的成本要比只在全表扫描里读取数据 的成本更大)。同时,字节数还可以帮助我们判断哪个表应该在查询时先被访问(驱动表),因为有些表可能 会限制其他表需要的字节数。 ● 查询里的列 COST 和 BYTES 值都是估计出来的。成本或字节数估算值较高的查询可能比估算值较 低的查询运行得更快。 162 ● X$KSPPI 表只能由 SYS 用户访问。在第 15 章中可以看到如何访问 X$表和使用这些参数的技巧。 不要在没有 Oracle 技术指导下使用这些未归档的参数。同时在 Oracle 的不同版本里这些视图里的布局和 列名称都可能会发生变动。 ● 跟踪查询可以提高性能,但使用内置了这些未入档的 TRACE init.ora 参数的 TRACE 功能,可以 让您深入地(或更好地)了解如何解决 Oracle 里的错误。 ● Oracle 公司喜欢把 STORED OUTLINES 看作 PLAN STABILITY。 ● Oracle 可以对每个查询运用 STORED OUTLINES,以查询执行计划。为了运用 STORED OUTLINES, 这个查询必须与原先存储的查询完全相同。稍许的改变都会使 Oracle 认为这两个查询不一样,并不使用这 个纲要。这个规则就跟 Oracle 在数据库中运行查询时分析它们所用的规则一样。 第第第第 10101010 章章章章 使用使用使用使用 PL/SQLPL/SQLPL/SQLPL/SQL 提高性能提高性能提高性能提高性能 ((((针对针对针对针对 DBADBADBADBA 和开发人员和开发人员和开发人员和开发人员)))) Oracle 10g 使 PL/SQL 又进了一步。本章将主要介绍 10g 中有用的新提示(到 10gR2 为止)以及在以前 版本中继续有用的提示。一旦可以使用所有强有力的查询来监控您的系统,那么就有必要使它们自动化运 行。PL/SQL 除了可完成以上的工作外,同时还将提供强大而有效的数据包和可用于性能调整的过程。PL/SQL 引擎处理所有的 PL/SQL 请求,并且将代码传递给 Oracle 去执行。当 PL/SQL 被传递给 Oracle 之后,通常 是放置在 Oracle 的系统全局区(SGA)中,特殊情况下会放置在共享池内。在 Oracle 中,PL/SQL 的源代码 可以以过程、函数、数据包,或者触发器的形式被存储在数据库中。一旦这些对象以编译过的格式存储在 数据库中,那么用户只要获得相应对象的执行特权就都可以使用任何的 Oracle 工具来执行这些对象。一旦 开始执行对象,p-代码(可执行代码)将被加载到 SGA 共享池中,再由 Oracle 执行。一个 PL/SQL 对象将会 一直保存在 SGA 共享池里,直到根据最近最少使用(Least Recently Used,简写为 LRU)算法将该对象设置 为失效。因此,如果有任何程序需要调用对象,只要该对象还没有失效,它就不必被重新加载到 SGA 共享 池中。所以,Oracle 通常是查询 SGA 共享池(效率很高)中的对象,而不是到磁盘(效率较低)上加载对象。 如何使用 PL/SQL 更好地调整 SQL 很有可能就是影响性能的最大驱动因素,当然,本章节中也将介绍其他的 调整方案。本章的第一部分着重介绍对 PL/SQL 的理解和定位。 本章主要内容: ● 使用 DBMS_APPLICATION_INFO 进行实时监控 ● 在 RAC 环境中为实时监控使用 DBMS_APPLICATION_INFO 的自定义替换 ● 在数据库的表中记录计时信息 ● 减少 PL/SQL 程序的单元迭代和迭代的时间 ● 使用 ROWID 进行迭代处理 ● 将数据类型、IF 语句的排列和 PLS_INTEGER 标准化 ● 减少对 SYSDATE 的调用 ● 减少 MOD 函数的使用 ● 在共享池中查找特定对象 ● 当错误发生时清空共享池 ● 在共享池中固定(pin)对象 ● 标识需要被固定的 PL/SQL 对象 ● 使用 PL/SQL 在共享池中固定所有包 ● 使用和修改 DBMS_SHARED_POOL.SIZES 163 ● 从 DBA_OBJECT_SIZE 中获取详细的对象信息 ● 发现无效的对象 ● 发现已禁用的触发器 ● 使用 PL/SQL 关联数组用于快速引用表查找 ● 访问 USER_SOURCE、USER_TRIGGER 和 USER_DEPENDENCIES ● 在 PL/SQL 中使用 Oracle 的 Date 数据类型 ● 使用 PL/SQL 来调整 PL/SQL ● 了解 PL/SQL 对象定位的含义 ● 使用回滚段打开大型游标 ● 使用数据库的临时表来提高性能 ● 集成用户跟踪机制以定位执行位置 ● 限制动态 SQL 的使用 ● 使用管道表函数来建立复杂的结果集 ● 使用条件编译限制调式命令 ● 为初学者提供的例子 10.110.110.110.1 使用使用使用使用 DBMS_APPLICATION_INFODBMS_APPLICATION_INFODBMS_APPLICATION_INFODBMS_APPLICATION_INFO 进行实时监控进行实时监控进行实时监控进行实时监控 DMBS_APPLICATION_INFO 包为用户提供了一个强有力的机制,用于交换环境中执行处理的时间点信息。 下面的程序清单列举了一个相关的例子,即每隔 1000 行,就让一个长时间运行的 PL/SQL 程序提供处理信 息。PL/SQL 代码段每隔 1000 行记录就更新应用程序的信息,更新的内容主要是处理的记录数和花费的时 间。 以下是说明更新所有雇员工资的示例: DECLARE CURSOR cur_employee IS SELECT employee_id, salary, ROWID FROM s_employee_test; lv_new_salary_num NUMBER; lv_count_num PLS_INTEGER := 0; lv_start_time_num PLS_INTEGER; BEGIN lv_start_time_num := DBMS_UTILITY.GET_TIME; FOR cur_employee_rec IN cur_employee LOOP lv_count_num := lv_count_num + 1; -- Determination of salary increase lv_new_salary_num := cur_employee_rec.salary; UPDATE s_employee_test SET salary = lv_new_salary_num WHERE rowid = cur_employee_rec.ROWID; IF MOD(lv_count_num, 1000) = 0 THEN DBMS_APPLICATION_INFO.SET_MODULE('Records Processed: ' || 164 lv_count_num, 'Elapsed: ' || (DBMS_UTILITY.GET_TIME - lv_start_time_num)/100 || ' sec'); END IF; END LOOP; COMMIT; DBMS_APPLICATION_INFO.SET_MODULE('Records Processed: ' || lv_count_num, 'Elapsed: ' || (DBMS_UTILITY.GET_TIME - lv_start_time_num)/100 || ' sec'); END; / 通过查询 V$SESSION 视图,可以监控处理过程,示例如下: SELECT username, sid, serial#, module, action FROM V$SESSION WHERE username = 'SCOTT'; 注意,与执行 PL/SQL 块的查询不同,该查询需要运行于单独的会话中。 以下是查询 V$SESSION 视图的输出结果,分 3 次不同的时间进行查询。最后一次查询的时间是在 PL/SQL 程序单元已经完成时。 USERNAME SID SERIAL# MODULE ACTION ---------- --- ------- ------------------------- ----------------- SCOTT 7 4 SQL*Plus SCOTT 10 10 Records Processed: 1000 Elapsed: 0.71 sec USERNAME SID SERIAL# MODULE ACTION ---------- --- ------- -------------------------- ------------------ SCOTT 7 4 SQL*Plus SCOTT 10 10 Records Processed: 10000 Elapsed: 4.19 sec USERNAME SID SERIAL# MODULE ACTION ---------- --- ------- -------------------------- ------------------- SCOTT 7 4 SQL*Plus SCOTT 10 10 Records Processed: 25000 Elapsed: 9.89 sec 所获得的响应时间主要取决于系统的运行速度,以及体系结构的完美程度。在上面的输出中,会发现 每个查询都返回了两个记录,这是因为在 SCOTT 模式中有两个不同的 SQL*Plus 会话在运行,一个是正在执 行的更新雇员工资信息的 PL/SQL 程序单元,另一个是正在通过 V$SESSION 视图监控进程的 SQL 语句。以 上示例演示了一种用于特定环境的有价值的技巧,并提供了实时监控的机制。这样就能更加容易地准确测 量出程序已经运行的时间,并可估计出程序还要多长时间才能完成。 如果 DBA 不想让用户都有权使用 V$SESSION 视图来获取所有用户的信息,那么他们可以在 V$SESSION 视图上创建一个新视图,以限制用户只能检索正在执行的用户的会话信息。可以以 SYS 用户的身份,通过 执行以下命令来完成这项任务。该语法可以创建新的视图(这个新的视图称为 session_log,当然也可以使 用其他的名称)。在查询中使用 USER,接下来将返回使用数据类型 VARCHAR2 的会话用户名称(已登录用户)。 165 CREATE VIEW session_log AS SELECT * FROM V$SESSION WHERE username = USER; 以下语法创建了一个公共同义词(public synonym)。 CREATE PUBLIC SYNONYM session_log FOR session_log; 下面的语法为所有的用户授予了 SELECT 权限: GRANT SELECT ON session_log TO PUBLIC; 在建立 session_log 视图之后,如前面的代码所示,可将前面 V$SESSION 视图的查询替换为以下查询 中所示的 session_log 视图中的 SELECT 查询,以限制输出结果只能包含用户执行查询的信息。 SELECT username, sid, serial#, module, action FROM session_log; 技巧: 使用由 Oracle 提供的 DBMS_APPLICATION_INFO 包来记录 V$SESSION 视图在各个时间点上 的信息,可以实现对长时间运行的程序的监控。 10.210.210.210.2 在在在在 RACRACRACRAC 环境中为实时监控使用自定义包代替环境中为实时监控使用自定义包代替环境中为实时监控使用自定义包代替环境中为实时监控使用自定义包代替 DBMS_APPLICATION_INFODBMS_APPLICATION_INFODBMS_APPLICATION_INFODBMS_APPLICATION_INFO 上面的内容只能应用于 RAC 环境,因为实例特有的表(V$表)将成为提供应用级实时反馈的不适当机制。 在 RAC 环境中,多个 Oracle 实例将用于单个 Oracle 数据库。在任何时候,每个实例都会占用部分系统的 负载。因此,在某一个实例上运行的进程将不能看到(通过 V$SESSION 和 V$SESSION_LONGOPS)由在 RAC 的 另一个实例上运行的会话所提供的实时反馈。 通过引入类似于 DBMS_APPLICATION_INFO 调用和行为的自定义包可以克服这样的限制。本质上,这是 通过物理表(T$SESSION)在数据库级别上提供数据库使用的所有实例信息的持久显示来完成的。自定义包包 含了大部分 DBMS_APPLICATION_INFO 中常用的设置函数(set_action、set_client_info、set_module)。当 在包中调用这些设置函数时,它们首先会将调用传递给 DBMS_APPLICATION_INFO 包,这样实例特定的 V$表 就会被更新,然后这个包将会复制 T$SESSION 表中会话的 V$记录。如果您只关注用于会话的实例上运行的 内容(或者不在 RAC 环境中),则可以继续查询 V$SESSION 表查看由自定义应用代码提供的实时反馈。如果 在 RAC 环境中,并且想要查看由运行的进程提供的实时反馈信息,则无论这些进程使用哪些实例,都应该 查询 T$SESSION 表。 注意,本节的提示内容只是特定于由 DBMS_APPLICATION_INFO 提供的基本监控功能,并不涉及自定义 包提供的长时间操作支持。然而,这里介绍的技术可以方便地扩展到长时间的操作上,这样它们都能用于 监控 RAC 环境中的任何实例。 首先介绍一个物理表,它类似于 V$SESSION 中的某些列: create table t$session 166 ( instance varchar2(100) not null, audsid number not null, sid number, serial# number, program varchar2(100), module varchar2(100), action varchar2(100), client_info varchar2(100), osuser varchar2(100), username varchar2(100), machine varchar2(100), terminal varchar2(100), logon_time date, last_update_time date not null ) pctfree 50 pctused 40 initrans 10 maxtrans 255 storage (initial 1M next 1M minextents 1 maxextents unlimited pctincrease 0) nologging; comment on table t$session is 'SessionInfo Persistent Storage Table.'; comment on column t$session.instance is 'The Instance Name.'; comment on column t$session.audsid is 'The Auditting SID (from V$SESSION.'; comment on column t$session.sid is 'The SID (from V$SESSION).'; comment on column t$session.serial# is 'The Serial# (from V$SESSION).'; comment on column t$session.program is 'The Program (from V$SESSION).'; comment on column t$session.module is 'The Module (specfied by the user in the api call).'; comment on column t$session.action is 'The Action (specfied by the user in the api call).'; comment on column t$session.client_info is 'The Client Info (specfied by the user in the api call).'; comment on column t$session.osuser is 'The OS User (from V$SESSION).'; comment on column t$session.username is 'The User Name (from V$SESSION).'; comment on column t$session.machine is 'The Machine (from V$SESSION).'; comment on column t$session.terminal is 'The Terminal (from V$SESSION).'; comment on column t$session.logon_time is 'The Logon Time (from V$SESSION.'; comment on column t$session.last_update_time is 'The last update time of this 167 record.'; create index t$session_idx1 on t$session ( instance, audsid ) pctfree 10 initrans 100 maxtrans 255 storage (initial 500K next 500K minextents 1 maxextents unlimited pctincrease 0) nologging; 注意对表的设计有以下几个重要点: ● 为了便于在 API 包中对“值太大”的保护,所有 varchar2 列的长度都相同。 ● 为了在高度并发的环境中保持最佳性能,这个表不使用任何 PK 或 UK 约束。然而,列可以由以下 约束进行逻辑绑定。 • PK = instance,audsid • UK = instance,sid,serial# 下面将介绍 DBMS_APPLICATION_INFO 的自定义版本,它支持相同的调用规范。在介绍源代码前,首先 将简述必要的过程: ● init_sessinfo_record(procedure, internal) 初始化会话信息记录,它会将当前会话的 V$SESSION 内容写入持久存储表中。 ● persistence_cleanup_session(procedure, internal) 使用运行代码的实例的 V$SESSION 记录同步会话信息持久存储表。 ● persistence_cleanup(procedure, internal) 协调持久会话记录与实例的同步。 ● write_sessinfo_record(procedure, internal) 将会话信息记录写入持久表中。 ● release_session_records(procedure) 删除与本次会话相关的持久记录。 ● set_action(procedure) 更新执行调用代码的实例和会话的 V$SESSION 和持久表的动作。 ● set_client_info(procedure) 更新执行调用代码的实例和会话的 V$SESSION 和持久表的 客户信息。 ● set_module(procedure) 更新执行调用代码的实例和会话的 V$SESSION 和持久表的模块和 动作。 源代码如下: CREATE OR REPLACE PACKAGE tsc_appinfo_pkg is 168 --A custom procedure to provide cleanup operations. procedure release_session_records; --Setter calls that mimic DBMS_APPLICATION_INFO. procedure set_action (p_action_name_c in varchar2); procedure set_client_info (p_client_info_c in varchar2); procedure set_module (p_module_name_c in varchar2, p_action_name_c in varchar2); END tsc_appinfo_pkg; / CREATE OR REPLACE PACKAGE BODY tsc_appinfo_pkg is --************************** --Declare package variables. --************************** --The name of the Instance servicing the current session. pg_session_instance_name_c varchar2(100); --The AUDSID of the current session. --This is needed to find the appropriate record in V$SESSION. pg_session_audsid_n number; --The earliest time when the next persistence table cleanup can occur. --The default will be in the past so that the first call to this package --within a session will drive a cleanup operation. pg_next_cleanup_time date := sysdate - 1; --This SessionInfo Record. --This record is used to replicate the updates being performed against --V$SESSION so thatthey can be written to the SessionInfo Persistent --Storage Table without the need to read V$SESSION. --Important Note: A given session has one and only one record in -- V$SESSION. pg_max_length_i constant integer := 100; type pg_sessinfo_type is record (arowid rowid, sid t$session.sid%type, serial# t$session.serial#%type, program t$session.program%type, module t$session.module%type, action t$session.action%type, client_info t$session.client_info%type, osuser t$session.osuser%type, username t$session.username%type, machine t$session.machine%type, terminal t$session.terminal%type, logon_time t$session.logon_time%type, last_update_time t$session.last_update_time%type); pg_sir pg_sessinfo_type; 169 --Reuseable cursors. cursor pg_current_sessions_cur is select audsid from V$SESSION; --*************************** --Declare package exceptions. --*************************** PU_FAILURE exception; pragma exception_init (PU_FAILURE, -20000); INVALID_COLUMN exception; pragma exception_init (INVALID_COLUMN, -904); PRECISION_ERROR exception; pragma exception_init (PRECISION_ERROR, -1438); RESOURCE_LOCKED exception; pragma exception_init (RESOURCE_LOCKED, -54); UNIQUE_VIOLATION exception; pragma exception_init (UNIQUE_VIOLATION, -1); --**************************** --Declare local program units. --**************************** --init_sessinfo_record procedure. -- --Description: Initializes the SessionInfo Record that will be used to -- persist, in this package, the contents of the sessions's -- V$SESSION information. -- --Technical: This procedure should only be called once at package -- instantiation. -- --Notes: This is a supporting program unit that is NOT exposed to the -- user. As such, this program unit must push exceptions up -- through the call stack. -- --Syntax: init_sessinfo_record; PROCEDURE init_sessinfo_record is cursor v_session_cur(p_audsid_i IN number, p_text_length_i IN integer) is select /*+ FIRST_ROWS */ sid, serial#, substr(program, 1, p_text_length_i), substr(module, 1, p_text_length_i), substr(action, 1, p_text_length_i), substr(client_info, 1, p_text_length_i), 170 substr(osuser, 1, p_text_length_i), substr(username, 1, p_text_length_i), substr(machine, 1, p_text_length_i), substr(terminal, 1, p_text_length_i), logon_time from V$SESSION where audsid = p_audsid_i; BEGIN --Retrieve V$SESSION information and store it in the SessionInfo Record. open v_session_cur(pg_session_audsid_n, pg_max_length_i); fetch v_session_cur into pg_sir.sid, pg_sir.serial#, pg_sir.program, pg_sir.module, pg_sir.action, pg_sir.client_info, pg_sir.osuser, pg_sir.username, pg_sir.machine, pg_sir.terminal, pg_sir.logon_time; close v_session_cur; EXCEPTION when PU_FAILURE then raise PU_FAILURE; when OTHERS then --Error logging goes here. NULL; END init_sessinfo_record; --persistence_cleanup_session procedure. --Description: Synchronizes the SessionInfo Persistent Storage Table with -- the V$SESSION records of the Instance in which the code -- is executing. The goal is to remove from the persistence -- tables those records whose parent session is no longer -- running in the Instance from which it originated. --Notes: Do not send an exception back to the caller. --Syntax: persistence_cleanup_session; PROCEDURE persistence_cleanup_session is pragma autonomous_transaction; type v_audsid_table is table of number; v_current_array v_audsid_table; v_persistent_array v_audsid_table; v_purge_array v_audsid_table := v_audsid_table(); 171 v_found_b boolean; cursor v_persistent_sessions_cur(p_instance_c IN varchar2) is select audsid from t$session where instance = p_instance_c; BEGIN --Obtain a list of all currently active sessions in the current --instance. open pg_current_sessions_cur; fetch pg_current_sessions_cur bulk collect into v_current_array; close pg_current_sessions_cur; --Obtain a list of all sessions for the current instance appearing --in the persistent table. open v_persistent_sessions_cur(pg_session_instance_name_c); fetch v_persistent_sessions_cur bulk collect into v_persistent_array; close v_persistent_sessions_cur; --Transfer to the Purge Array those records from the Persistent Array --that are still in the current array. for x in 1..v_persistent_array.count loop v_found_b := false; for y in 1..v_current_array.count loop if (v_current_array(y) = v_persistent_array(x)) then v_found_b := true; exit; end if; end loop; if ( not v_found_b ) then v_purge_array.extend(); v_purge_array(v_purge_array.count) := v_persistent_array(x); end if; end loop; --Purge from the persistent table those records that are still in the --persistent array as these are records that no longer have a --counterpart in V$SESSION. if (v_purge_array.count > 0) then forall i in 1..v_purge_array.count delete from t$session where instance = (select instance from v$instance) and audsid = v_purge_array(i); end if; commit; EXCEPTION when PU_FAILURE then rollback; 172 when OTHERS then rollback; --Error logging goes here. END persistence_cleanup_session; --persistence_cleanup procedure. --Description: Coordinates the synchronization of persistent Session -- records with the Instance in which they originated. --Syntax: persistence_cleanup; PROCEDURE persistence_cleanup is BEGIN --For performance reasons, Persistence Cleanup will never occur more --than once per minute. if ( sysdate > pg_next_cleanup_time ) then persistence_cleanup_session; pg_next_cleanup_time := sysdate + 1/1440; end if; END; --write_sessinfo_record procedure. --Description: Writes the SessionInfo Record to the persistent table. --Technical: For performance reasons, this subroutine will generate a -- single insert per package instantiation. The new rowid will -- be captured during the insert. From that point forward the -- captured rowid will be used to perform subsequent updates to -- the inserted record. --Notes: Do not send an exception back to the caller. --Syntax: write_sessinfo_record; PROCEDURE write_sessinfo_record is pragma autonomous_transaction; BEGIN --If we have a RowID for the SessionInfo Record then we will use it to --perform an update. if ( pg_sir.arowid is not null ) then update t$session set module = pg_sir.module, action = pg_sir.action, client_info = pg_sir.client_info, last_update_time = pg_sir.last_update_time where rowid = pg_sir.arowid; --It would be odd to have updated zero rows by rowid. --This would most likely be an incorrect cleanup operation being --performed by another instance or by a user. We will record an --error and then nullify the rowid of the SessionInfo Record so that --it will be treated as a new record needing an insert. if ( sql%rowcount = 0 ) then 173 pg_sir.arowid := null; --Error logging goes here. end if; end if; --If we do not have a RowID for the SessionInfo Record then we must --insert a new record and captures its rowid for use in future updates. if ( pg_sir.arowid is null) then --Just in case there is already a record in the table. --Normally, this shouldn't be necessary but we should compensate --for packages that may have been flushed from memory. --The primary concern here is to maintain the logical PK and UK --constraints on the table. delete from t$session where instance = pg_session_instance_name_c and audsid = pg_session_audsid_n; commit; delete from t$session where instance = pg_session_instance_name_c and sid = pg_sir.sid and serial# = pg_sir.serial#; commit; insert /*+ append */ into t$session(instance, audsid, sid, serial#, program, module, action, client_info, osuser, username, machine, terminal, logon_time, last_update_time) values (pg_session_instance_name_c, pg_session_audsid_n, pg_sir.sid, pg_sir.serial#, pg_sir.program, pg_sir.module, pg_sir.action, pg_sir.client_info, pg_sir.osuser, pg_sir.username, pg_sir.machine, pg_sir.terminal, pg_sir.logon_time, pg_sir.last_update_time) returning rowid into pg_sir.arowid; end if; commit; EXCEPTION when PU_FAILURE then rollback; when OTHERS then rollback; 174 --Error logging goes here. END write_sessinfo_record; --***************************** --Declare global program units. --***************************** --release_session_records procedure. --Description: Removes the persistent records associated with this session. -- Since the Session data is being persisted in a -- physical table we don't have the convenience of the records -- going away when the session disconnects(as is the case with -- V$SESSION). Normally, this should -- not pose a major issue since other calls to this package -- will eventually perform the persistence_cleanup operation. -- However, in the event that you are annoyed by lingering -- persistence information you can force the records to be -- purged immediately with this procedure. --Notes: Do not send an exception back to the caller. --Syntax: release_session_records; procedure release_session_records is pragma autonomous_transaction; BEGIN delete from t$session where instance = pg_session_instance_name_c and audsid = pg_session_audsid_n; commit; EXCEPTION when PU_FAILURE then rollback; when OTHERS then rollback; --Error logging goes here. END release_session_records; --set_action procedure. --Description: Updates the "Action" of both V$SESSION and the persistent -- table for the Instance and Session in which the calling -- code is executing. --Notes: Do not send an exception back to the caller. --Syntax: set_action (p_action_name_c in varchar2); --Where: p_action_name_c = The Action value to be set. PROCEDURE set_action (p_action_name_c in varchar2) is BEGIN --Perform cleanup operations on the Persistent Storage Tables. persistence_cleanup; --Update V$SESSION. Remember, this data will only be visible to 175 --sessions connected to the same Instance. DBMS_APPLICATION_INFO.set_action(p_action_name_c); --Update the SessionInfo Record to reflect the same change just made --to V$SESSION. pg_sir.last_update_time := sysdate; pg_sir.action := substr(p_action_name_c, 1, pg_max_length_i); --Update the SessionInfo Persistent Storage Table. Remember, this will --be visible to all connections to the database, regardless of the --Instance the connection is coming through. write_sessinfo_record; EXCEPTION when PU_FAILURE then NULL; when OTHERS then --Error logging goes here. NULL; END set_action; --set_client_info procedure. --Description: Updates the "Client Info" of both V$SESSION and the -- persistent table for the Instance and Session in which -- the calling code is executing. --Notes: Do not send an exception back to the caller. --Syntax: set_client_info (p_client_info_c in varchar2); --Where: p_client_info_c = The Client Info value to be set. PROCEDURE set_client_info (p_client_info_c in varchar2) is BEGIN --Perform cleanup operations on the Persistent Storage Tables. persistence_cleanup; --Update V$SESSION. Remember, this data will only be visible to --sessions connected to the same Instance. DBMS_APPLICATION_INFO.set_client_info(p_client_info_c); --Update the SessionInfo Record to reflect the same change just made --to V$SESSION. pg_sir.last_update_time := sysdate; pg_sir.client_info := substr(p_client_info_c, 1, pg_max_length_i); --Update the SessionInfo Persistent Storage Table. Remember, this will --be visible to all connections to the database, regardless of the --Instance the connection is coming through. write_sessinfo_record; EXCEPTION when PU_FAILURE then NULL; when OTHERS then --Error logging goes here. 176 NULL; END set_client_info; --set_module procedure. --Description: Updates the "Module" and "Action" of both V$SESSION and -- the persistent table for the Instance and Session in which -- the calling code is executing. --Notes: Do not send an exception back to the caller. --Syntax: set_action (p_module_name_c in varchar2, -- p_action_name_c in varchar2); --Where: p_module_name_c = The Module value to be set. -- p_action_name_c = The Action value to be set. PROCEDURE set_module (p_module_name_c in varchar2, p_action_name_c in varchar2) is BEGIN --Perform cleanup operations on the Persistent Storage Tables. persistence_cleanup; --Update V$SESSION. Remember, this data will only be visible to --sessions connected to the same Instance. DBMS_APPLICATION_INFO.set_module(p_module_name_c, p_action_name_c); --Update the SessionInfo Record to reflect the same change just made --to V$SESSION. pg_sir.last_update_time := sysdate; pg_sir.module := substr(p_module_name_c, 1, pg_max_length_i); pg_sir.action := substr(p_action_name_c, 1, pg_max_length_i); --Update the SessionInfo Persistent Storage Table. Remember, this will --be visible to all connections to the database, regardless of the --Instance the connection is coming through. write_sessinfo_record; EXCEPTION when PU_FAILURE then NULL; when OTHERS then --Error logging goes here. NULL; END set_module; BEGIN --Retrieve the AUDSID of the current session. BEGIN pg_session_audsid_n := sys_context('userenv', 'sessionid'); EXCEPTION when OTHERS then pg_session_audsid_n := to_number(to_char(sysdate, 'yyyydddsssss')); END; --Retrieve the name of the Oracle Instance in which the code is executing. 177 DECLARE cursor v_instance_cur(p_text_length_i IN integer) is select substr(instance_name, 1, p_text_length_i) from v$instance; BEGIN open v_instance_cur(pg_max_length_i); fetch v_instance_cur into pg_session_instance_name_c; close v_instance_cur; EXCEPTION when OTHERS then pg_session_instance_name_c := 'Error: '||user; END; --Initialize the SessionInfo record. init_sessinfo_record; EXCEPTION when OTHERS then --Error logging goes here. NULL; END tsc_appinfo_pkg; / 这个包(以及其支持的表)的主要目是为了在物理表中存储那些通常只存在于内存表(V$SESSION 和 V$SESSION_LONGOPS)中的信息,从而提供 RAC 环境中执行进程的实时反馈。物理存储表的简单引入带来了 一些严重的性能损害,这在该包的创建过程会体现。在与该包的 setter 方法交互时,开发人员必须认识到 这一点。 为了抵消使用物理表带来的性能影响,采取的措施如下: ● T$SESSION 表不使用任何 PK 或 UK 约束。这个包强制使用的逻辑约束是: • PK = instance, audsid • UK = instance, sid, serial# ● 在包实例化时,当前会话的 V$SESSION 记录只读取一次。从这时候起,只有通过包中的设置函数 修改的 module、action 和 client_info 值才能在物理表中被更新。因为读取 V$SESSION 的开销极大,因此 只读取一次 V$SESSION。通常这没有问题,因为大部分 V$SESSION 中的列都是静态列。如果开发人员在包 中和 DBMS_APPLICATION_INFO 中混合调用设置函数,则可能会遇到问题。在这种情况下,物理表将不会对 直接调用 DBMS_APPLICATION_INFO 产生的修改进行任何反射。 ● 必须使事务尽可能简短以避免竞争。 作为一名开发人员,当把这个包集成到自定义模块中时,需要完成以下工作: ● 不要在这个包和 DBMS_APPLICATION_INFO 中混合调用设置函数,这样做会导致物理存储表与 V$SESSION 不同步。 178 ● 明智地使用这些调用。在这个包中调用设置函数的执行时间比在 DBMS_APPLICATION_INFO 中调用 设置函数的时间更长。在快速执行中,应该为迭代代码提供在第 x 次迭代时调用这些设置函数的逻辑。如 果不采用这种预防措施,迭代代码的性能会受到极大影响。 以下脚本用于测试该自定义包: set timing on; BEGIN tsc_appinfo_pkg.set_module(user, 'Working...'); for i in 1..10000 loop tsc_appinfo_pkg.set_client_info('Iteration: '||i); end loop; tsc_appinfo_pkg.set_action('Done'); END; / 无论另一个会话是否用于同一个 RAC 节点,通过查询 T$SESSION 表可以监控脚本的执行过程。如果第 二个会话知道它和第一个会话一样,用于同一个 RAC 节点,则还可以监控 V$SESSION。 提示: 在 RAC 环境中为实时监控使用自定义包代替 DBMS_APPLICATION_ INFO。 10.310.310.310.3 在数据库的表中记录计时信息在数据库的表中记录计时信息在数据库的表中记录计时信息在数据库的表中记录计时信息 监控性能是一个持续的过程。运行环境中的许多变量都会随着时间的推移改变或者影响性能;因此, 性能监控应当是持续不断的。这些变量包括用户的增多、数据的增加、报表的增加、应用程序的修改/改进 的部署,以及由其他应用程序产生的额外的系统负载。因此,需要记住的是,必须定期对 Oracle 系统进行 监控,以确保系统性能保持在(或者高于)一个可接受的水平。 有一种监控系统性能的方法就是创建一种机制,对应用 程序的某些方面进行定时的统计分析并记录 下来。批处理程序就是这种监控程序最好的选择。这种监控程序可以把定时统计的信息保存到数据库的表 中,以完成任 务。以下示例提供了这种使用数据库的表记录信息的方法,即创建一张数据库的表,然后并 入统计进程时间的 INSERT 语句。在数据库的表中记录的重要信息包括程序标识符(用于标识程序的独特方 法)、 程序执行的日期和时间,以及执行程序所用的时间。这样就为该应用程序添加了一列,即更新的记 录数。这个附加的列对应用程序很重要,因为它将监控正被处理的 雇员记录的增长速度。当为您的应用程 序创建了一张时间记录表后,请增加列以记录额外的有可能会影响计时结果的重要处理信息。因此,以下 程序清单创建用于记 录计时信息的表。 CREATE TABLE process_timing_log (program_name VARCHAR2(30), execution_date DATE, records_processed NUMBER, elapsed_time_sec NUMBER); 一旦这张表创建完毕,就可改进 PL/SQL 程序单元,令其将时间信息记录到 process _timing_log 表 179 中,如下程序清单所示。 CREATE OR REPLACE PROCEDURE update_salary AS CURSOR cur_employee IS SELECT employee_id, salary, ROWID FROM s_employee_test; lv_new_salary_num NUMBER; lv_count_num PLS_INTEGER := 0; lv_start_time_num PLS_INTEGER; lv_total_time_num NUMBER; BEGIN lv_start_time_num := DBMS_UTILITY.GET_TIME; FOR cur_employee_rec IN cur_employee LOOP lv_count_num := lv_count_num + 1; -- Determination of salary increase lv_new_salary_num := cur_employee_rec.salary; UPDATE s_employee_test SET salary = lv_new_salary_num WHERE rowid = cur_employee_rec.ROWID; END LOOP; lv_total_time_num := (DBMS_UTILITY.GET_TIME - lv_start_time_num)/100; INSERT INTO process_timing_log (program_name, execution_date, records_processed, elapsed_time_sec) VALUES ('UPDATE_SALARY', SYSDATE, lv_count_num, lv_total_time_num); COMMIT; END update_salary; / 如上程序清单所示,计时器在程序单元开始运行时开始计时,在程序单元结束运行时停止计时。在计 时器开始计时和结束计时之间的时间段,update_salary 程序的每次操作所用的时间都将被记录到 process_timing_log 表中。如果 update_salary 程序单元执行了 3 次,如下程序清单中所示,那么就有 3 次时间记录被插入到 process_timing_log 表中。 另一种方法就是使用 DBMS_PROFILER 包来获得 PL/SQL 每一行源代码的定时统计信息。可以参阅 Metalink 文献 104377.1,“Performance of New PL/SQL Features”,以获取更多的信息。 EXECUTE update_salary EXECUTE update_salary EXECUTE update_salary 以下程序清单从 process_timing_log 表中检索信息。 180 SELECT program_name, TO_CHAR(execution_date,'MM/DD/YYYY HH24:MI:SS') execution_time, records_processed, elapsed_time_sec FROM process_timing_log ORDER BY 1,2; PROGRAM_NAME EXECUTION_TIME RECORDS_PROCESSED ELAPSED_TIME_SEC ------------- ------------------- ----------------- ---------------- UPDATE_SALARY 07/02/2002 19:43:57 25252 8.89 UPDATE_SALARY 07/02/2002 19:44:07 25252 9.11 UPDATE_SALARY 07/02/2002 19:44:15 25252 8.62 这 时输出结果显示一个可能的结果。相同的程序执行所需要的时间也会有差别。如果这个差别随着时间而 增长,说明需要进一步分析程序单元或者应用程序,以判断是 什么造成了执行时间的增长。如果已经建立 了记录机制,那么就可以在任何时候对执行时间进行监控,因为时间信息已经记录在了数据库的表中。 在上一个程序清单中,记录了每一个程序单元的执行时间。如果程序比较复杂,并且需要执行的时间 很长,那么最好修改程序中记录时间统计信息的机制。process-timing-log 表的 INSERT 操作可以在一定 数量的迭代之后再执行,或者只记录程序单元中特定功能的执行时间。 技巧: 在数据库中为长时间运行的 PL/SQL 程序单元建立记录执行时间信息的表,将为您的系统 整合一种具有预警性的性能监测机制。可以在任何时间查看这张表,以判断随着时间的 推移,性能是否有下降现象。 技巧: 根据活动会话的数量确定的系统负载对程序执行的性能有很大的影响;因此,修改数据 库中表的记录方法,为所有的活动会话增加一列,将是很有帮助的。可以通过往程序单 元中增加额外的查询来填充该列,而该程序单元是正在执行从 V$SESSION 视图中检索统 计数任务的程序。 10.410.410.410.4 减少减少减少减少 PL/SQLPL/SQLPL/SQLPL/SQL 程序的单元迭代和迭代的时间程序的单元迭代和迭代的时间程序的单元迭代和迭代的时间程序的单元迭代和迭代的时间 任何牵涉到循环逻辑的 PL/SQL 程序单元都可能存在大幅度提高性能的空间。可以通过两种方式来改 善这种类型的程序潜在的性能。第一种方法是通过逻辑的重构来减少迭代的数量,并保持功能性结果不变。 第二种方法是减少每次迭代的时间。无论采用哪一种方法,都可以大幅度提高系统运行的性能。 在更清晰地阐述该观点之前,让我们假设这样一种情况:我们需要在 PL/SQL 程序中处理 9000 个雇员 的记录,假设每处理一个雇员的记录需要花费 2s。这样总共需要花费 18000s,也就是 5h。如果每处理一 个雇员的记录的时间可以减少到 1s,那么处理 9000 个雇员的记录所需花费的时间也就减少了 9000s 或者 2.5h——差异是如此巨大! 以下程序清单中的示例展示了一个小型的重新构造的 PL/SQL 程序单元,它将用于解释如何减少每次 迭代的处理时间和总的处理时间。程序单元将循环处理 1,000,000 次。每次迭代都添加到递增计数器上, 用来每 100,000 次就显示一条信息,并且同时添加总的次数,用来检查是否已退出循环。为了查看 DBMS _OUTPUT,必须先确保使用 SET SERVEROUTPUT ON 命令。 181 CREATE OR REPLACE PACKAGE stop_watch AS pv_start_time_num PLS_INTEGER; pv_stop_time_num PLS_INTEGER; pv_last_stop_time_num PLS_INTEGER; -- This procedure creates a starting point for the timer routine and –- is usually called once at the beginning of the PL/SQL program unit. PROCEDURE start_timer; -– This procedure retrieves a point in time and subtracts the current -– time from the start time to determine the elapsed time. The -- interval elapsed time is logged and displayed. This procedure is -- usually called repetitively for each iteration or a specified -- number of iterations. PROCEDURE stop_timer; END stop_watch; / Package created. CREATE OR REPLACE PACKAGE BODY stop_watch AS PROCEDURE start_timer AS BEGIN pv_start_time_num := DBMS_UTILITY.GET_TIME; pv_last_stop_time_num := pv_start_time_num; END start_timer; PROCEDURE stop_timer AS BEGIN pv_stop_time_num := DBMS_UTILITY.GET_TIME; DBMS_OUTPUT.PUT_LINE('Total Time Elapsed: ' || TO_CHAR((pv_stop_time_num - pv_start_time_num)/100, '999,999.99') || ' sec Interval Time: ' || TO_CHAR((pv_stop_time_num - pv_last_stop_time_num)/100, '99,999.99') || ' sec'); pv_last_stop_time_num := pv_stop_time_num; END stop_timer; END; / Package body created. SET SERVEROUTPUT ON DECLARE lv_counter_num PLS_INTEGER := 0; lv_total_counter_num PLS_INTEGER := 0; BEGIN stop_watch.start_timer; LOOP lv_counter_num := lv_counter_num + 1; 182 lv_total_counter_num := lv_total_counter_num + 1; IF lv_counter_num >= 100000 THEN DBMS_OUTPUT.PUT_LINE('Processed 100,000 Records. ' || 'Total Processed ' || lv_total_counter_num); lv_counter_num := 0; EXIT WHEN lv_total_counter_num >= 1000000; END IF; END LOOP; stop_watch.stop_timer; END; / Processed 100,000 Records. Total Processed 100000 Processed 100,000 Records. Total Processed 200000 Processed 100,000 Records. Total Processed 300000 Processed 100,000 Records. Total Processed 400000 Processed 100,000 Records. Total Processed 500000 Processed 100,000 Records. Total Processed600000 Processed 100,000 Records. Total Processed 700000 Processed 100,000 Records. Total Processed 800000 Processed 100,000 Records. Total Processed 900000 Processed 100,000 Records. Total Processed 1000000 Total Time Elapsed: .71 sec Interval Time: .71 sec PL/SQL procedure successfully completed. 修改程序,仅当每次递增计数器的值到达 100 000 时,才增加变量 lv_total_counter_num 的值,这样 总体的执行时间将减少,如下程序清单所示。 DECLARE lv_counter_num PLS_INTEGER := 0; lv_total_counter_num PLS_INTEGER := 0; BEGIN stop_watch.start_timer; LOOP lv_counter_num := lv_counter_num + 1; IF lv_counter_num >= 100000 THEN DBMS_OUTPUT.PUT_LINE('Processed 100,000 Records. Total ' || 'Processed ' || lv_total_counter_num); lv_total_counter_num := lv_total_counter_num + lv_counter_num; lv_counter_num := 0; EXIT WHEN lv_total_counter_num >= 1000000; END IF; END LOOP; stop_watch.stop_timer; END; 183 / 该程序清单中并没有包含 DBMS_OUTPUT.PUT_LINE 对已处理记录数的输出结果。 Total Time Elapsed: .47 sec Interval Time: .47 sec PL/SQL procedure successfully completed. 上面的示例解释了通过改变迭代逻辑来减少迭代的单位处理时间所造成的性能差异。这是个基本的示 例,它展示了在 1 000 000 次迭代中提高 34%的效率。基于迭代和重构,改进所造成的性能差异是巨大的。 技巧: 当一个 PL/SQL 程序单元涉及到大量的循环或递归时,就应当关注于减少每一次迭代的单 位时间。这样的效果很明显,并且也很容易通过数学方法判断出总的改进的性能。循环 或者递归也应当被仔细检查,并通过重构来减少迭代的次数,但要保留函数的功能一致 性。由于 PL/SQL 和 SQL 本身具有极大灵活性,可以通过使用不同的方式得到同样的结果。 如果 PL/SQL 程序单元的运行并不十分理想,那么有时必须用其他方式重写逻辑。 10.510.510.510.5 使用使用使用使用 ROWIDROWIDROWIDROWID 进行迭代处理进行迭代处理进行迭代处理进行迭代处理 当 PL/SQL 程序从数据库中检索记录,执行特定的列值计算,再使用 UPDATE 命令完成更新,并检索出 记录后,若使用 ROWID 则有助于提高这个 PL/SQL 程序的性能。在检索每一条记录时,可以将 ROWID 添加到 指定的列中。在更新每一条记录时,可以在谓词子句中使用 ROWID。当访问一张表的记录时,ROWID 是速度 最快的方法,甚至比唯一参考索引还快。 以下示例解释了使用 ROWID 带来的性能提高。该示例检索 25,000 个雇员的所有记录,为每个雇员计 算新的工资,然后再更新雇员们的工资记录。实际的工资计算没有说明。第一个 PL/SQL 代码段说明了使用 employee_id 列进行更新的时间结果,该列上带有一个唯一的索引。 DECLARE CURSOR cur_employee IS SELECT employee_id, salary FROM s_employee_test; lv_new_salary_num NUMBER; BEGIN stop_watch.start_timer; FOR cur_employee_rec IN cur_employee LOOP -- Determination of salary increase lv_new_salary_num := cur_employee_rec.salary; UPDATE s_employee_test SET salary = lv_new_salary_num WHERE employee_id = cur_employee_rec.employee_id; END LOOP; COMMIT; stop_watch.stop_timer; END; 184 / 以下输出结果展示了两次执行上面代码段的时间。 Total Time Elapsed: 1.71 sec Interval Time: 1.71 sec PL/SQL procedure successfully completed. Total Time Elapsed: 1.59 sec Interval Time: 1.59 sec PL/SQL procedure successfully completed. 在函数功能保持不变的前提下,将更新操作改变为基于 ROWID 的更新操作。这牵涉到把 ROWID 添加到 SELECT 语句中并改变 UPDATE 谓词子句,如下程序清单所示。 DECLARE CURSOR cur_employee IS SELECT employee_id, salary, ROWID FROM s_employee_test; lv_new_salary_num NUMBER; BEGIN stop_watch.start_timer; FOR cur_employee_rec IN cur_employee LOOP -- Determination of salary increase lv_new_salary_num := cur_employee_rec.salary; UPDATE s_employee_test SET salary = lv_new_salary_num WHERE rowid = cur_employee_rec.ROWID; END LOOP; COMMIT; stop_watch.stop_timer; END; / 以下输出结果展示了两次执行上面代码段的时间。 Total Time Elapsed: 1.45 sec Interval Time: 1.45 sec PL/SQL procedure successfully completed. Total Time Elapsed: 1.48 sec Interval Time: 1.48 sec PL/SQL procedure successfully completed. 从时间结果中可以证明,使用 ROWID 的查询执行速度更快。第一个 PL/SQL 代码段的 UPDATE 语句执行 的方式是通过在 employee_id 上的索引来取得 ROWID,然后再通过 ROWID 在表中进行搜索;与此相反,第 二个 PL/SQL 代码段的 UPDATE 语句直接在表中使用 ROWID 进行搜索,而忽略了索引搜索。当有更多的记录 需要处理,或者所使用的索引不是唯一索引时, 性能的提高将显得更加明显。 185 技巧: 当需要在 PL/SQL 程序单元中选择一条记录,并且该记录需要在同一个 PL/SQL 程序单元 中进行计算时,使用 ROWID 变量将提高性能。需要注意的是,这不能用于索引组织表 (IOT)。 10.610.610.610.6 将数据类型将数据类型将数据类型将数据类型、、、、IFIFIFIF 语句的排列和语句的排列和语句的排列和语句的排列和 PLS_INTEGERPLS_INTEGERPLS_INTEGERPLS_INTEGER 标准化标准化标准化标准化 可以在标准的 PL/SQL 开发中引入部分小幅度的编程修改以提高性能。本节将介绍 3 种方法。 ● 确保比较运算中的数据类型相同 ● 根据条件出现的频率来排序 IF 条件 ● 使用 PLS_INTEGER PL/SQL 数据类型进行整数运算 10.6.110.6.110.6.110.6.1 确保比较运算中的数据类型相同确保比较运算中的数据类型相同确保比较运算中的数据类型相同确保比较运算中的数据类型相同 当变量或者常量值互相比较时,它们应该有相同的数据类型定义。当比较牵涉到不同的数据类型时, Oracle 将会自动转换其中的一个数值,这样就产生了额外的不希望的开销。无论何时在一个条件中进行数 值比较,参与比较的数值必须是同一种数据类型。在开发 PL/SQL 程序单元时必须遵循这种标准,这也是很 好的编程风格。 下面的程序清单解释了在比较中采用不同数据类型的代价,也就是在 IF 语句中将一个数字的数据类 型和字符值相比较。 CREATE OR REPLACE PROCEDURE test_if (p_condition_num NUMBER) AS lv_temp_num NUMBER := 0; lv_temp_cond_num NUMBER := p_condition_num; BEGIN stop_watch.start_timer; FOR lv_count_num IN 1..100000 LOOP IF lv_temp_cond_num = '1' THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = '2' THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = '3' THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = '4' THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = '5' THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = '6' THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = '7' THEN lv_temp_num := lv_temp_num + 1; ELSE 186 lv_temp_num := lv_temp_num + 1; END IF; END LOOP; stop_watch.stop_timer; END; / 以下程序清单展示了如何执行 test_if 过程。 EXECUTE test_if(8) 以下输出结果展示了执行 test_if 程序的结果。 Total Time Elapsed: .26 sec Interval Time: .26 sec PL/SQL procedure successfully completed. 不同的数据类型造成了不必要的开销。如果过程中参与比较的数值采用相同的数据类型,那么以下程序清 单中过程的执行速度将更快一些: CREATE OR REPLACE PROCEDURE test_if (p_condition_num NUMBER) AS lv_temp_num NUMBER := 0; lv_temp_cond_num NUMBER := p_condition_num; BEGIN stop_watch.start_timer; FOR lv_count_num IN 1..100000 LOOP IF lv_temp_cond_num = 1 THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = 2 THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = 3 THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = 4 THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = 5 THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = 6 THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = 7 THEN lv_temp_num := lv_temp_num + 1; ELSE lv_temp_num := lv_temp_num + 1; END IF; END LOOP; stop_watch.stop_timer; END; / 187 以下程序清单展示了新的 test_if 程序的执行情况。 EXECUTE test_if(8) Total Time Elapsed: .17 sec Interval Time: .17 sec PL/SQL procedure successfully completed. 正如以上程序清单中所示,速度提高了 23%。执行的频率越高,这种改进越明显。 技巧: 确保所有比较条件中的比较值具有相同的数据类型。另外,确保同属于一类数值类型的 比较值具有相同的子数据类型是很有帮助的。因此,在最后的示例中,IF 语句中的比较 量和 1、2、3 等进行比较,也就是将 NUMBER 类型与 PLS_INTEGER 类型进行比较。这仍然 将造成 Oracle 类型转换的开销。要消除这种开销,应当将 1、2、3 等数改成 1.0,2.0, 3.0……如果最后的示例做了这种修改,执行时间将降低到 26s。 10.6.210.6.210.6.210.6.2 根据条件出现的频率来排序根据条件出现的频率来排序根据条件出现的频率来排序根据条件出现的频率来排序 IFIFIFIF 条件条件条件条件 当编写带有多重条件的 IF 语句时,合理的编程方法就是按照某些特定顺序来进行条件检查。较为典 型的顺序是按照字母顺序或者数字顺序排列的,以使代码段的可读性更强,但它往往不是最优的顺序。尤 其是在 IF 语句中使用了多次的 ELSIF 条件,最常满足的条件应首先出现,其次是较容易满足的条件,依此 类推。 在前面一节中,程序的执行首先总是传递参数 8,这意味着每次循环都必须检查 IF 语句的 8 个条件操 作,以便确定是否满足条件。如果传递的是 1,就表示第一个条件满足了所有的 IF 执行条件,这样我们就 得到了一个更加优化的结果,如下所示: EXECUTE test_if(1) Total Time Elapsed: .05 sec Interval Time: .05 sec PL/SQL procedure successfully completed. 以上输出结果说明了在 IF 条件语句中采用最有效的顺序,可显著提高性能。因此,在进行条件编码 之前,应当进一步分析 IF 语句的条件执行顺序,以确保最高的效率。 技巧: 确保 PL/SQL 的 IF 条件语句以最经常满足的条件为排列顺序,而不是基于数字或者字母 的顺序。 10.6.310.6.310.6.310.6.3 使用使用使用使用 PLS_INTEGER PL/SQLPLS_INTEGER PL/SQLPLS_INTEGER PL/SQLPLS_INTEGER PL/SQL 数据类型进行整数运算数据类型进行整数运算数据类型进行整数运算数据类型进行整数运算 声明数值数据类型的通用标准是使用 NUMBER 数据类型。在 PL/SQL 2.2 版中,Oracle 引入了 PLS_INTEGER 数据类型。这种数据类型可以用于代替各种数值数据系列类型的声明中,只要变量的值是一 个整数,并且在-2147483647 到+2147483647 的范围内。因此,绝大部分计数器和操作符都可以使用这种数 据类型。PLS_INTGER 可以使用更少的内部命令来处理,因此使用这种数据类型就提高了性能。这种变量用 的越多,性能的提高就越明显。 188 以下程序清单展示了这样的提高。代码段与前面两节中的示例基本相同,但数据类型的声明从 NUMBER 改为了 PLS_INTEGER。 CREATE OR REPLACE PROCEDURE test_if (p_condition_num PLS_INTEGER) AS lv_temp_num PLS_INTEGER := 0; lv_temp_cond_num PLS_INTEGER := p_condition_num; BEGIN stop_watch.start_timer; FOR lv_count_num IN 1..100000 LOOP IF lv_temp_cond_num = 1 THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = 2 THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = 3 THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = 4 THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = 5 THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = 6 THEN lv_temp_num := lv_temp_num + 1; ELSIF lv_temp_cond_num = 7 THEN lv_temp_num := lv_temp_num + 1; ELSE lv_temp_num := lv_temp_num + 1; END IF; END LOOP; stop_watch.stop_timer; END; / 以下程序清单展示了如何执行 test_if 过程。 EXECUTE test_if(1) 由执行的结果可以明显地看出以下程序性能的提高: Total Time Elapsed: .03 sec Interval Time: .03 sec PL/SQL procedure successfully completed. 技巧: 处理整数时使用 PLS_INTEGER 类型,可以提高性能。 189 技巧: 如果将一个带有精确值的数字赋值给一个 PLS_INTEGER 变量,那么这个值将取整为一个 整数,就像对这个数字运行了 ROUND 取整函数一样。 10.710.710.710.7 减少对减少对减少对减少对 SYSDATESYSDATESYSDATESYSDATE 的调用的调用的调用的调用 为了检索当前的日期和时间,使用 SYSDATE 变量可算是一个方便的方法。调用 SYSDATE 会产生一些开 销;因此,如果要用这个变量来记录特定处理的日期,那么对这个变量的调用应当只在程序每次迭代开始 时。这种只在程序开始时调用 SYSDATE 的技术假定只在程序开始时需要记录日期。 以下示例解释了如何减少 SYSDATE 的调用。该示例有 10,000 次迭代循环,每次迭代均调用 SYSDATE(仅 仅是变量中的日期部分,因为 TRUNC 函数用于截去时间部分)。 DECLARE lv_current_date DATE; BEGIN stop_watch.start_timer; FOR lv_count_num IN 1..10000 LOOP lv_current_date := TRUNC(SYSDATE); END LOOP; stop_watch.stop_timer; END; / 以下输出结果展示了上面代码段两次执行的时间。 Total Time Elapsed: .04 sec Interval Time: .04 sec PL/SQL procedure successfully completed. Total Time Elapsed: .01 sec Interval Time: .01 sec PL/SQL procedure successfully completed. 以下 PL/SQL 代码段被修改为在程序开始时只调用一次 SYSDATE,并在每次迭代时设定另一个变量。 DECLARE lv_current_date DATE := TRUNC(SYSDATE); lv_final_date DATE; BEGIN stop_watch.start_timer; FOR lv_count_num IN 1..10000 LOOP lv_final_date := lv_current_date; END LOOP; stop_watch.stop_timer; END; / 以下的输出结果展示了上面代码段两次执行的时间。 190 Total Time Elapsed: .00 sec Interval Time: .00 sec PL/SQL procedure successfully completed. Total Time Elapsed: .01 sec Interval Time: .01 sec PL/SQL procedure successfully completed. 从上面的示例可以很清楚地看出,对SYSDATE 的调用产生了开销,如果可能的话,应当减少对 SYSDATE 的调用。 技巧: 应当限制在迭代或递归循环中调用 SYSDATE,因为这个变量将产生额外的开销。在声明 时将一个 PL/SQL DATE 变量赋值给 SYSDATE,然后引用这个 PL/SQL 变量,以减少开销。 10.810.810.810.8 减少减少减少减少 MODMODMODMOD 函数的使用函数的使用函数的使用函数的使用 某些 PL/SQL 函数在使用时比其他函数的开销要大。MOD 就是这种函数,最好是使用其他的 PL/SQL 逻 辑来取代它以提升综合性能。以下示例解释了这点。这是一个很有用的函数,但是如果以如下方式在 IF 语 句中执行了该函数,那么就增加了不必要的开销。 BEGIN stop_watch.start_timer; FOR lv_count_num IN 1..10000 LOOP IF MOD(lv_count_num, 1000) = 0 THEN DBMS_OUTPUT.PUT_LINE('Hit 1000; Total: ' || lv_count_num); END IF; END LOOP; stop_watch.stop_timer; END; / 以下输出结果展示了上述代码段两次执行的时间: Hit 1000; Total: 1000 Hit 1000; Total: 2000 Hit 1000; Total: 3000 Hit 1000; Total: 4000 Hit 1000; Total: 5000 Hit 1000; Total: 6000 Hit 1000; Total: 7000 Hit 1000; Total: 8000 Hit 1000; Total: 9000 Hit 1000; Total: 10000 Total Time Elapsed: .04 sec Interval Time: .04 sec PL/SQL procedure successfully completed. Total Time Elapsed: .04 sec Interval Time: .04 sec 191 以上 PL/SQL 代码段已经过修改,取消了 MOD 函数的使用,而采用其他的 PL/SQL 逻辑来执行相同的检 查,如下程序清单所示。 DECLARE lv_count_inc_num PLS_INTEGER := 0; BEGIN stop_watch.start_timer; FOR lv_count_num IN 1..10000 LOOP lv_count_inc_num := lv_count_inc_num + 1; IF lv_count_inc_num = 1000 THEN DBMS_OUTPUT.PUT_LINE('Hit 1000; Total: ' || lv_count_num); lv_count_inc_num := 0; END IF; END LOOP; stop_watch.stop_timer; END; / Hit 1000; Total: 1000 Hit 1000; Total: 2000 Hit 1000; Total: 3000 Hit 1000; Total: 4000 Hit 1000; Total: 5000 Hit 1000; Total: 6000 Hit 1000; Total: 7000 Hit 1000; Total: 8000 Hit 1000; Total: 9000 Hit 1000; Total: 10000 Total Time Elapsed: .01 sec Interval Time: .01 sec PL/SQL procedure successfully completed. Total Time Elapsed: .00 sec Interval Time: .00 sec 正如上两个示例所示,MOD 函数增加了开销,而使用 PL/SQL 的 IF 语句可获得更好的性能。 技巧: MOD 函数就是那种用其他的 PL/SQL 逻辑来替代执行时速度更快的函数。尽管这是微小的 细节,但应当将其作为标准编程技巧引入到您的 PL/SQL 标准编程技术中。 10.910.910.910.9 共享池和固定共享池和固定共享池和固定共享池和固定 PL/SQLPL/SQLPL/SQLPL/SQL 对象对象对象对象 SHARED_POOL_SIZE 设置了共享池在 SGA 中分配的空间大小(请参阅第 4 章和附录 A,了解 SHARED_POOL_SIZE 的细节和其他与共享池密切相关的参数)。共享池存储了在数据库中执行的所有的 SQL 语句和 PL/SQL 数据块。根据 Oracle 管理 SGA 共享池的方法,随着时间推移,SGA 共享池将出现碎片。此 外,由于 Oracle 并不对正在参与进程处理的对象进行时间统计,这样就有可能遇到 Oracle 的错误,表明 SGA 的共享池无法为新的对象分配足够的内存。您将收到的确切信息是:“ORA-4031:unable to allocate 192 XXX bytes of shared memory”(无法分配 XXX 字节的共享内存,XXX 是需要分配的字节数)。如果得到了 这个错误信息,就意味着应当尽可能快地扩展您的 SGA 共享池。在 Oracle 9i 之前的版本中,是通过修改 SHARED_POOL_SIZE 参数,然后关闭数据库并重启来实现共享沲的扩展的。快速但有代价的办法就是忽略这 个错误,直到下一次数据库关闭时再让其清空 SGA 共享池。这可以通过使用以下命令来实现(用户必须有 ALTER SYSTEM 特权): alter system flush shared_pool; 在 Oracle 9i 中,可以通过修改 SHARED_POOL_SIZE 来实现扩容,而不用关闭数据库,只要设置的值 不超过 SGA_MAX_SIZE。这就省去了以前版本必需的一些工作。当启动数据库时,您希望仍然能把大型对象 固定到共享池中,且必须确认有足够大的共享池来缓存所有这些语句。 技巧: 在 Oracle 9i 中,当数据库运行时,可以修改 SHARED_POOL_SIZE 的值,只要设置的值不 超过 SGA_MAX_SIZE。请参阅第 4 章了解如何设置初始化参数。 10.9.110.9.110.9.110.9.1 将将将将 PL/SQLPL/SQLPL/SQLPL/SQL 对象语句固定对象语句固定对象语句固定对象语句固定((((缓存缓存缓存缓存))))到内存中到内存中到内存中到内存中 如果不能保证提供足够大的 SHARED_POOL_SIZE 在内存中保存所有的语句,那么就应当采取谨慎的措 施将最重要的对象固定(缓存)到内存中。下面的示例解释了如何使用 DBMS_SHARED_POOL.KEEP 过程在内存 中固定 PL/SQL 对象语句(固定的是 PROCESS_DATE 过程)。 begin dbms_shared_pool.keep('process_date','p'); end; /  或者 execute sys.dbms_shared_pool.keep ('SYS.STANDARD'); 如果将对象固定在内存中,那么在下一次关闭数据库之前,这个对象就不会失效或者被清空。还需要 考虑的是,Metalink 的注意事项 61760.1:DBMS_SHARED_POOL 将被创建为用户 SYS。其他用户不拥有这个包。 需要访问这个包的任何用户都必须由 SYS 授予执行权限。如果在 SYS 模式中创建这个包并在不同的模式中 运 行 示 例 代 码 ,则首先必须给运行示例 (即 TEST)的用户授予 EXECUTE_CATALOG_ROLE 角 色 且 在 DBMS_SHARED_POOL 上给 TEST 以 EXECUTE 权限,然后需要在 SYS.DBMS_SHARED_POOL.KEEP 中完全地限定这 个包,因为 dbmspool.sql 脚本并不为这个包创建公有同义词。 技巧: 使用 DBMS_SHARED_POOL.KEEP 过程将 PL/SQL 对象固定到共享池中。 193 注意: 要使用这个过程,首先必须运行 DBMSPOOL.SQL 脚本。在启动 DBMSPOOL.SQL 脚本后, PRVTPOOL.PLB 脚本将自动执行。这些脚本不能使用 CATPROC.SQL 来运行。 10.9.210.9.210.9.210.9.2 固定所有的包固定所有的包固定所有的包固定所有的包 如果要固定系统中所有的包,可以执行以下程序清单中的代码(来自于 Oracle 的 Metalink)。 declare own varchar2(100); nam varchar2(100); cursor pkgs is select owner, object_name from dba_objects where object_type = 'PACKAGE'; begin open pkgs; loop fetch pkgs into own, nam; exit when pkgs%notfound; dbms_shared_pool.keep(own || '.' || nam, 'P'); end loop; end; / 一个更有针对性的方法就是仅仅固定需要重载的包,这要比固定所有的包要好,特别是因为从 Oracle 8i 开始,绝大部分的 DBA 接口都牵涉到 PL/SQL 包。然而,每一次启动都要删除表可能会产生问题,因为 被固定的包并不需要每天都重新加载。至少,必须检查确认并没有准备固定无效的包。通常 Oracle 自带的 (也是应该保存的)有问题的包包括 STANDARD、DBMS_STANDARD 和 DIUTIL。 技巧: 使用在 PL/SQL 中绑定的 DBMS_SHARED_POOL.KEEP 过程,可以在数据库启动时(如果内存/ 共享池允许的话)固定所有的包,并避免将来在加载包时出现错误。 10.1010.1010.1010.10 标识需要固定的标识需要固定的标识需要固定的标识需要固定的 PL/SQLPL/SQLPL/SQLPL/SQL 对象对象对象对象 碎片化造成在共享池中虽然有许多小的碎片可以使用,但没有足够大的连续空间,这在共享池中是普 遍的现象。消除共享池错误(如 10.9 节介绍的错误)的关键就是知道您即将加载对象的大小是否可能会产生 问题。一旦知道了这个存在问题的 PL/SQL,那么就可以在数据库启动时(这时共享池是完全连续的)就将这 个代码固定。这将确保在调用大型包时,它已经在共享池里,而不是在共享池中搜索连续的碎片(在使用系 统时,这些碎片可能就不复存在)。可以查询 V$DB_OBJECT_CACHE 视图来判断 PL/SQL 是否很大并且还没有 被标识为“kept”的标记。今后需要加载这些对象时,可能会产生问题(因为它们的大小和需要占用大量连 续的内存)。它将只展示当前在缓存中的代码。以下示例用于搜索那些需要大于 100KB 连续空间的对象。 194 select name, sharable_mem from v$db_object_cache where sharable_mem > 100000 and type in ('PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'PROCEDURE') and kept = 'NO'; 技巧: 通过查询 V$DB_OBJECT_CACHE 表,可以发现那些没有固定,但由于所需空间太大而很有 可能导致潜在问题的对象。 10.1110.1110.1110.11 使用和修改使用和修改使用和修改使用和修改 DBDBDBDBMS_SHARED_POOL.SIZESMS_SHARED_POOL.SIZESMS_SHARED_POOL.SIZESMS_SHARED_POOL.SIZES 通过 DBMS_SHARED_POOL.SIZES 包提供的过程可以非常方便和精确地查看到共享池的分配情况。这个 调用接受一个 MINIMUM SIZE 参数,并将显示共享池中所有大于所提供参数的游标和对象。以下程序清单展 示了检索该信息的实际语句。 Select to_char(sharable_mem / 1000 ,'999999') sz, decode (kept_versions,0,' ',rpad('yes(' || to_char(kept_versions) || ')' ,6)) keeped, rawtohex(address) || ',' || to_char (hash_value) name, substr(sql_text,1,354) extra, 1 iscursor from v$sqlarea where sharable_mem > &min_ksize * 1000 union select to_char(sharable_mem / 1000 ,'999999') sz, decode(kept,'yes', 'yes ','') keeped, owner || '.' || name || lpad(' ',29 - (length(owner) + length(name) ) ) || '(' || type || ')' name, null extra, 0 iscursor from v$db_object_cache v where sharable_mem > &min_ksize * 1000 order by 1 desc; 这个查询可以被放置到您自己构造的过程包中,以特定格式的视图方式来显示共享池中的游标和对象。 查找大对象查找大对象查找大对象查找大对象 可以使用 DBMS_SHARED_POOL.SIZE 包过程(DBMS_SHARED_POOL 是包的名称,sizes 是包内的过程)来查 看那些使用的共享内存高于您设定的阈值的对象。以下程序清单是在阈值为 100KB 时执行该过程的方法(后 面是输出结果): Set serveroutput on size 10000; begin sys.dbms_shared_pool.sizes(100); end; / SIZE(K) KEPT NAME 195 118 YES SYS.STANDARD (PACKAGE) 109 SELECT DT.OWNER,DT.TABLE_NAME,DT.TABLESPACE_NAME, DT.INITIAL_EXTTENT,DT.NEXT_EXTENT,DT.NUM_ROWS, DT.AVG_ROW_LEN, SUM(DE.BYTES) PHY_SIZE FROM DBA_TABLES DT,DBA_SEGMENTS DE WHERE DT.OWNER = DE.OWNER AND DT.TABLE_NAME = DE.SEGMENT_NAME AND DT.TABLESPACE_NAME = DE.TABLESPACE_NAME GROUP BY DT.OWNER,DT.TABLE_NAME,DT.TABLESPACE_NAME, DT.INITIAL_EXTENT,DT.NEX (0B14559C,3380846737) (CURSOR) 22 RDBA.RDBA_GENERATE_STATISTICS (PACKAGE) PL/SQL procedure successfully completed. 技巧: 可以使用 DBMS_SHARED_POOL.SIZE 包来查找一个对象的特定信息。 10.1210.1210.1210.12 从从从从 DBA_OBJECT_SIZE DBA_OBJECT_SIZE DBA_OBJECT_SIZE DBA_OBJECT_SIZE 中获取详细的对象信息中获取详细的对象信息中获取详细的对象信息中获取详细的对象信息 查询 DBA_OBJECT_SIZE 视图,显示特定对象所占用的内存情况,包括更多的关于对象的详细信息,如 下所示。 Compute sum of source_size on report Compute sum of parsed_size on report Compute sum of code_size on report Break on report select * from dba_object_size where name = 'RDBA_GENERATE_STATISTICS'; OWNER NAME TYPE SOURCE_SIZE PARSED_SIZE CODE_SIZE RDBA RDBA_GENERATE_STATISTICS PACKAGE 5023 4309 3593 RDBA RDBA_GENERATE_STATISTICS PACKAGE BODY 85595 0 111755 SUM 90618 4309 115348 (partial display only...not all columns shown) 获得共享池里当前可使用的连续空间获得共享池里当前可使用的连续空间获得共享池里当前可使用的连续空间获得共享池里当前可使用的连续空间 为什么加载对象时共享池会返回错误?因为 Oracle 无法在共享池中为该段代码分配一个足够大的空 间。在 10.11 节中您已经学习了如何确定您的代码的大小。也可以参阅 10.9 节在共享池中固定对象来了解 如何共享池中固定代码段。现在就可以仔细检查查询,以确定需要加载到共享池中的代码是否太大,需要 被固定,或者是如果有可能就需要重新研究代码并进行适当的压缩。 下面的查询访问 X$表(请参阅第 15 章),必须是 SYS 用户才能访问这些表: 196 select ksmchsiz, ksmchcom from x$ksmsp where ksmchsiz > 10000 and ksmchcom like '%PL/SQL%'; 这个查询说明了被访问的包太大,应当在数据库启动时就固定它。如果省去查询的最后一行,它同样 将显示内存中的大段空间(KSMCHCOM="free memory"和 KSMCHCOM= "permanent memory"),这些空间在未来 需要加载大段的代码时仍然可以使用。请参阅第 13 章,了解更多有关 X$表和示例结果的详细信息。 技巧: 通过查询 X$ksmsp 可以找出在共享池中占用大段空间的 PL/SQL 代码块。它们是在数据库 启动时需要固定的候选对象。 10.1310.1310.1310.13 发现无效的对象发现无效的对象发现无效的对象发现无效的对象 开发人员经常需要修改一小部分在执行时无法编译,并导致程序运行失败的 PL/SQL 代码。一个简单 查询,可以帮助您在最终用户遇到这些错误之前定位这些故障,如下所示。 col "Owner" format a12 col "Object" format a20 col "OType" format a12 col "Change DTE" format a20 select substr(owner,1,12) "Owner", substr(object_name,1,20) "Object", object_type "OType", to_char(last_ddl_time, 'DD-MON-YYYY HH24:MI:SS') "Change Date" from dba_objects where status <> 'VALID' order by 1, 2; 上面的示例显示了所有 INVALID 的对象,这意味着这些对象无法成功通过编译,或者对相互依赖的对 象的修改造成了它们的状态变为 INVALID。例如,假设我们有一个 PRCESS_DATE 过程被发现是无效的,那 么可以使用下面的命令来手工重新编译这个过程: alter procedure PROCESS_DATE compile; 一旦执行了这个命令,并且 PROCESS_DATE 通过重新编译之后,该过程的状态将自动被 Oracle 从 INVALID 设置为 VALID。另一个可以使用的手工方法就是调用 DBMS_ UTILITY.COMPILE_SCHEMA 程序包,如 下程序清单所示,按照已经确定的方案重新编译所有的存储过程、函数和程序包。 begin dbms_utility.compile_schema('USERA'); end; / 如果需要查询现有方案中全部的 PL/SQL 对象的状态,可以执行以下程序清单中的代码: 197 column object_name format a20 column last_ddl_time heading 'last ddl time' select object_type, object_name, status, created, last_ddl_time from user_objects where object_type in ('PROCEDURE', 'FUNCTION', 'PACKAGE', 'PACKAGE BODY', 'TRIGGER'); OBJECT_TYPE OBJECT_NAME STATUS CREATED last ddl ------------------ -------------------- ------- --------- --------- PACKAGE DBMS_REPCAT_AUTH VALID 12-MAY-02 12-MAY-02 PACKAGE BODY DBMS_REPCAT_AUTH VALID 12-MAY-02 12-MAY-02 TRIGGER DEF$_PROPAGATOR_TRIG VALID 12-MAY-02 12-MAY-02 PROCEDURE ORA$_SYS_REP_AUTH VALID 12-MAY-02 12-MAY-02 TRIGGER REPCATLOGTRIG VALID 12-MAY-02 12-MAY-02 技巧: 通过查询 DBA_OBJECTS(针对系统范围内的对象)或者 USER_OBJECT(仅仅针对您的方案) 来查询对象的状态,避免由用户造成的错误和重载。可以使用 DBMS_UTILITY.COMPILE_ SCHEMA 来重新编译单个对象或者整个方案。 10.1410.1410.1410.14 发现已禁用的触发器发现已禁用的触发器发现已禁用的触发器发现已禁用的触发器 在某些方面,禁用的触发器比无效的对象更加危险,因为它从不失败——而是根本不执行!这将对应 用程序产生严重的后果,因此,基于该程序代码内商业逻辑的商业进程也将出现错误。以下脚本用于标识 已禁用的触发器。 col "Owner/Table" format a30 col "Trigger Name" format a25 col "Event" format a15 col "Owner" format a10 select substr(owner,12) "Owner", trigger_name "Trigger Name", trigger_type "Type", triggering_event "Event", table_owner||'.'||table_name "Owner/Table" from dba_triggers where status <> 'ENABLED' order by owner, trigger_name; 如果将修改以上查询仅仅用于检查 SYS 和某些特定的列,您将会得到由 Oracle 创建的已禁用的触发 器,如下程序清单所示。 Select trigger_name "Trigger Name",STATUS, trigger_type "Type", triggering_event "Event" from dba_triggers where status <> 'ENABLED' and owner = 'SYS' order by owner, trigger_name; Trigger Name STATUS Type Event 198 ------------------------- -------- ---------------- --------------- AURORA$SERVER$SHUTDOWN DISABLED BEFORE EVENT SHUTDOWN AURORA$SERVER$STARTUP DISABLED AFTER EVENT STARTUP NO_VM_CREATE DISABLED BEFORE EVENT CREATE NO_VM_DROP DISABLED BEFORE EVENT DROP SYS_LOGOFF DISABLED BEFORE EVENT LOGOFF SYS_LOGON DISABLED AFTER EVENT LOGON 如果需要查找方案中所有的触发器,可以执行以下代码。 column trigger_name format a15 column trigger_type format a15 column triggering_event format a15 column table_name format a15 column trigger_body format a25 select trigger_name, trigger_type, triggering_event, table_name, status, trigger_body from user_triggers; TRIGGER_NAME TRIGGER_TYPE TRIGGERING_EVENT TABLE_NAME STATUS TRIGGER_BODY UPDATE_TOTAL AFTER STATEMENT INSERT OR UPDATE ORDER_MAIN ENABLED begin OR DELETE update total_orders set order_total = 10; end; 技巧: 通过查询 DBA_TRIGGERS(针对系统范围内的对象)或者 USER_ TRIGGERS(针对您的方案) 来查询触发器的状态,避免由于已禁用的触发器而产生的错误。已禁用的触发器将会给 应用程序带来致命的结果:它们不会失败,它们根本就不执行。 10.1510.1510.1510.15 将将将将 PL/SQLPL/SQLPL/SQLPL/SQL 表用于快速参考表查询表用于快速参考表查询表用于快速参考表查询表用于快速参考表查询 被设计用来处理传入系统数据的程序通常会结合大量的参考表查询,以便能够正确地校验数据或者对 数据进行编码。当搜索参考表时,通过使用数据类型的唯一索引,并把参考表导入 PL/SQL 表中,对参考表 的查询性能将有大幅度提高。设想一个输入的数据集中包含一个单一的数值列,且必须利用参考表将其翻 译成编码的字符串。以下程序清单是一段用于完成这个任务的代码,它使用的是经典的重复搜索参考表的 方法。 DECLARE v_code_c ref_table.ref_string%type; cursor v_lookup_cur (p_code_n IN number) is select ref_string from ref_table where ref_num = p_code_n; 199 cursor v_inbound_cur is select * from incoming_data; BEGIN --Open a cursor to the incoming data. for inbound_rec in v_inbound_cur loop BEGIN --Calculate the reference string from the reference data. open v_lookup_cur(inbound_rec.coded_value); fetch v_lookup_cur into v_code_c; if v_lookup_cur%notfound then close v_lookup_cur; raise NO_DATA_FOUND; end if; close v_lookup_cur; dbms_output.put_line(v_code_c); --processing logic... --Commit each record as it is processed. commit; EXCEPTION when NO_DATA_FOUND then null;--Appropriate steps... when OTHERS then null;--Appropriate steps... END; end loop; END; / 虽然这个程序看上去效率很高,但事实上它已经受到对参考表的反复查询的影响。尽管 Oracle 可能 已将整个参考表保存在内存中,但由于固定的需要或者其他优先级更高的查询,处理这些查询时还是将产 生一定数量的开销。 一个更加有效的方法就是把整个参考表加载到 PL/SQL 表中去。数值列(搜索所查找的列)在加载时作 为数组索引。当需要查询参考表的数据时,PL/SQL 表将代替参考表——输入数据中需要翻译的代码将作为 PL/SQL 表中的数组索引。处理 PL/SQL 表的继承特性是:如果使用一个无效的数组索引(即输入数据的代码 无法在参照表中找到匹配值),NO_DATA_FOUND 异常将会抛出。以下是相同的处理程序,只是在重写时使用 了 PL/SQL 表来存储参考数据: DECLARE type v_ref_table is table of ref_table.ref_string%type index by binary_integer; v_ref_array v_ref_table; v_code_c ref_table.ref_string%type; cursor v_lookup_cur is 200 select * from ref_table; cursor v_inbound_cur is select * from incoming_data; BEGIN --First, load the reference array with data from the reference table. for lookup_rec in v_lookup_cur loop v_ref_array(lookup_rec.ref_num) := lookup_rec.ref_string; end loop; --Open a cursor to the incoming data. for inbound_rec in v_inbound_cur loop BEGIN --Calculate the reference string from the reference data. v_code_c := v_ref_array(inbound_rec.coded_value); dbms_output.put_line(v_code_c); --processing logic... --Commit each record as it is processed. commit; EXCEPTION when NO_DATA_FOUND then null;--Appropriate steps... when OTHERS then null;--Appropriate steps... END; end loop; END; / 结果是处理的速度有了显著提高,因为使用了 PL/SQL 表来代替实际的数据库表,从而减少了开销。 不久之前人们提出了通过数值对表中的数组进行索引的 需求。因此,数组索引可以是一个字符串值。 当需要解析的编码值不一定是数值时,就可以通过该功能来使用相同的解决方案。考虑一个典型示例,其 中一些带有两 个字符,用来表示状态代码的数据需要进行解析和验证。以下程序清单对上面的过程进行了 修改。数组的索引类型必须是 VARCHAR2 类型。 DECLARE type v_ref_table is table of states_table.state_name%type index by states_table.state_code%type; v_ref_array v_ref_table; v_state_c states_table.state_name%type; cursor v_lookup_cur is select state_code, state_name from states_table; 201 cursor v_inbound_cur is select * from incoming_data; BEGIN --First, load the reference array with data from the reference table. for lookup_rec in v_lookup_cur loop v_ref_array(lookup_rec.state_code) := lookup_rec.state_name; end loop; --Open a cursor to the incoming data. for inbound_rec in v_inbound_cur loop BEGIN --Calculate the reference string from the reference data. v_state_c := v_ref_array(inbound_rec.coded_value); dbms_output.put_line(v_state_c); --processing logic... --Commit each record as it is processed. commit; EXCEPTION when NO_DATA_FOUND then null;--Appropriate steps... when OTHERS then null;--Appropriate steps... END; end loop; END; / 技巧: 将参考表加载到 PL/SQL 表中可以加快查询速度。这是因为利用了 PL/SQL 中数组索引的 优势。 10.1610.1610.1610.16 查找和调整所使用对象的查找和调整所使用对象的查找和调整所使用对象的查找和调整所使用对象的 SQLSQLSQLSQL 有些时候,调整存储对象最困难的部分就是查找存储在数据库中的实际代码。本节关注于能够获取可 以被调整的 SQL 查询。本节中,我们可以通过查询视图来检索隐藏在存储对象背后的实际源代码的信息。 检索一个已创建的名为 PROCESS_DATE 的过程的源代码如下: column text format a80 select text from user_source where name = 'PROCESS_DATE' order by line; 这种查询可以用于查询过程、触发器或者函数。至于数据包,可以将最后一行修改为: 202 order by type, line; TEXT procedure process_date is test_num number; begin test_num := 10; if test_num = 10 then update order_main set process_date = sysdate where order_num = 12345; end if; end; 要检索大家所熟悉的 DBMS_RULE 包的源代码,可以使用下面程序清单中的代码。 column text format a80 select text from dba_source where name = 'DBMS_RULE' and type = 'PACKAGE' order by line; TEXT ------------------------------------------------------------------------ PACKAGE dbms_rule AUTHID CURRENT_USER AS PROCEDURE evaluate( rule_set_name IN varchar2, evaluation_context IN varchar2, event_context IN sys.re$nv_list := NULL, table_values IN sys.re$table_value_list := NULL, column_values IN sys.re$column_value_list := NULL, variable_values IN sys.re$variable_value_list := NULL, attribute_values IN sys.re$attribute_value_list := NULL, stop_on_first_hit IN boolean := FALSE, simple_rules_only IN boolean := FALSE, true_rules OUT sys.re$rule_hit_list, maybe_rules OUT sys.re$rule_hit_list); PROCEDURE evaluate( rule_set_name IN varchar2, evaluation_context IN varchar2, event_context IN sys.re$nv_list := NULL, table_values IN sys.re$table_value_list := NULL, column_values IN sys.re$column_value_list, variable_values IN sys.re$variable_value_list := NULL, attribute_values IN sys.re$attribute_value_list := NULL, simple_rules_only IN boolean := FALSE, 203 true_rules_iterator OUT binary_integer, maybe_rules_iterator OUT binary_integer); FUNCTION get_next_hit( Iterator IN binary_integer) RETURN sys.re$rule_hit; PROCEDURE close_iterator( Iterator IN binary_integer); END dbms_rule; 35 rows selected. 要检索 DBMS_JOB 数据包的内容,可以使用以下程序清单中的代码。 column text format a80 select text from dba_source where name = 'DBMS_JOB' and type = 'PACKAGE BODY' order by line; TEXT PACKAGE BODY dbms_job wrapped 0 abcd abcd ... :2 a0 6b d a0 ac :3 a0 6b b2 ee :2 a0 7e b4 2e ac e5 d0 b2 e9 93 a0 7e 51 b4 2e :2 a0 6b 7e 51 b4 2e 6e a5 57 b7 19 3c b0 46 :2 a0 6b ac :2 a0 b2 ee ac e5 d0 b2 e9 :2 a0 6b :3 a0 6e :4 a0 :5 4d a5 57 :2 a0 a5 57 b7 :3 a0 7e 51 在这个示例中,已使用 WRAP 命令包装(保护)了数据包,所以无法读取输出结果。如果您想调整这样 的代码,那么您不得不放弃了。 可以使用以下查询检索触发器的源代码: column trigger_name format a15 column trigger_type format a15 column triggering_event format a15 column table_name format a15 column trigger_body format a25 select trigger_name, trigger_type, triggering_event, table_name, trigger_body from user_triggers; 204 TRIGGER_NAME TRIGGER_TYPE TRIGGERING_EVEN TABLE_NAME TRIGGER_BODY UPDATE_TOTAL AFTER STATEMENT INSERT OR UPDATE ORDER_MAIN begin OR DELETE update order_main set order_total = 10; end; 要查找 PL/SQL 对象的依赖关系,可以使用以下程序清单中的代码。 column name format a20 column referenced_owner format a15 heading R_OWNER column referenced_name format a15 heading R_NAME column referenced_type format a12 heading R_TYPE select name, type, referenced_owner, referenced_name,referenced_type from user_dependencies order by type, name; NAME TYPE R_OWNER R_NAME R_TYPE INSERT_RECORD PROCEDURE USERA ORDER_MAIN TABLE INSERT_RECORD PROCEDURE SYS STANDARD PACKAGE PROCESS_DATE PROCEDURE SYS STANDARD PACKAGE PROCESS_DATE PROCEDURE USERA ORDER_MAIN TABLE 技巧: 要查找 PL/SQL 数据包过程后的源代码,则要查询 USER_SOURCE 和 DBA_SOURCE 视图。要 查找触发器的源代码,则要查询 USER_TRIGGERS 和 DBA_TRIGGERS 视图。要查找 PL/SQL 对象之间的依赖关系,则要查询 USER_DEPENDENCIES 和 DBA_DEPENDENCIES 视图。 10.1710.1710.1710.17 在处理在处理在处理在处理 DATEDATEDATEDATE 数据类型时使用时间信息数据类型时使用时间信息数据类型时使用时间信息数据类型时使用时间信息 当处理 Oracle 的 DATE 数据类型时,如果把它视为 TIME 数据类型将会更加准确,因为 DATE 数据类型 总是存储完整的临时值,精确到秒。将一个日期变量直接插入到一个数据类型为 DATE 的 PL/SQL 变量或者 数据库列上是不可能的。如果在设计应用程序时不记住这一点,完成的产品很可能会出现您预料之外的副 作用。一个应用程序中由于不正确的日期管理造成的最常见的副作用之一就是,在制作报表时,根据日期 值过滤数据,但每次执行后所返回的结果都不一样。 当用一个数值初始化类型为 DATE 的列或变量时,所缺失的任何部分都将由 Oracle 自动提供。如果初 始值包含了日期信息,那么 Oracle 只需要提供时间信息,反之亦然。这就有了一个问题:您怎样能够区分 在初始化时缺少哪一部分信息?答案非常简单,只有当一个日期变量是根据另一个日期变量初始化来的, 才会自动提供两部分信息。系统变量 SYSDATE 就是这样一种日期变量。这样,如果任何一个列或变量是由 SYSDATE 初始化,那么它在初始化完成时就会包含日期和时间的值。 如果时间是 1998 年 1 月 10 日,3:25:22 A.M,而执行下面的命令: Date_Var_1 date := SYSDATE; 则包含在变量 Date_Var_1 中的数值为: 205 10-JAN-1998 03:25:22. 同样也可以使用一个文本字符串来初始化日期变量。例如: Date_Var_2 date := '10-JAN-98'; 包含在变量 Date_Var_1 中的数值为 10-JAN-98 00:00:00 这里介绍一个简单的 PL/SQL 数据块,如下所示: DECLARE date_var_2 DATE; BEGIN date_var_2 := '10-JAN-98'; DBMS_OUTPUT.PUT_LINE('Selected date is '|| to_char(date_var_2, 'DD-MON-YYYY HH24:MI:SS')); END; / 10-JAN-1998 00:00:00 技巧: 一个 DATE 数据类型总是存储完整的临时值,精确到秒。不可能将一个日期变量值直接插 入到一个数据类型为 DATE 的 PL/SQL 变量或者数据库列上。 基于这一点,所以很显然,Date_Var_1 和 Date_Var_2 是不相等的。尽管它们都包含了日期信息 10-JAN-98,但是它们的时间信息相差了将近三个半小时。程序中存在的问题在于:通常并不希望时间信息 和日期信息一块被继承。考虑这样一个应用程序,它用 SYSDATE 来初始化记录插入数据库表中的时间。如 果一个 PL/SQL 处理程序(或者是一个简单的 SQL SELECT 语句)没有考虑记录的时间信息,那么在处理时将 会丢失这些记录。 既然知道一张表中的日期值包含非午夜 12:00 的时间值,那么下面的语句将丢失部分记录。问题是由 于时间不等,这些语句都将遗失部分记录。 select * from table where date_column = SYSDATE; select * from table where date_column = trunc(SYSDATE); select * from table where date_column = '10-JAN-98'; select * 206 from table where date_column between '01-JAN-98' and '10-JAN-98'; 解决方案是在 WHERE 子句的两边截去时间部分的信息。 一个能预防该问题的方法就是:在条件测试的两边消除时间组件的差别,如下程序清单中所示。 select * from table where trunc(date_column) = trunc(SYSDATE); select * from table where trunc(date_column) = '10-JAN-98'; select * from table where trunc(date_column) between '01-JAN-98' and '10-JAN-98'; 在这些示例上需要注意的是:如果将 NLS_DATE_FORMAT 修改为默认值以外的值,则这些示例将无法运 行。我使用 dd-mon-yy hh:mi:ss 作为格式,但是修改后的查询没有返回任何数据。当我注销并重新登录, 重设 NLS_DATE_FORMAT 设置后,相同的查询就能返回数据。 调整方案是在 WHERE 子句的非列一侧截去时间信息。这个技术会产生我们不希望看到的副作用,它限 制了可能会提高查询性能的索引——在列名上的 TRUNC 函数将限制该列上的索引。真正所需的技术应当能 够调整过滤条件,来包含给定日期内所有可能的时间值。同样要注意以下程序清单中的.000011574 代表一 天中的 1 秒。 Select * from table where date_column between trunc(SYSDATE) and trunc(SYSDATE + 1) - .000011574; select * from table where date_column between to_date('10-JAN-98') and to_date('11-JAN-98') - .000011574; select * from table where date_column between to_date('01-JAN-98') and to_date('11-JAN-98') - .000011574; 技巧: Oracle 的 DATE 包含了日期和时间信息。当查询匹配数据时应当避免限制索引的使用。 办法就是不修改 WHERE 子句的数据列一侧的内容。在非数据列一侧执行所有的修改。正 如在第 2 章看到的,可以增加一个基于函数的索引来克服这个问题。 10.1810.1810.1810.18 调整和测试调整和测试调整和测试调整和测试 PL/SQLPL/SQLPL/SQLPL/SQL 207 也可以使用 PL/SQL 来对您的 PL/SQL 计时,以确保它的性能满足您的要求。这里介绍一个简单的示例, 它指导您如何写一个脚本,以便直接从 SQL*Plus(或者使用 SQL*Plus 中的 PL/SQL)来测试和调整您的过程 (在这个示例中是一个名为 get_customer 的过程)。 set serveroutput on declare cust_name char(100); begin dbms_output.put_line('Start Time: '||to_char(sysdate,'hh24:mi:ss')); get_customer(11111,cust_name); dbms_output.put_line('Complete Time: '||to_char(sysdate,'hh24:mi:ss')); dbms_output.put_line(cust_name); end; / 技巧: 使用 PL/SQL 来显示 PL/SQL 的开始和结束时间。主要是不要忘记使用 PL/ SQL 调整您的 PL/SQL。可以使用 DBMS_PROFILER 包为每一行 PL/SQL 代码获取时间统计信 息。 10.1910.1910.1910.19 了解了解了解了解 PL/SQLPL/SQLPL/SQLPL/SQL 对象定位的含义对象定位的含义对象定位的含义对象定位的含义 在 TUSC 中,因为许多显而易见的原因,我们一般建议将 PL/SQL 对象存储在服务器端。一般情况下, 服务器的处理能力更强,对象也重用得更频繁(尤其是固定到共享池后)。部署的安全措施也更加直接。把 需要处理的 PL/SQL 对象发送到客户端后,性能将取决于客户端的处理能力,并且能够减少客户端与服务器 之间的往返调用次数。但是,如果编码正确,也可对服务器限制这些调用(请参阅 10.20 节的示例)。有关 这个问题目前还存在许多争议,但是随着瘦客户机的发展,服务器将成为存储 PL/SQL 的唯一地方。图10-1 描述了当 PL/SQL 存储在服务器端时,它是如何执行的。下文展示其他一些原因,说明为何应该将代码存储 在服务器端: ● 编译过的代码(p-代码)可以提高性能。 ● 对象可以被固定在 Oracle 的 SGA 中。 ● 可以在数据库层启用事务层的安全性。 ● 冗余代码较少,版本控制问题也较少。 ● 可以在线查询数据源,因为它已经被存储到数据字典中。 ● 更容易做干扰分析,因为代码已经被存储到数据字典中。 ● 占用的内存更少,因为只有代码的副本保存在内存中。 ● 如果使用了数据包,那么整个包在初次使用时就已经被加载了。 208 图 10-1 在服务器端执行一个对象 技巧: 究竟把 PL/SQL 代码存储到什么地方将是一个争论不休的问题。一般说来,代码应当存储 到服务器端,并随着瘦客户机的流行而变为唯一的选择。 10.20 10.20 10.20 10.20 使用回滚段打开大型游标使用回滚段打开大型游标使用回滚段打开大型游标使用回滚段打开大型游标 本节是为那些不想使用 Oracle 的自动撤消功能的开发人员和 DBA 准备的。任何有经验的 PL/SQL 开发 人员都知道,在对数据库进行大型的 INSERTS/UPDATES/DELETES 操作时,需要设置合适的空间并使用回滚 段。如果在一个大型复杂的数据操作之前没有为回滚段明确地设置好合适的空间,那么该操作将会失败。 通常返回的错误代码是“ORA-01562:failed to extend rollback segment”。失败的原因是:对于没有明 确设置回滚段的事务,Oracle 将随机为它指定一个回滚段。如果这个随机分配的回滚段没有足够的空间去 支持整个事务,那么操作就会失败。消除这种类型错误的方法是,估测将要修改的数据量的大小,选择一 个合适的回滚段空间(在这一点上,DBA_ROLLBACK_SEG 视图是很有帮助的),并在执行 DML 语句前设置这个 回滚段。以下示例就展示了经过正确设置的代码: commit; set transaction use rollback segment rbs1; update big_table set column_1 = column_1 * 0.234; commit; 可能很少有人知道 Oracle 在使用游标时也使用回滚段,即使在游标的循环中没有用到 DML 语句。回 滚段用作正在执行的游标循环的一种工作区。这样,当回滚段的空间不足以读取游标时,就可能造成游标 循环失败。这种失败不会立即显现出来——它将在游标循环执行了大量迭代操作后才暴露出来。由于返回 的错误信息和单个的 DML 语句失败时返回的信息是一样的,所以很多开发人员都被愚弄了,他们一直在思 考代码中究竟出现了什么错误。他们虽然对在游标循环内部控制合适的事务管理空间 做了许多勇敢的尝 试,但仍徒劳无获。要想成功地打开一个大型游标,就必须在打开游标前设置一个大型的回滚段,如下程 序清单所示。 commit; set transaction use rollback segment rbs_big; for C1_Rec in C1 loop -- your processing logic goes here ... 209 end loop; 如果在游标循环内将处理大量的数据,则在游标循环内部也要用该代码设置回滚段。这样就防止了 DML 语句使用正被使用的相同的回滚段,确保可以读取大型游标。 技巧: 如果不使用自动撤消管理(参阅第 3 章以获取更多信息),那么在打开一个大型游标时, 必须指定一个空间足够大的回滚段。 使用动态事务管理来处理海量数据使用动态事务管理来处理海量数据使用动态事务管理来处理海量数据使用动态事务管理来处理海量数据 当为处理海量数据的过程编写代码时,请记住要把回滚段的空间考虑进去。对于处理海量数据的程序 而言,回滚段是弱链接。如果一个过程在结尾处提交一条单一的 COMMIT 语句,这和它处理上百万条数据时 的行为是不一样的。以下观点是有争议的:只要提供空间足够大的回滚段,单一事务也可以用于处理海量 数据。这个观点在逻辑上有两个缺点:(1)将 GB 字节的宝贵的磁盘空间用作回滚空间几乎是不可能的;(2) 如果有一个硬件或软件错误出现,那么整个数据集将需重新处理。因此,对于处理海量数据而言,动态事 务管理是其所需要的技术;它可以有效地使用磁盘空间(专用于回滚段),并在出现硬件或软件错误时,提 供自动恢复功能。 动态事务管理是一个由 3 个部分组成的编程技术:为游标和 DML 语句设置事务,执行间歇性的数据库 COMMITS 操作,使用一张表的一列作为处理标志,以说明哪些记录已经经过处理。请考虑以下数据库过程。 declare counter number; cursor C1 is select rowid,column_1,column_2,column_3 from big_table where process_time is NULL; begin Counter := 0; commit; set transaction use rollback segment rbs_big; for C1_Rec in C1 loop -- Commit every 1000 records processed. if (Counter = 0) or (Counter >= 1000) then commit; set transaction use rollback segment rbs_medium; Counter := 0; else Counter := Counter + 1; end if; -- Processing logic... update big_table 210 set process_time = sysdate where rowid = C1_Rec.rowid; end loop; commit; end; / SET TRANSACTION 语句确保了一个空间合适的回滚段,用于游标的读取和 DML 语句。每处理 1000 条记 录进行一次数据库提交操作的 COMMIT 操作提供了两个功能:防止 DML 语句超过回滚段的容量,把需要处理 的记录分成离散的单元,这样即使出现硬件或软件的错误,您也不会丢失已经完成的工作结果。最后, process_time 列作为处理标记,允许程序来识别记录是否已经被处理过。 技巧: 为事务处理指定正确的回滚段空间。限制 COMMIT 之间操作数据的数量是避免出现回滚段 错误的关键。 10.2110.2110.2110.21 使用数据库的临时表来提高性能使用数据库的临时表来提高性能使用数据库的临时表来提高性能使用数据库的临时表来提高性能 PL/SQL 表对于某些特定情况特别有用,尤其是牵涉到重复迭代的使用和相关数据较小的情况;但如果 使用不当,内存的使用(每个会话)将会增加。当需要一个临时存储空间在短时间内存储大量的记录时,创 建、索引和查询一张数据库临时表的方法是可行和十分有用的选择。我遇到很多开发人员在初步了解和掌 握 PL/SQL 表后,就放弃了常用的数据库临时表的方法。请记住,PL/SQL 表并不是万能的方法。 Oracle 通过往临时表中写入撤消数据来帮助实现事务恢复、回滚到保存点、保持读操作的一致性和回 收空间等功能。这样,临时表中的事务有可能会生成 redo 信息,因为我们需要记录回滚段或者撤消段所做 的修改。产生的 redo 信息比永久表上的 DML 操作产生的 redo 信息要少。 10.2210.2210.2210.22 集成用户跟踪机制以定位执行位置集成用户跟踪机制以定位执行位置集成用户跟踪机制以定位执行位置集成用户跟踪机制以定位执行位置 从开发的角度来看,基于 Oracle 开发出来的应用程序变的越来越复杂,且所有可以使用和正在使用 的产品也越来越多,以及 PL/SQL 程序单元源代码定位的灵活性也在不断提升,当用户对性能表示不满或者 通知 DBA 他们受不了时,数据库管理人员知道用户在该时间点上执行了什么操作,以及在源代码中定位实 际的处理逻辑是非常重要的。 Oracle 提供的 DBMS_APPLICATION_INFO 包提供了一种机制,用于定位正在执行的处理逻辑。开发人员 可以通过调用该包来记录正在执行的操作的当前位置 。 标 准 的 方 法 就 是 调 用 DBMS_APPLICATION_INFO.SET_MODULE 过程来确认用户正在运行的产品(例如,可以通过 Oracle Forms 的启 动触发器来实现,记录表中的实际用户和表的名称,并当表退出运行时,再将这些值置为 NULL)。在过程 或函数中,可以调用 DBMS_APPLICATION _INFO.SET_MODULE 过程来识别过程的名称,以及运行程序的用户“正式”地进入程序的时间——精确到秒。 一旦退出过程或函数,就可以调用同样的过程把这些值置为 NULL。 10.2310.2310.2310.23 限制动态限制动态限制动态限制动态 SQLSQLSQLSQL 的使用的使用的使用的使用 211 Oracle 提供了一个由 Oracle 开发的 DBMS_SQL 包和动态 SQL 命令 EXECUTE IMMEDIATE,可以创建动态 的 SQL 和 PL/SQL 命令。这些都是非常强有力的特性,但如果使用不当,也十分危险。当设计和开发 Oracle 应用程序时,最难做的决定就是在哪里嵌入语句来提供动态性能和灵活性。从功能的角度来看,开发动态 的和灵活的应用程序是非常有帮助的。然而,应用程序的动态性和灵活性越高,潜在的性能问题就越多。 如果一个完全正确且功能强大的应用程序的操作却无法达到用户可接受的水平时,我们认为它是失 败的。 如果在工作中必须等待的话,用户将会拒绝这样的应用程序。我并不提倡消除应用程序的动态性和灵活性, 但是必须权衡利弊。只有需要的时候才考虑应用程 序的灵活性,不要把每一个应用程序模块都设计成具有 适应将来的灵活性,如果这样的话商业规则就会改变。只有当您确认需要灵活性,并且肯定性能不会受到 负面 影响的情况下,才应当在应用程序中考虑灵活性。 DBMS_SQL 包和 EXECUTE IMMEDIATE 为 PL/SQL 程序单元提供了一些动态和灵活的手段。当需要的时候 可以使用这个包,但切勿滥用,除非您想尝试一下失败的感觉。 技巧: 如果您在一个 PL/SQL 程序单元中整合了 DBMS_SQL 包,将其用于为一个应用程序产品创 建动态的 SQL 语句,则必须牢记:对生成的 SQL 语句的优化工作将变得很困难。 技巧: 使用绑定变量和动态 SQL 可以使资源竞争最小化,性能最大化。 10.2410.2410.2410.24 使用管道表函数来建立复杂结果集使用管道表函数来建立复杂结果集使用管道表函数来建立复杂结果集使用管道表函数来建立复杂结果集 有时您会碰到 DML 选择语句无法提供所需信息的情况。通常,这种情况发生在数据不驻留在数据库表 中或者将表数据转换为有用形式超出 SQL 和内联函数能力的时候。以前的解决方案是创建预处理器,这样 在调用它时就能将数据收集到某种类型的中间表中,这可能是全局的临时表,用于使用简单的 DML 选择的 子查询提取。然而,管道表函数不仅能结合这两个步骤,而且还能消除在中间表中保存数据的开销。 管道表函数是指能够产生行集合(例如嵌套表)的函数,它们能像物理数据库表一样进行查询或者能赋 值给 PL/SQL 集合变量。可以使用表函数代替 FROM 子句中数据库表的名称或者代替查询 SELECT 列表中的列 名。 为了说明这个过程,首先假设这个简单的表是模型中的唯一表。 create table states ( state_code varchar2(2) not null, state_name varchar2(100) not null, constraint states_pk primary key (state_code), constraint states_uk1 unique (state_name), constraint states_chk1 check (state_code = upper(state_code)) ); 需要解决的问题是:需要一种方法来创建 SQL 脚本用来复制模式中所有的自定义约束,它们的需求如 212 下: ● 脚本需要在应用程序的服务器上,而不是在数据库服务器上创建,使用 Java Server Pages(JSP) 方法。 ● 脚本需要保证考虑约束之间的依赖关系。 ● 脚本需要在复制约束时使得被禁用的约束保持禁用状态。 ● 当复制启用的检查和外键约束时,脚本应该保护现有数据的再次验证。 现在,有可能通过使用多表连接、 多个 UNION 子句和大量的 DECODE 语句的大型 SQL 查询来解决这个 问题,但是所得到的最终结果将难以维护。所以需要更好的解决方案,其中涉及管道表函数,将来您会发 现,在一些非常基本的 PL/SQL 功能中可以找到这些管道表函数。通过使用管道表函数,可以简化 JSP 从数 据库中获得所需信息的操作……只要查询简单的 DML SELECT 语句即可。管道表函数将以符合需求规则的形 式把 DDL 命令返回给 JSP。从 JSP 的观点来看,管道表函数就像一张表,这样它就能够简单地进行查询并 迭代返回的结果集,将命令写入文件中。 通过关键字 PIPELINED 可以声明管道表函数,PIPELINED 关键字说明函数迭代返回行。管道表函数的 返回类型必须是支持的集合类型,例如嵌套表或 varray。这个集合类型可以在模式级别或在包中进行声明。 在函数内部,返回的是集合类型的个别元素。以下是问题解决方案的包的头信息。注意,get_constraint_ddl 函数返回集合类型并使用 PIPELINED 关键字。 CREATE OR REPLACE PACKAGE ddl_extract_pkg is --Record and array types to support pipelined tabled functions. type sg_constraint_ddl_rec is record (ddl_name varchar2(100), ddl_text varchar2(1000)); type sg_constraint_ddl_array is table of sg_constraint_ddl_rec; --Public routines. FUNCTION get_constraint_ddl return sg_constraint_ddl_array pipelined; END ddl_extract_pkg; / 在 PL/SQL 中,PIPE ROW 语句使得管道表函数返回一行并继续处理。该语句能使 PL/SQL 表函数在产生 一个数据行时就返回该数据行。PIPE ROW 语句只应该用于管道表函数的主体部分中,如果用在其他地方将 发生错误。对于不返回任何数据行的管道表函数,可以省略 PIPE ROW 语句。管道表函数可以有不返回任何 值的 RETURN 语句。RETURN 语句将控制权还给客户并保证在下次获取时返回 NO_DATA_FOUND 异常。 在查看包的主体部分前,将简要地讨论其中的一些关键信息: ● 首先,为了避免从不同的字典表中重构 DDL 的冗长集合,需要使用 DBMS _METADATA 包。这个包 完成从字典中建立 DDL 的操作,它需要一些初始的基于 PL/SQL 的配置调用。通过使用 DBMS_METADATA 包, 可以在需要时捕获所有重构 DDL 的细微差别(例如存储参数、表空间和段属性)。 ● 当从 DBMS_METADATA 中获得基本的重构的 DDL 后,需要使用字符串命令处理 DDL 以完成特定的功 能。 ● 管道函数的内部处理必须考虑约束的依赖顺序。函数返回记录的顺序在调用 DML SELECT 语句中 定义。 CREATE OR REPLACE PACKAGE BODY ddl_extract_pkg is 213 --scrub_raw_ddl function. -- --Description: This function performs basic scrubbing routines on a -- DDL command returned by dbms_metadata.get_ddl. -- --Syntax: scrub_raw_ddl(p_status_c, p_cons_type_c, p_ddl_c); -- --Where: p_status_c = The current status (Enabled/Disabled). -- p_cons_type_c = The constraint type (P, U, C, R). -- p_ddl_c = The constraint reconstruction DDL. -- FUNCTION scrub_raw_ddl (p_status_c IN varchar2, p_cons_type_c IN varchar2, p_ddl_c IN varchar2) return varchar2 is v_new_ddl_c varchar2(1000); BEGIN --Capture the passed DDL. v_new_ddl_c := p_ddl_c; --Trim off any carriage returns. v_new_ddl_c := replace(v_new_ddl_c, chr(10), null); --Trim off any whitespace. v_new_ddl_c := trim(v_new_ddl_c); --For Check and Relational constraints, if the constraint is --currently disabled then we will leave it that way. --Otherwise, we will enable it but without the re-validation of existing data. if ( p_cons_type_c in ('C', 'R') ) then if ( ( p_status_c = 'ENABLED' ) ) then if ( instr(v_new_ddl_c, ' NOVALIDATE') = 0 ) then v_new_ddl_c := v_new_ddl_c||' NOVALIDATE'; end if; end if; end if; --Properly terminate the command. v_new_ddl_c := v_new_ddl_c||';'; --Return. return(v_new_ddl_c); END scrub_raw_ddl; --get_constraint_ddl function. -- --Description: Pipelined table function returning proper DDL commands to -- reconstruct the custom constraints (PK, UK, CHK, FK) for all -- tables within the current schema. -- FUNCTION get_constraint_ddl return sg_constraint_ddl_array pipelined is 214 v_mdc_i integer; v_raw_sql_c varchar2(1000); --The function returns a collection of records of type X. --So, in the code we will return single records of type X. v_out_record sg_constraint_ddl_rec; --Cursor to control the extraction order to prevent dependency errors. --Check constraints, then PK, then UK, then FK. --We do this to prevent dependencies errors. cursor v_extract_order_cur is select 1 as a_cons_order, 'C' as a_cons_type, 'CONSTRAINT' as a_cons_group from dual union all select 2, 'P', 'CONSTRAINT' from dual union all select 3, 'U', 'CONSTRAINT' from dual union all select 4, 'R', 'REF_CONSTRAINT' from dual order by 1; --Cursor to access the custom constraints from the data dictionary. cursor v_constraints_cur (p_type_c IN varchar2) is select owner, table_name, constraint_name, constraint_type, status, validated from user_constraints where table_name = 'STATES' and constraint_type = p_type_c and generated <> 'GENERATED NAME'; BEGIN --Configure the dbms_metadata package. v_mdc_i := dbms_metadata.session_transform; dbms_metadata.set_transform_param(v_mdc_i, 'PRETTY', false); dbms_metadata.set_transform_param(v_mdc_i, 'SEGMENT_ATTRIBUTES', false); dbms_metadata.set_transform_param(v_mdc_i, 'STORAGE', false); dbms_metadata.set_transform_param(v_mdc_i, 'TABLESPACE', false); dbms_metadata.set_transform_param(v_mdc_i, 'CONSTRAINTS_AS_ALTER', true); dbms_metadata.set_transform_param(v_mdc_i, 'CONSTRAINTS', true); dbms_metadata.set_transform_param(v_mdc_i, 'REF_CONSTRAINTS', true); dbms_metadata.set_transform_param(v_mdc_i, 'SQLTERMINATOR', false); --Open the cursor that controls the extraction order... 215 for extract_order_rec in v_extract_order_cur loop --Open the cursor to access the constraints of the --current type (PK, UK, etc). for constraints_rec in v_constraints_cur(extract_order_rec.a_cons_type) loop --Initialize the next pipeline record to be returned. v_out_record.ddl_name := constraints_rec.constraint_name; v_out_record.ddl_text := null; --Get the raw DDL for the current constraint. v_raw_sql_c := dbms_metadata.get_ddl(extract_order_rec.a_cons_group, constraints_rec.constraint_name, constraints_rec.owner); --Scrub the raw DDL. --The cleaned DDL will be placed into the record --being returned to the pipeline. v_out_record.ddl_text := scrub_raw_ddl(constraints_rec.status, extract_order_rec.a_cons_type, v_raw_sql_c); --Return the constructed command to the pipeline. pipe row(v_out_record); end loop; end loop; return; END get_constraint_ddl; END ddl_extract_pkg; / 在安装完这个包后,执行这个包和查询 DML SELECT 语句一样简单。当从 SQL 中访问 PIPELINED 表函 数时,需要记住一些细微的差别: ● 必须使用 SQL TABLE 集合表达式告诉 Oracle,从管道表函数返回的集合应该作为一张表用于查 询和 DML 操作。 ● 需要从集合中访问的列必须显式地说明,不能使用列通配符(*0)。 select x.ddl_name, x.ddl_text from table(ddl_extract_pkg.get_constraint_ddl) x order by 1; DDL_NAME DDL_TEXT ------------ -------------------------------------------------- 216 STATES_CHK1 ALTER TABLE "TRS3_PROC"."STATES" ADD CONSTRAINT "S TATES_CHK1" CHECK (state_code = upper(state_code)) ENABLE NOVALIDATE; STATES_PK ALTER TABLE "TRS3_PROC"."STATES" ADD CONSTRAINT "S TATES_PK" PRIMARY KEY ("STATE_CODE") ENABLE; STATES_UK1 ALTER TABLE "TRS3_PROC"."STATES" ADD CONSTRAINT "S TATES_UK1" UNIQUE ("STATE_NAME") ENABLE; 提示: 通过管道表函数可以避免中间表,以建立复杂的结果集。 提示: 使用 DBMS_METADATA 从数据字典中创建重构的 DDL。 10.2510.2510.2510.25 别管调试命令别管调试命令别管调试命令别管调试命令 在开发 PL/SQL 模 块期间,总是不能避免地要使用调式命令。比调试命令本身更重要的是由开发人员 选择调试点的位置以最大化调试的好处。对于复杂的算法来说,有效的调试是一门 技术,只有那些对代码 非常熟悉的人员才能准确地定位调试语句以获得最大的收益。但是,在这些代码用于实际应用前,这些调 试语句必须被删除或禁用(注释),因为 PL/SQL 缺少许多程序语言中所带的条件编译,直到现在亦是如此! Oracle 10g 使得开发人员能够在原来的位置保留这些调试命令,从而便于在产生问题时能重新恢复这些命 令。 有了条件编译,就能进入只有在编译期间才会执行的 if-then 控制结构,它的目的是使用 if-then 控 制结构来控制程序编译时所包含的文本语句(从 THEN 或 ELSE 子句中)。条件编译控制结构通过标准 if-then 块的关键字(IF, THEN, ELSE, ELSEIF, END)前的条件编译触发器字符($)来进行标识(除了 END 代替 END IF 作为块的结束符的情况以外)。Oracle PL/SQL 编译器对源代码进行初始扫描以查找条件编译触发器字符$。 如果能找到任何有效的触发器字符,编译器就将计算编译条件以确定在实际编译的代码中包含哪些代码文 本。以下是条件编译块的基本结构: $if test_expression $then text_to_include [ $elsif test_expression $then text_to_include ] [ $else text_to_include ] $end 217 条件编译使用选择指令(selection directive)或查询指令(inquiry directive)来确定在编译程序中 包含哪些文本。选择指令允许在编译期间计算静态表达式。下面的代码说明了一个最简单的使用选择指令 的条件编译命令: $if static_boolean_expression $then text_to_include; $end 在编译时,如果 static_boolean _expression 为 TRUE,则会在编译程序中包含 text _to_include; 否则将跳过 text_to_include。为了说明这点,下面将介绍一个包的声明,它将被专门用于存储调试目的 的条件编译常量。 CREATE OR REPLACE PACKAGE debug_pkg IS debug constant boolean := true; END debug_pkg; / 下面将为一些虚构的商业应用程序创建包的声明。 CREATE OR REPLACE PACKAGE worker_pkg as PROCEDURE run_prc; END worker_pkg; / 在包的正文部分包含引用调试包中静态常量的条件编译命令。 CREATE OR REPLACE PACKAGE BODY worker_pkg as PROCEDURE run_prc is BEGIN dbms_output.put_line('Processing started.'); $if debug_pkg.debug $then dbms_output.put_line('Debugging is on.'); $end dbms_output.put_line('Processing completed.'); END; END worker_pkg; / 由于在编译这个包的正文时静态常量被设置为 TRUE,则会在编译程序中包含额外的 DBMS_OUTPUT 命 令,通过执行 run_prc 过程可以验证这点: set serverout on; exec worker_pkg.run_prc; Processing started. Debugging is on. Processing completed. PL/SQL procedure successfully completed 修改 debug_pkg 包将导致重新编译所有的依赖对象,而且当重新编译时,条件编译控制常量的当前值 将被用于确定调试语句是否包含在重新编译的代码中。 218 CREATE OR REPLACE PACKAGE debug_pkg IS debug constant boolean := false; END debug_pkg; / 由于这次静态常量设置为 FALSE,则当 worker_pkg 包自动地重新编译时不会包含额外的 DBMS_OUTPUT 命令,再次执行 run_prc 过程可以验证这点。 set serverout on; exec worker_pkg.run_prc; Processing started. Processing completed. PL/SQL procedure successfully completed 暂停一会,执行典型的查询数据字典的活动以返回存储包的源代码。 select text from user_source where name = 'WORKER_PKG' and type = 'PACKAGE BODY' order by line; TEXT ------------------------------------------------------------------------ PACKAGE BODY worker_pkg as PROCEDURE run_prc is BEGIN dbms_output.put_line('Processing started.'); $if debug_pkg.debug $then dbms_output.put_line('Debugging is on.'); $end dbms_output.put_line('Processing completed.'); END; END worker_pkg; 8 rows selected 可以看出,不能再依赖于_source(例如 USER_SOURCE, DBA_SOURCE)字典表来显示数据库中正在执行的 代码。_source 字典表毕竟就是源代码。为了确定已经编译过的准确代码,可以考虑条件编译,Oracle 提 供了 DBMS_PREPROCESSOR 包。 set serverout on BEGIN dbms_preprocessor.print_post_processed_source('PACKAGE BODY', USER, 'WORKER_PKG'); END; / PACKAGE BODY worker_pkg as PROCEDURE run_prc is 219 BEGIN dbms_output.put_line('Processing started.'); dbms_output.put_line('Processing completed.'); END; END worker_pkg; PL/SQL procedure successfully completed 现在回到调试包的讨论中。为了在调式过程上有不同粒度,这里引入一些特定过程的控制常量。 CREATE OR REPLACE PACKAGE debug_pkg IS debug_run_prc constant boolean := true; debug_xxx_prc constant boolean := false; debug_yyy_prc constant boolean := false; debug_zzz_prc constant boolean := false; END debug_pkg; / 现在更新 worker 包,使用新的常量。 CREATE OR REPLACE PACKAGE BODY worker_pkg as PROCEDURE run_prc is BEGIN dbms_output.put_line('Processing started.'); $if debug_pkg.debug_run_prc $then dbms_output.put_line('Debugging is on.'); $end dbms_output.put_line('Processing completed.'); END; END worker_pkg; / 确认执行结果和预期一样。 set serverout on; exec worker_pkg.run_prc; Processing started. Debugging is on. Processing completed. PL/SQL procedure successfully completed 记住,包含静态常量的包和用于条件编译而引用静态常量的包之间存在物理依赖。这样,如果利用 debug_pkg 包修改单个常量的设置,则无论是否在依赖的包中引用修改的常量,仍会导致对所有依赖于这 个包的过程和函数进行级联重编译。对于存在大量存储代码的应用程序来说不应该发生这样的行为。在这 种情况下,可以将静态常量分散于多个包中,或者通过 Oracle 10gR2 使用另一种控制条件编译的方法,即 查询指令。 首先进行清理工作。 220 drop package debug_pkg; Package dropped 条件编译查询指令能够通过以下预定义的指令名称将测试条件依赖于编译环境。 ● 任何 Oracle PL/SQL 的编译初始参数,例如 PLSQL_CCFLAGS、PLSQL _CODE _TYPE 或 PLSQL_WARNING。 ● 来自 PLSQL_LINE 的模块行号。 ● 来自 PLSQL_UNIT 的当前源单位名称,注意这个指令名称将为异步模块返回 NULL。 ● 由 PLSQL_CCFLAGS 引入的自定义名称-值对。 本例将通过 PLSQL_CCFLAGS 初始参数构建自定义的名称-值对。 alter session set PLSQL_CCFLAGS = 'MyDebugMode:TRUE'; 接下来修改测试过程以使用查询指令。 CREATE OR REPLACE PACKAGE BODY worker_pkg as PROCEDURE run_prc is BEGIN dbms_output.put_line('Processing started.'); $if $$MyDebugMode $then dbms_output.put_line('Debugging is on.'); $end dbms_output.put_line('Processing completed.'); END; END worker_pkg; / 快速测试这个过程来说明预期的结果。 set serverout on; exec worker_pkg.run_prc; Processing started. Debugging is on. Processing completed. PL/SQL procedure successfully completed. 同使用依赖于静态常量的选择指令不同的是,修改自定义查询指令的值不会导致自动重新编译这个包。 alter session set PLSQL_CCFLAGS = 'MyDebugMode:FALSE'; Session altered. set serverout on; exec worker_pkg.run_prc; Processing started. Debugging is on. Processing completed. PL/SQL procedure successfully completed. 221 除非发生其他情况,才能导致这个包重新编译在自定义查询指令中所做的修改。 alter package worker_pkg compile; Package altered. set serverout on; exec worker_pkg.run_prc; Processing started. Processing completed. PL/SQL procedure successfully completed. 如果要在不修改会话的情况下调整特定包的行为,可以在强制重新编译模块期间指定 PL/SQL 的永久 编译参数。 alter package worker_pkg compile PLSQL_CCFLAGS = 'MyDebugMode:TRUE' reuse settings; Package altered. set serverout on; exec worker_pkg.run_prc; Processing started. Debugging is on. Processing completed. PL/SQL procedure successfully completed. REUSE SETTINGS 子句可以绕过那些删除或重载(从会话中)所有永久编译条件的常规编译器行为。因 此,在强制重编译期间将会更新的唯一编译器参数就是通过 ALTER 命令声明的部分。 提示: 使用条件编译限制调试命令。 提示: 使用在 DBMS_DB_VERSION 中定义的静态常量作为选择指令来控制条件编译。 DBMS_DB_VERSION 包指定了 Oracle 的版本号和其他信息,这些信息对于基于 Oracle 版 本的简单条件编译选择很有用。 10.2610.2610.2610.26 为初学者提供的例子为初学者提供的例子为初学者提供的例子为初学者提供的例子 因为您可能是 PL/SQL 的初学者,所以本小节也提供了一些有关 PL/SQL 代码、过程、函数、包和触发 器的例子。对这些对象的样式以及它们的区别有一定的感性认识是非常重要的,尤其是如果您以前从来没 有接触过它们。本节被有意放在了结尾处,仅仅给您充当一个短小的参考,让您对每一段代码有个感性认 识。我的目的不是要教会您如何写 PL/SQL 代码(如需详细了解,可以参考 Joe Trezzo 编写的 PL/SQL Tips and Techniques 一书)。 无论过程还是函数,都可以接受参数并且可以从 PL/SQL 中调用。然而,典型的过程是执行一个动作。 在过程中使用的参数可以是 in(put),out(put),或者 in(put)/out(put),而函数通常用于计算一个数值, 222 参数只能是 in(put)。事实上,您甚至不能指定参数的 “方向”。函数只允许一个返回值。函数是“可选” 的,因此,您可以创建自定义函数来返回信息。在 Oracle 7.2 版本里,开发人员可以建立自定义函数,以 代替标准的 SQL 型函数来执行处理工作。 在创建索引时也可以使用函数。这样,索引键将会按您的查询方式进行存储。 10.26.110.26.110.26.110.26.1 创建创建创建创建 PL/SQLPL/SQLPL/SQLPL/SQL 代码代码代码代码 以下程序清单说明了如何创建 PL/SQL 代码。 declare acct_balance NUMBER(11,2); acct CONSTANT NUMBER(4) := 3; debit_amt CONSTANT NUMBER(5,2) := 500.00; begin select bal into acct_balance from accounts where account_id = acct for update of bal; if acct_balance >= debit_amt THEN update accounts set bal = bal - debit_amt where account_id = acct; else insert into temp values (acct, acct_balance, 'Insufficient funds'); -- insert account, current balance, and message end if; commit; end; / 10.26.210.26.210.26.210.26.2 创建过程创建过程创建过程创建过程 下面的示例说明了如何创建一个过程。 create or replace procedure get_cust (in_cust_no in char, out_cust_name out char, out_cust_addr1 out char, out_cust_addr2 out char, out_cust_city out char, out_cust_st out char, out_cust_zip out char, out_cust_poc out char) IS begin select name, addr1, addr2, city, st, zip, poc into out_cust_name, out_cust_addr1, out_cust_addr2, out_cust_city, out_cust_st, out_cust_zip, 223 out_cust_poc from customer cust, address addr where cust.cust_no = addr.cust_no and addr.primary_flag = 'Y' and cust.cust_no = in_cust_no; end get_cust; / 10.26.310.26.310.26.310.26.3 执行执行执行执行 PL/SQLPL/SQLPL/SQLPL/SQL 过程过程过程过程 以下示例说明了如何在 PL/SQL 的代码块中执行 PL/SQL 过程。 get_cust (12345, name, addr1, addr2, city, st, zip, poc); 10.26.410.26.410.26.410.26.4 创建函数创建函数创建函数创建函数 下面的示例说明了如何创建一个函数。 create or replace function get_cust_name (in_cust_no number) return char IS out_cust_name cust.cust_last_name%type; begin select cust_last_name into out_cust_name from cust where customer_id = in_cust_no; return out_cust_name; end get_cust_name; 10.26.510.26.510.26.510.26.5 在在在在 SQLSQLSQLSQL 中执行中执行中执行中执行 GET_CUST_NAMEGET_CUST_NAMEGET_CUST_NAMEGET_CUST_NAME 函数函数函数函数 以下示例说明了如何执行 GET_CUST_NAME 函数。 select get_cust_name(12345) from dual; 10.10.10.10.26.626.626.626.6 创建数据包创建数据包创建数据包创建数据包 以下示例说明了如何创建数据包。 Create or replace package emp_actions IS -- package specification procedure hire_employee (empno NUMBER, ename CHAR, ...); procedure retired_employee (emp_id NUMBER); end emp_actions; 224 / Create or replace package body emp_actions IS -- package body procedure hire_employee (empno NUMBER, ename CHAR, ...) is begin insert into emp VALUES (empno, ename, ...); end hire_employee; procedure fire_employee (emp_id NUMBER) IS begin delete from emp WHERE empno = emp_id; end fire_employee; end emp_actions; / 10.26.710.26.710.26.710.26.7 在数据库触发器中使用在数据库触发器中使用在数据库触发器中使用在数据库触发器中使用 PL/SQLPL/SQLPL/SQLPL/SQL 以下示例说明了如何在数据库触发器中使用 PL/SQL。 create trigger audit_sal after update of sal ON emp for each row begin insert into emp_audit VALUES( ...) end; 10.2710.2710.2710.27 技巧回顾技巧回顾技巧回顾技巧回顾 ● 为实时监控使用 DBMS_APPLICATION_INFO。 ● 在 RAC 环境中为实时监控使用自定义包代替 DBMS_APPLICATION_INFO。 ● 在数据库中为长时间运行的 PL/SQL 程序单元建立记录执行时间信息的表,将为您的系统整合一 种具有预警性的性能监测机制。可以在任何时间查看这张表,以判断随着时间的推移,性能是否有下降现 象。 ● 根据活动会话的数量确定的系统负载对程序执行的性能有很大的影响;因此,修改数据库中表的 记录方法,为所有的活动会话增加一列,将是很有帮助的。可以通过往程序单元中增加额外的查询来填充 该列,而该程序单元是正在执行从 V$SESSION 视图中检索统计数任务的程序。 ● 当一个 PL/SQL 程序单元涉及到大量的循环或递归时,就应当关注于减少每一次迭代的单位时间。 这样的效果很明显,并且也很容易通过数学方法判断出总的改进的性能。循环或者递归也应当被仔细检查, 并通过重构来减少迭代的次数,但要保留函数的功能一致性。由于 PL/SQL 和 SQL 本身具有极大灵活性,可 以通过使用不同的方式得到同样的结果。如果 PL/SQL 程序单元的运行并不十分理想,那么有时您必须用其 他方式重写逻辑。 ● 当需要在 PL/SQL 程序单元中选择一条记录,并且该记录需要在同一个 PL/SQL 程序单元中进行计 算时,使用 ROWID 变量将提高性能。需要注意的是,这不能用于索引组织表(IOT)。 ● 确保所有比较条件中的比较值具有相同的数据类型。另外,确保同属于一类数据类型的比 较值具有相同的子数据类型是很有帮助的。因此,在最后的示例中,IF 语句中的比较量和 1、2、 225 3 等进行比较,也就是将 NUMBER 类型与 PLS_INTEGER 类型进行比较。这仍然将造成 Oracle 类型 转换的开销。要消除这种开销,应当将 1、2、3 等数改成 1.0,2.0,3.0……当本章中的示例做 了这种修改,执行时间将会降低。 ● 确保 PL/SQL 的 IF 条件语句以最经常满足的条件为排列顺序,而不是基于数字或者字母的顺序。 ● 如果将一个带有精确值的数字赋值给一个 PLS_INTEGER 变量,那么这个值将取整为一个整数,就 像对这个数字运行了 ROUND 取整函数一样。 ● 应当限制在迭代或递归循环中调用 SYSDATE,因为这个变量将产生额外的开销。在声明时将一个 PL/SQL DATE 变量赋值给 SYSDATE,然后引用这个 PL/SQL 变量,以减少开销。 ● 在 Oracle 9i 中,当数据库运行时,可以修改 SHARED_POOL_SIZE 的值,只要设置的值不超过 SGA_MAX_SIZE。请参阅第 4 章了解如何设置初始化参数。 ● 使用 DBMS_SHARED_POOL.KEEP 过程来固定 PL/SQL 对象。 ● 查询 V$DB_OBJECT_CACHE 视图来发现没有被固定的对象,以及由于太大而有可能引起问题的对 象。 ● 使用 DBMS_SHARED_POOL.SIZES 包过程来查找一个对象的具体信息。 ● 通过查询 x$ksmsp 可以找出在共享池中占用大段空间的 PL/SQL 代码块。它们是在数据库启动时 需要固定的候选对象。 ● 通过查询 DBA_OBJECTS(针对系统范围内的对象)或者 USER_OBJECT(仅针对您的方案)来查询触发 器的状态,避免由用户造成的错误和重新加载。 ● 通过查询 DBA_TRIGGERS(针对系统范围内的对象)或者 USER_TRIGGERS(针对您的方案)来查询触 发器的状态,避免由于禁用的触发器而产生的错误。禁用的触发器将会给应用程序带来致命的危害:它们 不会失败,它们根本就不执行。 ● 将参考表加载到 PL/SQL 表,以加快查询速度。 ● 要查找 PL/SQL 对象的源代码,则 需查询 USER_SOURCE、DBA_SOURCE、USER_TRIGGERS 和 DBA_TRIGGERS 视图。要查找查询的依赖关系,则需查询 USER_DEPENDENCIES 和 DBA_DEPENDENCIES 视图。 ● 一个 DATE 数据类型总是存储完整的临时值,精确到秒。不可能将一个日期变量值直接插入到一 个数据类型为 DATE 的 PL/SQL 变量或者数据库列上。 ● Oracle 的 DATE 包含了日期和时间信息。当查询匹配数据时应当避免限制索引。其办法就是 不修改 WHERE 子句数据列一侧的内容。在非数据列一侧执行所有的修改。 ● 使用 PL/SQL 来显示 PL/SQL 的开始和结束时间。 ● 通常情况下,PL/SQL 应当存储在服务器端。 ● 在 PL/SQL 内为大型游标指定大小合适的回滚段。 ● 使用管道表函数建立复杂的结果集。 ● 如果在一个 PL/SQL 程序单元中整合了 DBMS_SQL 包,将其用于为一个生产应用程序动态创 建 SQL 语句,则必须牢记:对生成的 SQL 语句的优化工作将变得很困难。 ● 使用绑定变量和动态 SQL 可以使资源竞争最小化,性能最大化。 ● 使用 DBMS_METADATA 从数据字典中创建重构的 DDL。 ● 使用条件编译限制调试命令。 ● 使用在 DBMS_DB_VERSION 中定义的静态常量作为选择指令来控制条件编译。 10.2810.2810.2810.28 参考文档参考文档参考文档参考文档 Joe Trezzo, PL/SQL Tips and Techniques (Oracle Press, 1999) Joe Trezzo, Procedures, Functions, Packages, and Triggers TUSC, 1999 SQL Language Reference Manual (Oracle Corporation) Application Developer’s Guide (Oracle Corporation) 226 Frank Naude’s underground Oracle Web page (www.oraclefaq.com) Bradley Brown, "OOPs-Objected Oriented PL/SQL," Select Magazine, April 1996 Scott Urman and Tim Smith, Oracle PL/SQL Programming (Oracle Press, 1996) Kevin Loney and Bob Bryla, Oracle Database 10g DBA Handbook (McGraw-Hill, 2005) Steven Feuerstein, Oracle PL/SQL Programming, 4/e (O’Reilly & Associates, 2005) Steven Feuerstein, "Using SQL to Examine Stored Code," Integrator, February1996 Oracle Database PL/SQL User’s Guide and Reference 10 g Release 2 (Oracle Corporation) 第第第第 11111111 章章章章 调整调整调整调整 RACRACRACRAC 和使用并行特性和使用并行特性和使用并行特性和使用并行特性 Oracle 的并行服务器首先由 Oracle 6.1(beta)和 Oracle 6.2(有限客户版本产品)引入,但只在 VAX/VMS 上广泛使用。直到出现 Oracle 9i,Oracle 才真正有了集群产品(实时应用集群,Real Application Clustering,RAC),当时他们几乎全部(据说是 95%)重写产品代码。在 Oracle 10g 中,RAC 不但已经变得 成熟,而且已经成为了网格计算(整个使用 Oracle RAC 或集群体系结构的服务器网格)的奠基石。 除了使用多个服务器来增强可用性和提高性能以外,Oracle 还改进了 Oracle 7.1 中首先引入的并行 查询技术。并行查询选项(Parallel Query Option,PQO)现在称为并行执行选项(Parallel Executions Option,PEO),它使查询操作和 DML 语句可以并行执行,从而显著提升性能。每一个版本的 RDBMS 内核都 对 PEO 进行了增强处理。在 Oracle 10g 中,绝大多数操作都可以并行执行,包括查询(并行 SQL 执行)、DML 和 DDL 操作、分区内并行处理、数据复制和恢复的并行处理及数据加载;多个并行查询服务器进程甚至可 以在同一个分区上执行。 本章主要内容: ● 实时应用集群(RAC)概述和体系结构 ● 调整 RAC 互连 ● 查找 RAC 等待事件 ● 使用企业管理器网格控制(Enterprise Manager Grid Control)调整网格(大范围 RAV 实现方式) ● 并行操作的基本概念 ● 并行 DML、DDL 语句和操作 ● Oracle 9i 的并行 DML 语句和操作 ● 并行处理和分区 ● 操作之间和操作内部的并行处理 ● 使用并行操作生成表和索引的示例 ● 并行 DML 语句和示例 ● 通过 V$视图监控并行操作 ● 在并行操作时使用 EXPLAIN PLAN 和 AUTOTRACE ● 调整并行执行和 Oracle 9i 初始化参数 ● 并行加载 ● 性能比较和监控并行操作 ● 其他并行处理的注意事项 227 11.111.111.111.1 实时应用集群实时应用集群实时应用集群实时应用集群(RAC)(RAC)(RAC)(RAC) 信息系统的高性能和高可用性是企业日常操作的主要需 求。随着最近几十年对存储信息依赖性的不 断增长,需要积累和分析大量数据。因此对高性能的数据库的需求以及保持这种数据库随时联机的认知和 要求也不断增 加。全局操作和电子商务的增长也非常依赖高可用性的存储数据。由于对数据库系统的不均 匀和不可预知加载,许多商业团体迫切需要寻找高性能系统和合适的并行 系统来支持复杂的大型数据库系 统。可伸缩性是另一种重要特性。随着业务的增长,数据积累和数据交互也随之增长。越来越多的用户和 应用程序开始使用数据库系 统。数据库系统应当能够满足数据日益增长的需要,同时又不会降低性能、减 少可伸缩性的范围。Oracle 9i 引入了实时应用集群(RAC)来解决这些问题。本节没有涵盖 RAC 功能的所有 方面,只是着重介绍了 RAC 的一些重要概念及其内部工作方式。本书的这一部分没有专门讨论 RAC。 11.1.111.1.111.1.111.1.1 并行数据库并行数据库并行数据库并行数据库 并行集群数据库是一种复杂的应用程序,它能够并发地从集群中的任何服务器访问同一个数据库(数 据表组、索引和其他对象),同时不会破坏数据的完整性。并行数据库通常包含多个同时访问相同物理存储 器或数据的实例(节点/服务器)。依据存储访问类型,并行系统有两种实现方式:无共享模型或磁盘共享模 型。 在无共享模型(也称为数据分区模型)中,每个系统都有一部分数据库,每个分区只能由所属系统读取 或修改。数据分区让每个系统都能够本地缓存处理器内存中的部分数据库,而不需要跨系统通信来提供数 据访问并发性和一致性控制。IBM 和 Microsoft 的数据库都能够这样操作,以前的也能这样。而 Oracle 采 用磁盘共享模型,这使得它们在网格计算中遥遥领先。 在磁盘共享模型中,集群的所有节点都可以访问包含数据的所有磁盘。磁盘共享体系结构需要合适的 锁定管理技术来控制更新并发控件。集群中的每个节点都能直接访问保存共享数据的所有磁盘。每个节点 都有局部数据库缓冲区缓存。Oracle 的 RAC 数据库就这样运行。 为了突出高可用性和高性能,Oracle 很早就已经提供了 Oracle 并行服务器(Oracle Parallel Server, OPS)。在 Oracle 9i 中,它融入下一代,将 OPS 重新构建为实时应用集群(RAC)。RAC 采用磁盘共享模型, 因此也能访问所有共享磁盘,同时也广泛包含协调节点间资源的机制。磁盘共享技术在过去几年得到了快 速发展,给 RAC 也带来了许多好处。存储区网络(SAN)技术对服务器隐藏了硬件单元、控制器、磁盘驱动和 互连的许多复杂方面,它只提供存储卷(storage volume)。同样,集群中的一组服务器提供单个系统图像 和计算资源。另一方面,有些新的技术公司(例如 Egenera)对处理区网络(PAN)的兴趣也越来越浓厚(参见 www.egenera.com/pdf/system_data.pdf )。BladeFrame 计算在增加其他节点和管理方面提供了不容置疑的 可伸缩性。所有这些硬件发展只会让 RAC 的故事更加引人注目。 11.1.211.1.211.1.211.1.2 Oracle RAC Oracle RAC Oracle RAC Oracle RAC 的体系结构的体系结构的体系结构的体系结构 从高级别上来看,RAC 是访问单个 Oracle 数据库的多个 Oracle 实例(节点)。该数据库是存储在共享 存储系统上的物理数据库。每个实例都在单独的主机(也称为节点或服务器)上。所有节点都通过私有互连 集群,所有节点都能访问共享存储。所有节点都可以并发执行相同数据库中的事务。集群管理软件(通常由 集群供应商提供)提供单个系统图像,控制节点成员,监控节点状态。从广义上说,主要组件包括: ● 节点/服务器 ● 高速私有互连(将节点连接在一起) 228 ● 集群管理器或 OSD(操作系统依赖层) ● 共享磁盘或存储器 ● 集群文件系统或原始设备 ● 卷管理器 ● 公共网络 集群互连 如果一块数据在一个节点上,而用户在另一个节点需要它,Oracle 使用缓存熔合通过互连(连接节点 的电线,或是某种类型的开关/纤维)将数据块传递到另一个节点。并行处理依赖在多个处理器中传递消息。 运行并行程序的处理器需要数据和指令,然后执行计算。每个处理器定期检查其他节点或主节点,然后计 划下一步动作,或同步结果提交。这些活动依赖消息传递软件,例如满足行业标准的消息传递接口(Message Passing Interface,MPI)。 在并行数据库中, 有大量消息传递和数据库块或页面要传递到另一个节点的本地缓存。大多数功能 和性能取决于传输媒介或方法的效率。对于集群的整体性能和并行应用的使用而言, 它变得至关重要。因 为并行数据库没有对用户可以连接和访问哪些节点强加任何约束条件,所以用户可以选择连接到集群中的 任何节点。不考虑应用程序的本 质,OLTP 或存储数据库的数据,使用互连将数据块从一个节点移动到另 一个节点是一种广泛做法。集群互连提供某种类型的扩充缓存来压缩所有节点的缓存,这种作用是集群最 重要的设计特性之一。通常,集群互连可用于下面这些高级功能: ● 健康、状态和消息同步 ● 分布式锁定管理器(DLM)消息 ● 访问远程文件系统 ● 应用程序专用通信量 ● 集群别名路由 通过在集群内一组节点之间分布计算而获得的高性能需要集群互连来提供高数据传输率和节点间的 低等待通信。同时,互连需要能够检测和隔离错误,使用备用路径。互连的一些基本要求有: ● 短消息的低延迟 ● 大量消息高速并可持续的数据率 ● 每条消息的低主 CPU 利用率 ● 流程控制、错误控制和心跳连续性监控 ● 执行控制程序直接与主机进程交互(操作系统迂回)的主机接口 ● 可测量的交换网络 许多集群供应商都创造了非常具有竞争力的技术。下面描述的许多互连产品接近 SMP(symmetric multiprocessing,对称多处理)总线的延迟级别。表 11-1 总结了各种互连性能(此时它们仍然比较快)。 ● HP 内存通道 内存通道互连是一种高速网络互连,它为应用程序提供集群内地址空间。应 用程序将地址空间映射到它们自己的虚拟地址空间作为 8KB 页面,然后就像使用正常的内存一样, 从该地址空间读取或写入该地址空间。 ● Myrinet Myrinet 是一种高效、高性能的数据包通信和交换技术。它广泛用于 Linux 集群。 Myrinet 软件支持大多数常见主机和操作系统。该软件是开源的。 229 ● 可伸缩的互连(SCI) SCI 是 Sun 公司的最佳集群互连,因为它具有高数据率和低延迟。与 使用低性能方法相比,使用 SCI 能够更好地伸缩强调互连的应用程序。Sun SCI 实现远程共享内 存(Remote Shared Memory,RSM),一种绕过 Solaris TCP/IP 通信系统开销的特性。这样可以提 高集群性能。 ● Veritas 数据库版本/高级集群(DBE/AC)通信由 LLT(低延迟传输)和 GAB(组成员和原子广 播)服务组成。LLT 提供内核到内核的通信,并可作为 IP 堆栈的性能推进器。使用 LLT 代替 IP 可 以减少 IP 堆栈的延迟和系统开销。它现在称为存储器基础(Storage Foundation)。 ● HP HyperFabric 消息传递协议(Hyper Messaging Protocol,HMP) HP Hyper Fabric 支持 IP 上的标准 TCP/UDP 和 HP 所有的 HMP。HyperFabric 通过提供多个网络接口卡之间 连接通信量的透明加载平衡,扩充 TCP/UDP 的可伸缩性和可读性。HMP 加上操作系统迂回性能和 对协议缺载的硬件支持,提供低延迟和非常低的 CPU 利用率。 要构建高性能的 Oracle 实时应用集群,选择正确的互连至关重要。选择适合环境的适当技术时要小 心。可以与供应商协商以获得最新的可用硬件。见表 11-1。 表 11-1 一些互连产品及其性能 度量标准 典型的 SMP 总线 内存通道 Myrinet SCI Gigabit Ethernet 延迟(微秒) 0.5 3 7 到 9 9 100 CPU 系统开销(微秒) < 1 < 1 < 1 每秒的消息数(百万) > 10 > 2 硬件带宽 (MB/秒) > 500 > 100 ~ 250 ~ 50 这里的关键是,进入磁盘在毫秒范围内,而通过互连则在微秒或单个数字的毫秒范围内。 11.1.311.1.311.1.311.1.3 Oracle RAC Oracle RAC Oracle RAC Oracle RAC 系统的内部工作方式系统的内部工作方式系统的内部工作方式系统的内部工作方式 在 Oracle 9i RAC 中,我们不再讨论 DLM、PCM、非 PCM、锁定监控器等。在 Oracle 10g 中,大多数 功能被以全局缓存服务(Global Cache Service)的名义取代或实现。现在将锁定作为保留资源。以前版本 中的后台进程仍然存在但发挥的作用不同。 1. RAC 实例和进程 RAC 是多实例数据库。多个实例同时访问同一个数据库。从RAC 实例和独立 Oracle 实例之间的结构方 面来看没有太多区别。除了所有普通 Oracle 进程(例如 PMON、SMON、LGWR 和 DBWR)之外,还有许多专门进 程,它们可以协调实例内通信,利用集群中节点之间共享的资源。因为有实例内缓冲动作和新的数据块(称 为以前图像数据块,用来保持数据的完整性),所以使用了 SGA 的其他资源。 ● LMON 全局队列服务监控器(LMON)通过监控整个集群来管理全局队列和资源。LMON 管理实 例和进程终止以及全局缓存服务的相关恢复。 ● LMD 全局队列服务守护程序(Global Enqueue Service Daemon,LMD)是锁定代理进程,它 管理对全局缓存服务队列的队列管理器服务请求,从而控制对全局队列和资源的访问。LMD 进程 也处理死锁检测和远程队列请求。 230 ● LMSn 这些全局缓存服务进程(LMSn)是全局缓存服务(GCS)的进程。RAC 软件提供多达 10 个全局缓存服务进程。LMSn 的数量随着集群中节点间消息通信量数量的变化而变化。LMSn 进程 完成如下操作: • 处理全局缓存服务资源的远程实例的中断。 • 为共享资源管理资源请求和跨实例调用操作。 • 构建一列无效锁定元素,在恢复过程中确认锁定元素。 • 处理全局锁定死锁检测,监控锁定转换超时。 ● LCK 进程 管理全局队列请求和跨实例广播。 ● DIAG Diagnosability Daemon 监控实例的健康状况。它捕获实例进程失败的数据。 2. 2. 2. 2. 全局缓存资源全局缓存资源全局缓存资源全局缓存资源(GCS)(GCS)(GCS)(GCS)和全局队和全局队和全局队和全局队列服务列服务列服务列服务(GES)(GES)(GES)(GES) GCS 和 GES(它们都是基本的 RAC 进程)发挥了关键作用。GCS 确保数据的单个系统图像,即使数据被多 个实例访问。GCS 和 GES 是实时应用集群的集成组件,它们协调对共享数据库的同时访问,还协调对数据 库和数据库缓存内共享资源的同时访问。GES 和 GCS 共同维护全局资源目录(Global Resource Directory, GRD)来记录有关资源和队列的信息。GRD 保存在内存中,存储在所有实例上。每个实例都管理部分目录。 分布式特性是 RAC 容错的关键点。 协调共享缓存服务器内的并发任务称为同步。同步使用私有互连和大量消息传输。下面这些类型的资 源需要同步:数据块和队列。GCS 整体维护数据块模式,并负责实例间的数据块传输。LMS 进程处理 GCS 消 息,并完成大多数 GCS 处理。 队列是一种共享内存结构,它串行化对数据库资源的访问。它可以是局部的,也可以是全局的。Oracle 在 3 种模型中使用队列:①空(N)模式;②共享(S)模式;③独占(X)模式。数据块是读写入和读写出缓冲器 的基本结构。它通常是最经常被请求的资源。 GES 维护或处理字典缓存、库缓存、事务锁定和 DDL 锁定的同步。换句话说,GES 管理队列而不是数 据块。为了同步访问数据字典缓存,需要在独占模式和单节点集群数据库中使用闩锁。在集群数据库缓存 中使用全局队列。 3.3.3.3. 缓存熔合和资源协调缓存熔合和资源协调缓存熔合和资源协调缓存熔合和资源协调 因为实时应用集群中的每个节点都有自己的内存(缓存)(不与其他节点共享),所以 RAC 必须协调不同 节点的缓存,同时减少可能降低性能的其他磁盘 I/O。缓存熔合这种技术使用高速互连来提供集群中实例 之间从缓存到缓存的数据块传输。缓存熔合功能允许脏数据块的直接内存写,从而不需要强迫磁盘写和重 读(或 ping)提交的数据块。然而,这不是说不会发生磁盘写。缓存取代和出现检查点时仍然需要磁盘写。 缓存熔合解决了涉及实例间并发性的问题:多个节点上的并发读操作、不同节点上的并发读写、不同节点 上的并发写操作。 如果数据块不一定总是在实例的缓存中,那么 Oracle 只从磁盘读取它们。因为推迟了数据块的写, 所以它们通常包含来自多个事务的修改。只有当出现检查点时,修改过的数据块才会被写到磁盘。进一步 231 讨论之前,我们需要熟悉 Oracle 9i RAC 中引入的一些概念:资源模式和资源角色。因此相同数据块可以 同时存在于多个实例中,所以有两个标识符可以帮助协调这些数据块: ● 资源模式。模式有空模式、共享模式和独占模式。数据块可以用不同模式保存,取决于资源占有 者是要修改数据还是只读它们。 ● 资源角色。这些角色可以局部管理和全局管理。 全局资源目录(GRD)不是数据库。它是内部结构的集合,用来查找数据块的当前状态。当数据块传输 出本地缓存之外,并传输到另一个实例的缓存中,就更新了 GRD。在 GRD 中可用下面有关资源的信息: ● 数据块标识符(Data Block Identifier,DBA) ● 大多数当前版本的位置 ● 数据块的模式(N、S、X) ● 数据块的角色(局部或全局) 4. 4. 4. 4. 以前图像以前图像以前图像以前图像 要保持数据的完整性,在 RAC 的 9i 版本中引入了以前图像(Past Image,PI)这个新概念。数据块的 以前图像在发送数据块之前保存在内存中,作为它是不是脏数据块的标志。当失败时,GCS 可以通过读 PI 来重构数据块的当前版本。这个 PI 不同于 CR 数据块,重构读一致性图像时需要它。数据块的 CR 版本表示 某个时间点上数据的一致图。 例如,实例 A 的事务 A 已经更新数据块 5 上的行 2,后来实例 B 的事务 B 更新了同一个数据块 5 上的 行 6。数据块 5 从实例 A 转换为实例 B。同时,在实例 A 上创建了数据块 5 的 PI。 11.1.411.1.411.1.411.1.4 SCN SCN SCN SCN 处理处理处理处理 系统变更数(System Change Number,SCN)专门确定提交的事务和它进行的变更。SCN 是逻辑时戳,定 义某个时间点上数据库的提交版本。Oracle 给每个提交的事务分配一个唯一的 SCN。 在 RAC 内,因为有多个实现提交的实例,所以需要在实例内维护 SCN 变更,但同时它们必须在集群内 的所有实例之间同步。所以,SCN 由全局缓存服务使用 Lamport SCN 生成模式、硬件时钟或专门的 SCN 服 务器处理。SCN 记录在重做日志中, 以便在 Oracle 9i RAC 中可以同步恢复操作。 1111....RACRACRACRAC 不会崩溃吗不会崩溃吗不会崩溃吗不会崩溃吗 RAC 会崩溃吗?当然会。不好的设计或选择会让它崩溃。除了数据库本身之外,还有许多组件与提供 数据库服务有关。RAC 可以准备就绪并运行,但客户不能实现它。在涉及的客户机和数据库服务器之间有 中间网络组件。它们可能失败。破坏所有硬件的自然灾难——例如火灾、洪水和地震——可能让集群和数 据库不能运行。 假设组件可能失败,RAC 会提供最大限度的保护,并提供连续的数据库服务。即使丢失许多组件,RAC 集群仍然可以运行。但它需要依据涉及的所有组件提供冗余设计。设计是关键字。建立两个或更多节点还 不够;双重互连、到存储单元的双路径、双存储单元、双电源、双公共网络接口等将创建健壮的实时应用 集群。表 11-2 显示单个组件失败的结果。 232 表 11-2 单个组件失败的结果 结 果 组 件 失败的结果 可行 CPU panic/崩溃 节点失败,其他节点仍然有用 可行 内存崩溃 节点失败,其他节点仍然有用 可行 互连 双重互连,可行 (续表) 结 果 组 件 失败的结果 崩溃 互连交换 节点不能通信 可行 OS 失败/冻结 节点失败,其他节点仍然有用 崩溃 集群管理器 s/w 集群冻结,所有节点失效 可行 DB 实例崩溃 在其他节点上运行的实例提供数据库服务 可行 控制文件(破坏/丢失) 使用多元化控制文件 可行 重做日志文件 多元化重做文件 崩溃 丢失的数据文件 需要媒介恢复 崩溃 人为错误 取决于错误类型 崩溃 删除的对象 DB 可用,但应用程序停止 崩溃 DB 软件故障 DB可能在所有实例上停止 只要有一个 Oracle 实例在集群中可用,客户应用程序就有数据访问,可以毫无疑问地执行其应用程 序。 2. 2. 2. 2. 小结小结小结小结 本节绝对没有涵盖 RAC 功能的所有方面。它仅仅着重讨论了 RAC 的一些重要概念和内部运行方式(当 然,这也会改变)。理解特定 RAC 要求和全局共享缓存的实现方式有助于正确设计 RAC 及其应用。充分讨论 RAC 可能需要一整本书的篇幅,而接下来几节将通过调整 RAC 来帮您理解它。 11.1.511.1.511.1.511.1.5 RAC RAC RAC RAC 性能调整概述性能调整概述性能调整概述性能调整概述 与 RAC 实现方式相关的性能问题应该依次关注以下方面: ● 传统数据库调整和监控(本书的大部分内容) ● RAC 集群互连性能(本章和第 5 章) ● 监控工作量性能(本书大部分内容,特别是第 5 章) ● 监控特别与 RAC 相关的争用(本章) ● 在调整 RAC 特定操作之前,应该分别调整每个实例: • 应用调整 • 数据库调整 • 操作系统调整 233 ● 然后开始调整 RAC。正常或传统数据库监控在本书的其他章节(特别是第 5 章)讨论。本章讨论与 RAC 相关的数据库性能。单独调整每个实例之后,再关注通过集群互连通信的进程。 1111....RACRACRACRAC 集群互连性能集群互连性能集群互连性能集群互连性能 RAC 调整最复杂的方面涉及监控和与全局服务目录(GSD)相关的进程的后续调整。与GSD 相关的一组进 程是 GES 和 GCS。GSD 进程通过集群互连通信。如果没有将集群互连配置为有效处理数据包,那么整个 RAC 实现方式的性能就会很糟糕。不管与性能相关的调整和其他方面的配置如何,结果总是这样。 2222....互连通信量互连通信量互连通信量互连通信量————————会话等待会话等待会话等待会话等待 等待影响互连通信量的非空闲等待事件的会话由查询监控,该查询使用全局动态性能视图 gv$session_wait 列出 GCS 等待。在 STATSPACK 或 AWR 报表中也可以查看这些等待。监控的主要等待如表 11-3 所示。 表 11-3 监控的主要等待 等 待 等 待 描 述 全局缓存遇忙 当会话要等待完成资源上正在进行的操作时出现的等待事件 gc 缓存遇忙 当进程必须等待数据块变得可用(因为另一个进程正在获得该数据块的资源) 时发生的等待事件 缓存遇忙全局 CR 通过全局缓存一致读取(读所需的数据块)上的等待 要确定经历系统上等待的会话,就要完成下面的任务。查询 V$SESSION_WAIT,确定是不是所有会话都 要经历与 RAC 相关的等待(当前)。确定导致这些会话争用的对象。尽量调整对象或查询以减少争用。例如, 查询 v$session_wait,确定是不是所有会话都会经历与 RAC 缓存相关的等待。注意,经常使用 GV$视图来 显示整个集群的统计数据,而 V$视图仍然会显示单个节点的统计数据。如果计划使用 RAC,必须将多个节 点的 V$视图和查询扩充为 GV$视图。本节只是帮助您查看所有组件的初级指导。本书这一部分没有专门讨 论 RAC,但这些内容有助于您调整 RAC。 SELECT inst_id, event, p1 FILE_NUMBER, p2 BLOCK_NUMBER, WAIT_TIME FROM gv$session_wait WHERE event IN ('buffer busy global cr', 'global cache busy', 'buffer busy global cache'); 该查询的输出结果如下所示: INST_ID EVENT FILE_NUMBER BLOCK_NUMBER WAIT_TIME ------- ----------------------------- ----------- ------------ ---------- 1 global cache busy 9 150 15 2 global cache busy 9 150 10 运行该查询来确定导致这些会话争用的对象,确定与返回的每个 file _number/block _number 组合的文件和数据块相对应的对象(该查询比较慢): SELECT owner, segment_name, segment_type FROM dba_extents 234 WHERE file_id = 9 AND 150 BETWEEN block_id AND block_id+blocks-1; 输出结果如下所示: OWNER SEGMENT_NAME SEGMENT_TYPE ---------- -------------------- --------------- SYSTEM MOD_TEST_IND INDEX 完成如下操作,调整对象以减少应用程序争用的机率: ● 减少每个数据块的行数 ● 将数据块的大小调整得更小 ● 修改 INITRANS 和 FREELISTS 11.1.611.1.611.1.611.1.6 RAC RAC RAC RAC 等待事件和互连统计数据等待事件和互连统计数据等待事件和互连统计数据等待事件和互连统计数据 如果要运行 RAC,下面的报表就会列出 RAC 事件(多个实例)。如上所述,需要为每个实例运行 STATSPACK 或 AWR 报表。对于 statspack 而言,您要运行需要监控的每个节点上的 statspack.snap 过程和spreport.sql 脚本,以便与其他实例比较。查看节点是否在有效运行的最好方法之一是,将那个节点的报表与访问相同 数据库的另一个节点的报表进行比较。第 5 章讨论了网格控制调整。单个实例调整应该在试图调整通过集 群互连通信的进程之前完成,记住这一点非常重要。换句话说,在将单个实例移动到 RAC 之前调整其中的 系统。 下面简要列举了您可能遇到的一些顶级等待事件,第 14 章将更详细讨论等待事件。需要注意的顶级 全局缓存(gc)等待事件包括: ● gc 当前数据块遇忙 当实例请求 CURR 数据块(要完成某个 DML)和要传输的数据块正在使用 时会出现这种情况。 ● gc 缓存遇忙 当会话必须等待资源上正在进行的操作完成时出现的等待事件,因为数据块 正在使用。进程必须等待数据块变得可用,因为另一个进程正在获取该数据块的资源。 ● gc cr 请求 当实例等待另一个实例的缓存的数据块时发生(通过互连发送)。这种等待说 明,当前实例在本地缓存中没有找到数据块的一致性读(CR)版本。如果数据块不在远程缓存中, 那么这种等待之后就是 db 文件连续读等待。调整 SQL,它会产生大量从节点到节点的读。尽量将 使用相同数据块的用户放在相同的实例上,以便数据块不会在实例间移动。有些非 Oracle 应用 服务器会在节点间移动相同的进程,以便查找最快的节点(未意识到它们在节点间移动相同的数 据块),将这些长进程绑定到相同的节点。如果与较小缓存相关的缓慢 I/O 有问题,就要潜在增 加本地缓存的大小。监控 V$CR_BLOCK_SERVER,查看是否有像读取 UNDO 字段这样的问题。将 P1,P2,P3=file, block, lenum 的值与等待相关(查看 V$LOCK_ELEMENT,查找 lock_element_addr 将相同值作为 lenum 的行)。当实例请求 CR 数据块以及要传输的数据块没有到达请求实例时会发 生这种情况。这是最常见的情况,通常是因为 SQL 没有调整好,以及许多索引数据块在实例间来 回移动。 图 11-1 显示 AWR 报表的 RAC 部分。可以看到集群中有 6 个实例(节点)。您也可以看到发送和接收的 数据块的数量,以及本地缓存中访问的数据块相对于磁盘或另一个实例的数量(93.1%)。您可能会猜想到, 访问本地缓存中的数据块会更快,但访问另一个节点上的远程缓存几乎总是比访问磁盘快(假设有足够快的 235 互连,同时没有互连饱和)。 下面是另一个获得会话等待信息的有价值的查询。INSTANCE_ID 列出保存等待会话的实例。SID 是等 待会话的唯一标识符(gv$session)。p1、p2 和 p3 列列出了事件专用信息,这些信息可能有助于调试。 LAST_SQL 列出等待会话执行的最后 SQL。 SET NUMWIDTH 10 COLUMN STATE FORMAT a7 tru COLUMN EVENT FORMAT a25 tru COLUMN LAST_SQL FORMAT a40 tru SELECT sw.inst_id INSTANCE_ID, sw.sid SID, sw.state STATE, sw.event EVENT, sw.seconds_in_wait SECONDS_WAITING, sw.p1, sw.p2, sw.p3, sa.sql_text LAST_SQL FROM gv$session_wait sw, gv$session s, gv$sqlarea sa WHERE sw.event NOT IN ('rdbms ipc message','smon timer','pmon timer', 'SQL*Net message from client','lock manager wait for remote message', 'ges remote message', 'gcs remote message', 'gcs for action', 'client message', 'pipe get', 'null event', 'PX Idle Wait', 'single-task message', 'PX Deq: Execution Msg', 'KXFQ: kxfqdeq - normal deqeue', 'listen endpoint status', 'slave wait','wakeup time manager') AND sw.seconds_in_wait > 0 AND (sw.inst_id = s.inst_id and sw.sid = s.sid) AND (s.inst_id = sa.inst_id and s.sql_address = sa.address) ORDER BY seconds_waiting DESC; 图 11-1 AWR 报表 RAC 统计数据 236 下面的查询描述上一节中事件的参数名称: COLUMN EVENT FORMAT a30 tru COLUMN p1text FORMAT a25 tru COLUMN p2text FORMAT a25 tru COLUMN p3text FORMAT a25 tru SELECT DISTINCT event EVENT, p1text, p2text, p3text FROM gv$session_wait sw WHERE sw.event NOT IN ('rdbms ipc message','smon timer','pmon timer', 'SQL*Net message from client','lock manager wait for remote message', 'ges remote message', 'gcs remote message', 'gcs for action', 'client message','pipe get', 'null event', 'PX Idle Wait', 'single-task message', 'PX Deq: Execution Msg', 'KXFQ: kxfqdeq - normal deqeue','listen endpoint status', 'slave wait','wakeup time manager') AND seconds_in_wait > 0 ORDER BY event; GV$SESSION_WAIT 视图的内容如表 11-4 所示。 表 11-4 GV$SESSION_WAIT 视图的内容 列 数 据 类 型 描 述 INST_ID NUMBER RAC 配置中实例的编号 SID NUMBER 会话标识符 SEQ# NUMBER 唯一确定该等待的序列号,随着每个等待递增 EVENT VARCHAR2(64) 会话等待的资源或事件 P1TEXT VARCHAR2(64) 第一个附加参数的描述 P1 NUMBER 第一个附加参数 P1RAW RAW(4) 第一个附加参数 P2TEXT VARCHAR2(64) 第二个附加参数的描述 P2 NUMBER 第二个附加参数 P2RAW RAW(4) 第二个附加参数 P3TEXT VARCHAR2(64) 第三个附加参数的描述 P3 NUMBER 第三个附加参数 P3RAW RAW(4) 第三个附加参数 WAIT_CLASS_ID NUMBER 等待类的标识符 WAIT_CLASS# NUMBER 等待类的编号 WAIT_CLASS VARCHAR2(64) 等待类的名称 WAIT_TIME NUMBER 非零值是会话的最后等待时间。零值表示会话正在等待 (续表) 列 数 据 类 型 描 述 SECONDS_IN_WAIT NUMBER 如果 WAIT_TIME = 0,那么 SECONDS_IN_WAIT 就是当前等 待条件下花费的秒数。如果 WAIT _TIME > 0, 那么 237 SECONDS_IN_WAIT 就是从最后等待开始的秒数, SECONDS_IN_WAIT – WAIT _TIME / 100 是从最后等待结 束开始的活动秒数 STATE VARCHAR2(19) 状态 Oracle 10g 中新增了 WAIT_CLASS 列,它代表 12 个基本等待类,其中之一是集群等待类。 技巧: 使用 V$SESSION_WAIT 或 GV$SESSION _WAIT、Statspack 或 AWR 报表来查找 RAC 等待事件。 1. GES1. GES1. GES1. GES 锁定阻塞者和等待者锁定阻塞者和等待者锁定阻塞者和等待者锁定阻塞者和等待者(Blocker and Waiter)(Blocker and Waiter)(Blocker and Waiter)(Blocker and Waiter) 保存全局锁定(永久中断其他锁定)的会话对实现 RAC 可能存在问题,并且它存在于许多与应用设计有 关的实例中。等待锁定释放的会话会挂起,并要求调查中断的对象以便确定状态。大量保存全局锁定的会 话会创造大量互连通信量,从而降低性能。 -- GES LOCK BLOCKERS: --INSTANCE_ID The instance on which a blocking session resides --SID Unique identifier for the session --GRANT_LEVEL Lists how GES lock is granted to user associated w/ blocking session --REQUEST_LEVEL Lists the status the session is attempting to obtain --LOCK_STATE Lists current status the lock has obtained --SEC Lists how long this session has waited SET numwidth 10 COLUMN LOCK_STATE FORMAT a16 tru; COLUMN EVENT FORMAT a30 tru; SELECT dl.inst_id INSTANCE_ID, s.sid SID ,p.spid SPID, dl.resource_name1 RESOURCE_NAME, decode(substr(dl.grant_level,1,8),'KJUSERNL','Null','KJUSERCR','Row-S (SS)', 'KJUSERCW','Row-X (SX)','KJUSERPR','Share','KJUSERPW','S/Row-X (SSX)', 'KJUSEREX','Exclusive',request_level) AS GRANT_LEVEL, decode(substr(dl.request_level,1,8),'KJUSERNL','Null','KJUSERCR','Row-S (SS)', 'KJUSERCW','Row-X (SX)','KJUSERPR','Share','KJUSERPW','S/Row-X (SSX)', 'KJUSEREX','Exclusive',request_level) AS REQUEST_LEVEL, decode(substr(dl.state,1,8),'KJUSERGR','Granted','KJUSEROP','Opening', 'KJUSERCA','Canceling','KJUSERCV','Converting') AS LOCK_STATE, s.sid, sw.event EVENT, sw.seconds_in_wait SEC FROM gv$ges_enqueue dl, gv$process p, gv$session s, gv$session_wait sw WHERE blocker = 1 AND (dl.inst_id = p.inst_id and dl.pid = p.spid) AND (p.inst_id = s.inst_id and p.addr = s.paddr) AND (s.inst_id = sw.inst_id and s.sid = sw.sid) ORDER BY sw.seconds_in_wait DESC; 238 GES LOCK WAITERS: --INSTANCE_ID The instance on which a blocking session resides --SID Unique identifier for the session --GRANT_LEVEL Lists how GES lock is granted to user associated w/ blocking session --REQUEST_LEVEL Lists the status the session is attempting to obtain --LOCK_STATE Lists current status the lock has obtained --SEC Lists how long this session has waited SET numwidth 10 COLUMN LOCK_STATE FORMAT a16 tru; COLUMN EVENT FORMAT a30 tru; SELECT dl.inst_id INSTANCE_ID, s.sid SID, p.spid SPID, dl.resource_name1 RESOURCE_NAME, decode(substr(dl.grant_level,1,8),'KJUSERNL','Null','KJUSERCR','Row-S (SS)', 'KJUSERCW','Row-X (SX)','KJUSERPR','Share','KJUSERPW','S/Row-X (SSX)', 'KJUSEREX','Exclusive',request_level) AS GRANT_LEVEL, decode(substr(dl.request_level,1,8),'KJUSERNL','Null','KJUSERCR','Row-S (SS)', 'KJUSERCW','Row-X (SX)','KJUSERPR','Share','KJUSERPW','S/Row-X (SSX)', 'KJUSEREX','Exclusive',request_level) AS REQUEST_LEVEL, decode(substr(dl.state,1,8),'KJUSERGR','Granted','KJUSEROP','Opening', 'KJUSERCA','Canceling','KJUSERCV','Converting') AS LOCK_STATE, s.sid,sw.event EVENT, sw.seconds_in_wait SEC FROM gv$ges_enqueue dl, gv$process p,gv$session s,gv$session_wait sw WHERE blocked = 1 AND (dl.inst_id = p.inst_id and dl.pid = p.spid) AND (p.inst_id = s.inst_id and p.addr = s.paddr) AND (s.inst_id = sw.inst_id and s.sid = sw.sid) ORDER BY sw.seconds_in_wait DESC; 2. 2. 2. 2. 熔合读写熔合读写熔合读写熔合读写 当需要将另一个实例以前改变的数据块写到磁盘以响应检查点或缓存老化时,就会出现熔合写。出现 这种情况时,Oracle 会发送消息通知另一个实例:会执行熔合写将数据块移动到磁盘。熔合写不需要另外 写到磁盘,它们是一个实例产生的所有物理写的子集。DBWR 熔合写/物理写的比率显示 Oracle 管理的写与 熔合写的比例。下面这个查询确定缓存熔合写操作的比率: SELECT A.inst_id "Instance", A.VALUE/B.VALUE "Cache Fusion Writes Ratio" FROM GV$SYSSTAT A, GV$SYSSTAT B WHERE A.name='DBWR fusion writes' AND B.name='physical writes' AND B.inst_id=a.inst_id ORDER BY A.INST_ID; 以下是示例输出: Instance Cache Fusion Writes Ratio 239 --------- ------------------------- 1 .216290958 2 .131862042 缓存熔合写操作比率的大值表示: ● 缓存不够大 ● 检查点不够 ● 根据缓存取代或检查点写的大量缓冲器 11.1.711.1.711.1.711.1.7 集群互连调整集群互连调整集群互连调整集群互连调整———————— 硬件等级硬件等级硬件等级硬件等级 集群互连调整是集群配置的重要方面。Oracle 依赖集群互连在实例间传输数据。专门私有网络用于互 连非常重要。下面的查询有助于确定实例是否正确注册了网络地址: SQL> SELECT * FROM GV$CLUSTER_INTERCONNECTS; INST_ID NAME IP_ADDRESS IS_ SOURCE ---------- ------ ---------------- --- ------------------------------- 1 eth1 10.16.0.168 NO Oracle Cluster Repository 2 eth1 10.16.0.170 NO Oracle Cluster Repository SOURCE 列表示互连与 OCR(Oracle 集群仓库)集成。该列可能的值有: ● Oracle 集群仓库 使用 OCR 配置的互连信息。 ● 集群互连 使用参数 CLUSTER_INTERCONNECT 配置的互连信息。 ● 操作系统独立 配置第三方集群管理器,Oracle 集群件只是 Oracle RDBMS 和第三方集群管 理器之间的纽带。 对集群互连的重要测试应该从硬件配置测试开始。确定传输率对实际实现数据包大小的测试应该确保 安装了所有说明。从 Oracle 数据库 10g 版本 2 开始,不支持在配置两个节点间的互连时使用交叉电缆。因 此需要交换作为参与集群的节点之间的纽带。确定系统的性能时,必须独立于互连的速度确定交换的速度, 以确定交换和互连的实际延迟 。 集群互连的速度完全依赖硬件供应商(见表 11-1)和分层的操作系统。当前版本的 Oracle 依赖操作系 统和在集群互连间发送信息数据包的硬件。例如,SUN 4800 之间支持的一种集群互连是用户自带寻址信息 协议(User Datagram Protocol,UDP)。然而,这种特定版本互连协议上的 Solaris 在数据传输方面有 64K 数据包大小的操作系统限制。要通过该互连协议传输相当于 256K 的数据,就要在 4 个往返上采用这种配置。 在具有大量互连通信量的高级事务系统上,这可能会导致严重的性能问题。 在最初硬件和操作系统级别测试确认互连的数据包大小之后,后续测试会使用缓存到缓存数据传输或 缓存熔合技术通过 Oracle 数据库完成,以确保没有任何严重的附加延迟。随后的查询提供系统上一致数据 块请求的平均延迟。这些视图中的数据是从上次启动 Oracle 实例以来的累积数字。这些视图中的数据没有 反映互连的实际性能,也没有显示数据传输中延迟的真实情况。要获得更现实的性能情况,最好再次启动 所有 Oracle 实例和测试。要获得良好的性能,重要的是集群互连之间的延迟要尽可能低。集群互连上延迟 可能由下列原因造成: 240 ● 运行队列中有大量进程等待 CPU 或计划延时 ● 平台专用操作系统参数设置影响 IPC 缓冲或进程安排 ● 缓慢、遇忙或不完善的互连 Oracle 建议一致数据块请求的平均延迟通常不应该超过 15 毫秒(取决于系统配置和量)。通过互连发 送许多数据块时,它实际上很高(特别是进入磁盘通常这样)。对于 high-volume 系统而言,它应该在单个 数字的毫秒到微秒的范围。一致数据块请求的平均延迟是一致读取请求往返——从请求实例到保存实例再 返回请求实例——的平均延迟。 set numwidth 20 column "AVG CR BLOCK RECEIVE TIME (ms)" format 9999999.9 select b1.inst_id, b2.value "GCS CR BLOCKS RECEIVED", b1.value "GCS CR BLOCK RECEIVE TIME", ((b1.value / b2.value) * 10) "AVG CR BLOCK RECEIVE TIME (ms)" from gv$sysstat b1, gv$sysstat b2 where b1.name = 'gc cr block receive time' and b2.name = 'gc cr blocks received' and b1.inst_id = b2.inst_id; INST_ID GCS CR BLOCKS RECEIVED GCS CR BLOCK RECEIVE TIME AVG CR BLOCK RECIVE TIME (ms) ------ ------------------- -------------------- ------------------------ 1 2758 112394 443.78 2 1346 1457 10.8 2 rows selected. 在前面的输出结果中,注意 AVG CR BLOCK RECEIVE TIME 是 443.78 毫秒;当 Oracle 推荐的预期平均 延迟不应该超过 15 毫秒时,它已经非常高。如果 CPU 的空闲时间有限,而且系统通常处理长时间查询,那 么就可能出现高值。然而,使用用户模式 IPC 时,平均延迟可能低于 1 毫秒。DB_MULTI_BLOCK_READ_COUNT 参数的高值也可能影响延迟。这是因为请求进程可以依据该参数的设置给数据块发出多个请求。这样请求 进程可能必须等待更长时间。这种高延迟要求进一步调查集群互连配置,必须在操作系统级别执行这些测 试。当互连有这种高延迟时,另一个好的测试是在操作系统级别通过检查实际连接时间来执行测试。这有 助于确定操作系统级别有没有问题。毕竟,RAC 环境内的数据传输不会产生性能问题。 除了能够在操作系统级别完成的基本数据包传输测试之外,还可以执行其他检查和测试,以确保正确 配置集群互连。在参与集群的节点之间有冗余的私有高速互连。实现网络接口卡(network interface card, NIC)连接或配对有助于互连负载平衡和某个互连失败时的故障恢复。用户网络连接不会影响集群互连通信 量,也就是说它们彼此分离。 在操作系统级别,netstat 和 ifconfig 命令显示与网络相关的数据结构。下面的输出结果(来自 netstat-i)显示配置有 4 个网络适配器,并且实现了 NIC 配对: [oracle@oradb3 oracle]$ netstat –i Kernel Interface table Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg bond0 1500 0 3209 0 0 0 4028 0 0 0 BMmRU bond0:1 1500 0 4390 0 0 0 6437 0 0 0 BMmRU 241 bond1 1500 0 7880 0 0 0 10874 0 0 0 BMmRU eth0 1500 0 1662 0 0 0 2006 0 0 0 BMsRU eth1 1500 0 1547 0 0 0 2022 0 0 0 BMsRU eth2 1500 0 4390 0 0 0 6437 0 0 0 BMRU eth3 1500 0 3490 0 0 0 4437 0 0 0 BMRU lo 16436 0 7491 0 0 0 7491 0 0 0 LRU Iface 列中的值有如下含义: ● bond0 它是使用连接功能创建的公共互连(连接 eth0 和 eth1)。 ● bond0:1 它是分配给 bond0 的 VIP(虚拟 IP)。 ● bond1 它是使用连接功能创建的私有互连(连接 eth2 和 eth3)。 ● eth0 和 eth1 这些是物理公共接口,它们连接/配对在一起((bond0)。 ● eth2 和 eth3 这些是物理私有接口,它们连接/配对在一起((bond1)。 ● lo 这是回送,即输出结果也说明配置了回送选项。使用 ORADEBUG 命令可以验证 Oracle 是否在使用回送选项,这将在本节稍后讨论。回送 IP 的使用取决于每个节点上定义的路由表的 完整性。修改路由表可能导致互连不能运行。 前面 netstat 的输出结果还包括 MTU(最大传输单元),它设置为 1500 字节(这是 UDP 的标准设置)。MTU 定义不包括数据链标题。然而,数据包大小计算包括数据链标题。各种工具显示的最大数据包大小等于 MTU 加上数据链标题的长度。要获得互连的最大好处,应该将 MTU 配置为支持的最高可能值。例如,使用大型 架构将它设置为 9K,这有助于提高互连带宽和数据传输。 除了能够在操作系统级别完成的基本数据包传输测试之外,还可以执行其他检查和测试,以确保集群 互连配置正确。还要从 Oracle 实例执行检查以确保互连协议配置正确。如果将下面的命令作为用户“SYS” 执行,就会在用户转储目标目录中生成跟踪文件,该目录包含某些属于 UDP /IPC 配置的诊断信息。(关于 DEBUG 功能的更多信息请参考第 13 章)。在阅读有关它的 Oracle 文档之前请不要使用它。 SQL> ORADEBUG SETMYPID ORADEBUG IPC EXIT 下面的代码摘自附属于互连协议的跟踪文件。输出结果证明集群互连正用于实例到实例的消息传输。 SSKGXPT 0x3671e28 flags SSKGXPT_READPENDING info for network 0 socket no 9 IP 172.16.193.1 UDP 59084 sflags SSKGXPT_WRITESSKGXPT_UP info for network 1 socket no 0 IP 0.0.0.0 UDP 0 sflags SSKGXPT_DOWN context timestamp 0x4402d no ports 前面的输出结果摘自 Sun 4800,它显示了 IP 地址,同时说明使用的协议是 UDP。在某些操作系统上(例 如 Tru64),跟踪输出结果不显示集群互连信息。下面操作系统级别的 NDD Unix 命令会证明实际 UDP 大小 的定义。下面的输出结果出自 SUN 环境: 242 oradb1:RAC1:oracle # ndd -get /dev/udp name to get/set ? udp_xmit_hiwat value ? length ? 8192 name to get/set ? udp_recv_hiwat value ? length ? 8192 输出结果显示 UDP 被配置为 8K 数据包。 将这种查找应用于从 Oracle 视图收集的数据,这样可以显 示在集群互连间传输的所有数据块会执行 14050 次往返(112394/8 =14050)。如果将它设置为 64K,那么往 返的次数会大大减少(112394/64 = 1756)。 影响互连通信量的另一个参数是 DB_FILE_MULTIBLOCK_READ_ COUNT。这个参数说明每次从磁盘读取的 数据块数量。数据需要通过集群互连传输时,该参数确定读取传输过程中每个实例从另一个实例请求的数 据块的大小。应该按照互连延迟和硬件供应商定义的数据包大小,并且在考虑操作系统限制(例如,SUN UDP 最大设置只有 64K)之后确定该参数的大小。下面的内核参数定义 udp 参数设置: ● udp_recv_hiwat ● udp_xmit_hiwat 将这些参数设置为 65536 会将 udp 缓冲大小增加为 64K。 另一个参数 CLUSTER_INTERCONNECTS 提供有关其他集群互连可用性的 Oracle 信息,其他这些集群互 连可用于通过集群互连的缓存熔合活动。该参数用优先集群通信量网络重写操作系统级别的默认互连设置。 该参数通过减少这种延迟,的确能为关注高互连延迟的系统提供优势,但配置这个参数会影响互连高可用 性的特性。换句话说,通常不明显的互连失败可能导致 Oracle 集群失败,因为 Oracle 仍然试图访问网络 接口。 资源可用性 任何计算机、节点上,或者提供给 Oracle 实例的可用资源都是有限的,也就是说它们不是无限可用 的,如果系统上的进程需要它们,这些资源也许不能立即可用。任何系统上的可用资源数量都有物理限制。 例如,处理器资源受系统上可用 CPU 数量的限制,内存或缓存区的数量受系统上可用物理内存的限制。对 于 Oracle 进程而言,它进一步受到分配给 SGA 的内存的实际数量的限制。在 SGA 内,从共享池区再次预先 分配共享池、缓冲区缓存等。这些都是正常的单实例配置使用的内存分配方式。 在 RAC 环境中,没有参数分配任何全局特定资源,例如全局缓存大小或全局共享池区。Oracle 从 SGA 中分配部分可用资源用于全局活动。可以使用 GV$RESOURCE_LIMIT 视图监控全局资源的可用性。例如,下 面的查询显示可用于全局活动的资源的当前数量。在下面的输出结果中,资源的可用性受“LIMIT_VALUE” 列的限制,当这些资源偏低时,增加限度的方法是增加 SHARED_POOL_SIZE。 下面查询生成的输出结果包含资源的当前利用率: SELECT RESOURCE_NAME, CURRENT_UTILIZATION CU, MAX_UTILIZATION MU, 243 INITIAL_ALLOCATION IA, LIMIT_VALUE LV FROM GV$RESOURCE_LIMIT WHERE MAX_UTILIZATION > 0 ORDER BY INST_ID, RESOURCE_NAME; RESOURCE_NAME CU MU IA LV --------------------- ---------- ---------- ---------- ---------- cmtcallbk 0 1 187 UNLIMITED dml_locks 2 59 748 UNLIMITED enqueue_locks 19 27 2261 2261 enqueue_resources 22 45 968 UNLIMITED gcs_shadows 2259 2579 18245 18245 ges_big_msgs 27 28 964 UNLIMITED ges_cache_ress 338 1240 0 UNLIMITED ges_procs 35 36 320 320 ges_reg_msgs 44 81 1050 UNLIMITED max_rollback_segments 11 11 187 65535 max_shared_servers 1 1 UNLIMITED UNLIMITED processes 31 34 150 150 sessions 37 40 170 170 sort_segment_locks 0 1 UNLIMITED UNLIMITED transactions 2 4 187 UNLIMITED (truncated output) SHARED_POOL_SIZE 增加为 10MB 时,全局资源分配也会改变为下列新值: gcs_resources 2553 2553 19351 19351 gcs_shadows 1279 1279 19351 19351 规则是,MAX_UTILIZATION (MU)接近 LIMIT_VALUE (LV)并且长时间将常量保持为这个值时,就考虑增 加 SGA。 Oracle 也在内存中保持几个全局区,它们与 Oracle 紧密相关。分配大小是这些区的常量,而它们也 包含在参数 SHARED_POOL_SIZE 中。例如,下面的查询显示专门为 RAC 环境保持的内存区: SELECT * FROM v$sgastat where name like 'g%'; POOL NAME BYTES ----------- -------------------------- ---------- shared pool ges enqueue cur. usage pe 16 shared pool ges deadlock xid hash tab 11036 shared pool ges recovery domain table 108 shared pool ges process hash table 9504 shared pool gcs res hash bucket 65536 shared pool gcs close obj 4104 shared pool ges lmd process descripto 2684 244 shared pool gcs scan queue array 216 shared pool ges process array 281600 shared pool gcs resources 7379392 shared pool grplut_kfgsg 256 shared pool generic process shared st 448 shared pool ges enqueue max. usage pe 16 shared pool ges reserved msg buffers 2897860 shared pool gcs mastership buckets 4608 shared pool ges scan queue array 108 shared pool groups_kfgbsg 4096 shared pool gcs shadows 5241536 shared pool gcs resource freelist dyn 32 shared pool gcs shadow locks dyn seg 32 shared pool ges resource hash seq tab 16384 shared pool gcs I/O statistics struct 32 shared pool ges deadlock xid freelist 7040 shared pool gcs resource freelist arr 272 shared pool ges regular msg buffers 609004 shared pool gcs opaque in 4028 shared pool grptab_kfgsg 3592 shared pool ges enqueues 3870184 shared pool ges res mastership bucket 3072 shared pool ges resource hash table 442368 shared pool ges ipc instance maps 384 shared pool ges big msg buffers 3979396 shared pool gcs res latch table 15360 shared pool gcs affinity 4108 shared pool ges resource 3105300 shared pool gcs shadow locks freelist 272 shared pool ges enqueue multiple free 400 shared pool ges lms process descripto 5368 shared pool ges shared global area 22724 shared pool gcs commit sga state 67596 41 rows selected. 11.1.811.1.811.1.811.1.8 使用企业管理器网格控制调整使用企业管理器网格控制调整使用企业管理器网格控制调整使用企业管理器网格控制调整 RACRACRACRAC Oracle 企业管理器网格控制、AWR 报表和 STATSPACK 报表都是确定互连延迟良好的信息来源。最好的 RAC 调整工具是 Oracle 企业管理器网格控制。虽然在第 3 章已经详细讨论过它,但这里互连部分也值得研 究。要查找集群的数据库(或 RAC/网格数据库),我们必须进入目标/所有目标(Targets/All Targets)屏幕, 再单击集群数据库查看。本示例中,单击“ioug”集群数据库显示该集群数据库的监控信息(如图 11-2 所 示)。该屏幕显示有全部 6 个实例。有一些 CPU(约 25%)正在被使用,还有不到 20 个活动会话。 245 图 11-2 “ioug”6 个实例集群数据库的性能信息 该页面的一个非常重要的部分是诊断小结(Diagnostic Summary)部分,它在本示例中显示有 4 个互连 查找。 技巧: 使用企业管理器网格控制查找互连问题。 在同一个屏幕的底部,可以看到集群数据库的一些最有用的链接和信息,如图 11-3 所示,其中包括 所有与集群数据库相关的实例。这里还显示了 6 个实例(从 ioug_ioug1 到 ioug_ ioug6),这样就很容易通过单击实例来查看各个实例的信息。 246 图 11-3 “ioug”6 个实例集群数据库的性能信息(底部) 单击某个实例(本示例中是 ioug_ioug1),就会显示该实例的主要信息屏幕,如图 11-4 所示。在图 11-4 的右上角还有一个下拉菜单,它让您能够快速切换到另一个数据库实例。 图 11-4 “ioug1”实例的性能信息 数据库性能标签数据库性能标签数据库性能标签数据库性能标签 单击性能标签会显示该集群的详细图表和一个主屏,它可以用来调整互连。图 11-5 显示了该屏幕的 上半部分。单击单个图表会显示给定性能度量更详细的图表。移动到屏幕中间会显示其他图表。 247 技巧: OEM 内的数据库或集群性能屏幕是查找系统中性能问题的最快方法。 图 11-5 集群数据库性能 图 11-6 显示了其他许多性能链接。这些链接包括数据库锁定、顶级会话(Top Session)、顶级客户(Top Consumer)、集群缓存一致(Cluster Cache Conherence)和顶级段(Top Segment)。每个链接都可用来研究 具体问题。 单击图 11-5 中所示的 Cluster Host Load Average 图表,就会显示该图的更大版本,列出的每个节 点都有颜色。在图 11-7 所示的示例中,该图表显示了 ioug 集群中的 4 个实例(在 4 个物理节点上)。实例 是 ioug3、ioug4、ioug5 和 ioug6,实例占据的物理节点是 atlmdi3、atlmdi4、atlmdi5 和 atlmdi7。 单击图 11-5 中的第 2 个图表会显示互连问题。全局缓存数据块访问延迟和传输与从一个实例向另一 个实例发送数据块有关。单击集群数据库性能(Cluster Database Performance)屏幕(如图 11-8 所示)上的 集群缓存一致(Cluster Cache Coherency)链接也会显示这个屏幕。在图 11-8 中,数据块传输的数量在上 午 11:10 急剧增加。所有访问延迟超过 20 毫秒的数据块都需要进一步调查。修复这个问题涉及调整导致大 量数据块读取或传输的查询,获得更快的互连,排除放慢传输的所有锁定(附属于数据块的一个实例)或使 用公共(而不是私有)互连。 248 图 11-6 集群数据库其他性能链接 图 11-7 集群数据库性能加载平均值 249 图 11-8 集群缓存一致性 在图 11-5 中的第 3 个图表中,活动会话图表显示了大量集群等待。单击图表右边的 Cluster 链接(但 是在实例级别),就会显示所有集群等待的详细图表,如图 11-9 所示。我们可以看到显示时许多与该图表 相关的全局缓存(gc)类型的等待。在图表下方,我们可以看到正在运行的实际 Top SQL 查询以及运行这些 查询的用户的顶级会话(Top Session)。屏幕只显示了集群等待的 Top SQL 和顶级会话。这是一个彩色屏幕, 用不同颜色显示每个等待,以便让调整变得更加直观。 技巧: 可以研究企业管理器上的特定全局缓存等待事件。 单击图表右边 gc current block busy 等待上的链接,就会立即转换为该等待的柱状图,通过它查看 这些等待是许多短等待还是较少的长等待。这里有些等待很短(1~2 毫秒),有些等待较长(32 毫秒及以上), 如图 11-10 中的柱状图所示。 注意: 第 5 章更详细讨论了使用企业管理器网格控制的方法,那里的图也比这里多。第 5 章还 介绍了查找导致问题的特定 SQL 语句的方法,以及使用企业管理器网格控制调整特性调 整它们的方法。 250 图 11-9 活动会话等待——集群 图 11-10 gc current block busy 等待的等待事件柱状图 11.211.211.211.2 并行操作的基本概念并行操作的基本概念并行操作的基本概念并行操作的基本概念 使用并行操作可以让多个处理器(以及可能补加的处理器)协同工作,执行单个 SQL 语句。这个特性提 高了数据密集型的操作,并且是动态的(在运行期才决定执行路径),能够充分利用您所有的处理器和磁盘 驱动(如果实现得当)。它需要一些额外的开销和管理,但 PEO 可以提高多数操作的性能。 251 考虑一个全表扫描。相对于使用单一进程来执行全表扫描,Oracle 可以创建多个进程来并行执行表的 扫描。用于执行扫描的进程的数目称为并行度(degree of parallelism,DOP)。可以在创建表或者查询的 时候使用提示来设置并行度。图 11-11 展示了将 EMP 表的全表扫描分割成 4 个独立的并行查询服务器进程 (并行度为 4)。创建第 5 个进程(查询的协调器)它是为了协调 4 个并行查询服务器进程。 技巧: 并行处理一般都牵涉到访问磁盘。如果数据不是分布在多个磁盘上,使用 PEO 将会造成 I/O 瓶颈。 图 11-11 使用并行执行的一个简单的全表扫描(没有显示磁盘访问) 如果图 11-11 中全表扫描返回的结果集仍需要排序的话,最终的操作应该像图 11-12 一样。Oracle 将使用一个进程来协调查询,4 个进程运行查询,4 个进程对查询排序。总共 9 个进程,但并行度仍然是 4。 如果有 9 个处理器(CPU)的话,您的机器将使用全部 9 个处理器来执行操作(取决于您系统的设置以及正在 同时执行的其他操作)。如果可供使用的处理器不足 9 个,则在 Oracle 管理查询时,可能遇到一些 CPU 的 瓶颈问题。 因为查询协调部分的操作将占用一些资源,所以使用并行操作通常并不能增强(甚至还可能降低)快速 查询的性能。 技巧: 在很小的表或者非常快速的查询中使用并行操作同样也会降低性能,因为协调查询也会 消耗性能资源。应当评估并行的代价是否会超过非并行的代价。 所有的查询(图 11-11 和图 11-12)都需要访问物理磁盘以获取数据,然后再将它们存到 SGA 中。根据 查询是如何分解的来决定数据在磁盘上的平衡分布将造成巨大的 I/O 差异。 252 图 11-12 使用并行执行的一个简单的需要排序的全表扫描(没有显示 SGA) 技巧: 当并行度被设为 N,那么并行操作总共就需要(2*N)+1 个进程。尽管并行操作是处理进程 而不是处理器,但当有大量的处理器可以使用时,Oracle 将使用其他的处理器来运行并 行查询,这通常将增强查询的性能。 11.311.311.311.3 并行并行并行并行 DMLDMLDMLDML 和和和和 DDLDDLDDLDDL 语句和操作语句和操作语句和操作语句和操作 Oracle 支持 DDL 和 DML 操作的并行处理。Oracle 可以对以下表和索引的操作实现并行化处理: ● SELECT ● UPDATE, INSERT, DELETE ● MERGE ● CREATE TABLE AS ● CREATE INDEX ● REBUILD INDEX ● MOVE/SPLIT/COALESCE PARTITION ● ENABLE CONSTRAINT 下列操作也可以在语句中实现并行化处理: ● SELECT DISTINCT ● GROUP BY ● ORDER BY ● NOT IN ● UNION 和 UNION ALL ● CUBE 和 ROLLUP ● 统计函数,例如 SUM 和 MAX ● NESTED LOOPS 连接 ● SORT/MERGE 连接 ● Star 转换 技巧: 自 Oracle 8i 起即可运行并行 DML 语句。这个功能主要应用于分布在不同分区上的表和 索引。Oracle 使用基于成本的优化器来决定是否需要并行处理一个语句,并决定所实施 的并行度。 11.411.411.411.4 Oracle 9i Oracle 9i Oracle 9i Oracle 9i 的并行的并行的并行的并行 DMLDMLDMLDML 语句和操作语句和操作语句和操作语句和操作 在 Oracle 9i 中,绝大多数操作都可以并行执行,包括查询、DML 和 DDL 操作。而且,自 Oracle 9i 后,还支持分区并行处理;多个并行查询服务器进程甚至可以针对同一个分区执行。 并行度可能要受到多种因素的制约。虽然在 Oracle 9i 中分区策略在并行处理中并不扮演重要的角色, 但还是要注意以下的限制因素: 253 ● 服务器上可以使用的处理器数。 ● 仍然需要启用分区选项,而 UPDATE、DELETE 和 MERGE 只在已分区的表上才能使用并行化处理。 ● 通过设置初始化参数 PARALLEL_MAX_SERVERS,调整实例允许的并行查询服务器进程数。 ● 如果使用数据库资源管理器(Database Resource Manager),并行度受到用户策略的限制。 ● 实例中其他用户使用的并行查询服务器进程的数量。 ● PARALLEL_ADAPTIVE_MULTI_USER 参数的设置,为了支持其他用户,该参数可能限制了您的并行 化处理。 在多用户环境中监控您的并行操作是很重要的,这样可以给它们分配与您计划相应的资源。数据库资 源管理器将帮助您分配资源。 11.511.511.511.5 并行处理和分区并行处理和分区并行处理和分区并行处理和分区 Oracle 的分区特性可能会对 Oracle 10g 中的并行操作产生显著影响。分区是指表数据和索引的逻辑 划分,同一张表或索引的分区可能分布在多个表空间上。基于这种体系结构,下面是 Oracle 8i 在分区上 的并行操作的重要区别: ● 只有访问多个分区时,才能对已分区的对象执行并行处理操作。 ● 在 Oracle 8i 中,如果一张表被分为 12 个逻辑区域,而正在执行的查询将只访问其中 6 个分区(因 为数据的维数说明了数据存储的分区),那么最多只有 6 个并行服务器进程可以被分配用于满足这个查询。 注意: 在 Oracle 9i 和 Oracle10g 中,这些限制已经被取消了。 11.611.611.611.6 操作内部和操作之间的并行处理操作内部和操作之间的并行处理操作内部和操作之间的并行处理操作内部和操作之间的并行处理 根据数据的分布,分配给每个并行服务器进程的处理器 以及响应并行服务器数据请求的设备的速度 都有所不同,每个并行查询服务器进程完成任务的时间可能是不同的。当每一个服务器进程完成工作时, 它将结果传递给 在语句层次结构中的下一级操作。任何一个并行服务器进程都可能处理或服务于在语句层 次结构中上一级的并行执行服务器传递过来的语句操作请求。 技巧: 分配给语句的任何服务器进程都可以处理从同一个语句内的进程提交的请求。所以,如 果一些进程运行得比其他进程快, 则当运行较慢的进程可以提供并行执行进程的子集生 成的行后(但只限在上一级语句层次结构上),那么运行较快的进程就可以使用这些行。 优化器将评估一个语句并决定在它执行时应使用多少个并行查询服务器进程。操作内部的并行化处理 与操作之间的并行化处理是不同的。操作内部的并行化处理是将处理一个 SQL 语句的单一任务再进行划分, 例如使用并行执行服务器来读取一张表。当一个 SQL 语句中的多个部分并行执行时,一部分并行执行服务 器将结果传递给另外一部分并行执行服务器。这就是所说的操作之间的并行化处理。 并行度可应用于 SQL 语句中每一个可以并行化处理的操作,包括通过 ORDER BY 子句要求对数据进行 排序的操作。如图 11-2 所示,一个并行度为 4 的查询可以请求到 9 个进程。 254 使用操作内部和操作之间的并行处理示例使用操作内部和操作之间的并行处理示例使用操作内部和操作之间的并行处理示例使用操作内部和操作之间的并行处理示例(PARALLEL(PARALLEL(PARALLEL(PARALLEL 和和和和 NOPARALLELNOPARALLELNOPARALLELNOPARALLEL 提示提示提示提示)))) 可以使用 SQL 提示,或者通过为表或索引声明的对象级的选项来将 SQL 语句进行并行化处理。下面的 程序清单展示了语句提示的使用。 select /*+ parallel (ORDER_LINE_ITEMS) */ Invoice_Number, Invoice_Date from ORDER_LINE_ITEMS order by Invoice_Date; 前面一个语句没有定义并行度,所以此处将使用由表的定义或者将要使用的初始化参数指定的默认的 并行度。当创建表时,可以指定该表可以使用的并行度,如下面的程序清单所示: create table ORDER_LINE_ITEMS (Invoice_Number NUMBER(12) not null, Invoice_Date DATE not null) parallel 4; 当执行对 ORDER_LINE_ITEMS 表的查询时没有为查询指定并行度,Oracle 使用 4 作为并行度的默认值。 要重写默认值,可以使用 PARALLEL 提示来指定一个新的值,如下面程序清单所示。该程序清单还显示 PARALLEL_INDEX 提示, 它与 PARALLEL 提示的唯一不同是指定了索引名。 select /*+ parallel (ORDER_LINE_ITEMS, 6) */ Invoice_Number, Invoice_Date from ORDER_LINE_ITEMS order by Invoice_Date; select /*+ parallel_index (ORDER_LINE_ITEMS, invoice_number_idx, 6) */ Invoice_Number, Invoice_Date From ORDER_LINE_ITEMS where Invoice_Number = 777 order by Invoice_Date; 上面的程序清单指定并行度为 6。最多将分配或创建 13 个并行执行服务器来满足这个查询。 为了简化这个提示语法,可以使用表的别名,如下面的程序清单所示。如果为表指定了别名,在提示 中必须使用别名,而不是表的名称。 select /*+ parallel (oli, 4) */ Invoice_Number, Invoice_Date from ORDER_LINE_ITEMS oli order by Invoice_Date; 技巧: 可通过 PARALLEL 提示打开并行操作。如果使用了 PARALLEL 提示,但是没有在该提示或 者在表中设置并行度,查询仍然将按照并行化处理,但DOP 要通过初始化参数 CPU_COUNT 和 PARALLEL _THREADS _PER_CPU 计算得到。 也可以“关闭”对一张表上的给定查询中的并行操作,即使该查询已经被指定使用并行操作。 255 ORDER_LINE_ITEMS 表的默认并行度是 4,但下面程序清单中的查询通过 NO_PARALLEL 提示改写了该设置: select /*+ no_parallel (oli) */ Invoice_Number, Invoice_Date from ORDER_LINE_ITEMS oli order by Invoice_Date; 技巧: 使用 NO_PARALLEL 提示将禁用语句的并行操作,即使该语句根据并行对象的定义本应当 是使用并行处理的。 若需要修改一张表默认的并行度,可以使用 alter table 命令的 PARALLEL 子句,如下面的程序清单 所示: alter table order_line_items parallel (degree 4); 若需要禁用一张表的并行操作,可以使用 alter table 命令的 NO_PARALLEL 子句,如下面的程序清单 所示: alter table order_line_items no_parallel; 协调进程根据以下条件评估是否要并行执行相应语句: ● SQL 语句中包含的提示 ● 通过修改会话强制执行并行命令来设置会话值 ● 定义为并行处理的表/索引和表/索引的统计 建议在 SQL 语句本身或者表的定义中明确指定并行度。对大多数操作,可以直接使用并行度的默认值, 但为了对时间敏感的操作的性能管理,应当使用提示指定一个并行度。 技巧: 使用提示而不是依赖于表的定义指定并行度,这样可以确保给定查询的所有操作都得到 了性能调整。 11.711.711.711.7 使用并行操作生成表和索引的示例使用并行操作生成表和索引的示例使用并行操作生成表和索引的示例使用并行操作生成表和索引的示例 为进一步解释 SQL 语句中并行操作的应用,请考虑下面程序清单所示的针对表和索引创建任务的并行 操作的实现方法。 在创建表时使用并行操作: create table ORDER_LINE_ITEMS tablespace tbsp1 storage (initial 75m next 75m pctincrease 0) parallel (degree 4) 256 as select /*+ parallel (OLD_ORDER_LINE_ITEMS,4) */ * from OLD_ORDER_LINE_ITEMS; 在创建索引时使用并行操作: create index ORDER_KEY on ORDER_LINE_ITEMS (Order_Id, Item_Id) tablespace idx1 storage (initial 10m next 1m pctincrease 0) parallel (degree 5) NOLOGGING; CREATE INDEX 语句创建了使用并行排序操作的 ORDER_KEY 索引。CREATE TABLE 语句从一张已经存在 的 OLD_ORDER_LINE_ITEMS 表中,使用并行操作选取并创建了一张并行度为 4 的新表 ORDER_LINE_ITEMS。 在前面的表创建程序清单中 ,CREATE TABLE 命令中两个独立的操作利用了并行化的优点 : 对 OLD_ORDER_LINE_ITEMS 表的查询是并行化处理的,而对 ORDER_LINE_ITEMS 表的插入也是并行化的。 注意: 尽管并行查询增加了在修改数据时的操作性能,但会连续记录重做日志,并且造成瓶颈。 使用在 Oracle 8 中引入的 NOLOGGING 选项,可以在创建表和索引时避免这个瓶颈。 因为对重做日志文件的写操作是连续的,重做日志的写操作可以有效地消除您为语句定义的并行化处 理。使用 NOLOGGING 可以强制执行批量操作,以避免记录日志,但单独的 INSERT 命令必须仍然写入重做日 志文件。如果使用 NOLOGING 选项,您必须可以通过归档重做日志文件之外的其他方法来恢复数据。 技巧: 使用 NOLOGING 可以克服由于重做日志文件连续的写操作造成的 I/O 瓶颈。 直到目前,我们仍然忽略了示例中 SELECT 语句所需要的数据的物理位置。如果一次全表扫描的数据 全部包含在一块单一磁盘上,您唯一得到的就是在磁盘上形成一个巨大的 I/O 瓶颈。可以通过使用并行操 作来获得性能增益的一条潜在规则就是:数据是存储在不同的设备上的,它们应可以独立寻址。 不仅如此,使用 PEO 可能让您的系统的性能变得更糟糕。如果您系统还有备用的处理能力,但存在 I/O 瓶颈,则使用 PEO 将更快地产生更多的 I/O 请求,生成一个更大的队列要 I/O 系统来管理。如果您已经遇 到了 I/O 瓶颈,对这个瓶颈创建更多的进程并不会提高系统性能。您需要根据可用的 I/O 设备来重新设计 您的数据分布。 技巧: 确保您的数据分布得当,否则并行查询服务器进程可能会加剧已经存在的 I/O 瓶颈问题。 回到前面“使用并行操作创建索引”中所示的创建索引语句,考虑以下两点技巧: 257 ● 如果内存中没有足够的内存可以执行排序操作(SORT_AREA_SIZE),则创建索引时将用到临时表空 间。以下面这种方式构建临时表空间:物理数据文件至少分散分布在与 CREATE INDEX 语句中并行度数一样 多的磁盘上。 ● 当增加/启用一张表的主键或者唯一键时,您不可能在并行处理中创建相关索引。相反,应当首 先并行创建索引,然后使用 ALTER TABLE 来增加/启用约束条件,并指定 USING INDEX 子句。要使它正常工 作,索引与约束条件必须有相同的名字。 为执行高效并行操作而分布数据的实际示例 请回到 CREATE TABLE 语句的示例,如果这是一个初始时数据负载很小但数据在不断增长的数据仓库, 则可以采用下列条件/事件序列: (1) 创建的表空间(TBSP1)包含 4 个数据文件,每个文件大小为 100MB,分布在不同的磁盘上。 (2) CREATE TABLE 语句按照指定的 MINEXTENTS 4 执行,为每个扩展空间创建 75MB 的空间(在 4 块不 同的磁盘/设备上),因为扩展空间不能跨越数据文件。 (3) 表的存储定义随后分配了另外一块 25MB 的空间,用于其他较小数据量的加载/填充。 (4) 这个例子中的临时表空间定义使用了至少 4 个数据文件,以从物理结构上组成表空间。 这个方法解释了对表和临时表空间结构的合理规划和管理,可以提供必需的物理数据分布,获取并行 DDL 操作的最大性能。但如果使用 ASM 就不需要这样。 技巧: 高效的并行操作极大地依赖于数据在物理上的分布情况。应当避免在数据库中造成 I/O 瓶颈。 在 Oracle 9i 中,可以使用 Oracle 托管文件(Oracle-Maneged File,OMF)的特性来为数据库创建数 据文件。如果使用这个特性,创建的所有 OMF 数据文件都将放置在由初始化参数 DB_CREATE_FIEL_DEST 定 义的指定目录下。为避免造成 I/O 冲突,应当将这个参数指向一个分布在多个磁盘上的逻辑卷。可以在创 建 OMF 文件以后再移动它,通过命令 ALTER DATABASE 或者 ALTER TABLESPACE 调用标准过程来移动并重命 名数据文件。 11.811.811.811.8 并行并行并行并行 DMLDMLDMLDML 语句和示例语句和示例语句和示例语句和示例 Oracle 8 RDBMS 提供了并行执行 DML 操作的能力。在一个 SQL 会话中并行执行 DML 语句的操作时,必 须启用对并行 DML 的支持。下面是并行执行 DML 的条件: ● 如果您没有首先完成事务,您就不能启用一个并行 DML 会话。您必须首先执行提交或者回滚操作。 ● 必须通过命令 alter session enable parllel dml 启用会话的并行功能。 ● 直到并行事务结束之后,才可读取一张被并行 DML 修改的表(通过提交或者回滚操作)。 258 注意: 并行 DML 模式并不影响并行 DDL 或者并行查询。 下列语句阻碍了并行 DML 的执行: ● SELECT for UPDATE ● LOCK TABLE ● EXPLAIN PLAN 注意: 语句失败并不会禁用会话的并行 DML 功能。 11.8.111.8.111.8.111.8.1 并行并行并行并行 DMLDMLDMLDML 的约束条件的约束条件的约束条件的约束条件 当使用并行 DML 操作时,考虑下面的约束条件: ● UPDATE、MERGE 和 DELETE 操作在没有分区的表上不能并行化处理。 ● 当一张表正在被并行 DML 命令修改时,那么同一个事务内对同一张表上的访问未结束前,其他任 何 DML 操作或查询都无法再访问该表。您必须在这些事务之间执行一个提交或者回滚命令。 ● 如果一张表上有触发器,将不支持并行 DML 操作。同样,正在执行复制操作的表也无法执行并行 DML 操作。 ● 使用 DELETE CASCADE 删除一张有外键的表的操作也无法被并行化;如果 DML 中牵涉到主键,也 就不能删除带有延迟约束或是自引用外键的表。 ● 在对象和 LOB 列上不支持并行 DML 操作,但可以在同一张表的其他列上执行。 ● 针对群集表的 DML 操作不能作并行化处理。 ● 对远程对象的任何 INSERT/UPDATE/MERGE/DELETE 操作都不能作并行化处理。 ● 在 Oracle 9i 之前,从并行 DML 执行期间的系统故障中恢复事务是串行化执行的;但在 Oracle 9i 中,Oracle 可以在事务恢复的前滚阶段和回滚阶段均提供并行化处理。 技巧: 并行 DML 被限制用于某些特定类型的表,并且有时候只是其中的特定列。您必须管理表, 以正确地启用并行 DML 操作。 11.8.211.8.211.8.211.8.2 并行并行并行并行 DMLDMLDMLDML 语句示语句示语句示语句示例例例例 下面两个程序清单解释了并行 DML 语句的使用。第一个程序清单为会话创建了一个新的事务,并启用 了并行 DML 操作。 commit; alter session enable parallel dml; 第二个程序清单更新了表 COSTS(从 Oracle 示例中选取的一张已分区的表),并行度为 4,然后执行对 该表的查询。 259 update /*+ PARALLEL (costs, 4) */ COSTS set Unit_Price = Unit_Price * 1.15 where Prod_Id > 40000; 27041 rows updated. select COUNT(*) from COSTS; select COUNT(*) from COSTS * ERROR at line 1: ORA-12838: cannot read/modify an object after modifying it in parallel commit; Commit complete. 该查询操作将会失败,因为这张表上的并行事务还没有提交。但如果您对另一张表执行同样的 Select 操作,则不会出现这个错误。 技巧: 必须确保在使用并行 DML 语句后执行提交或回滚操作。否则,您在同一个表上的并行 DML 语句之后立即执行 SELECT 操作时将出现错误。 下面的程序清单展示了一个并行 DDL 语句。注意这个示例,两个不同的部分得到并行化处理的:查询, 并行度为 6;填充表,并行度为 4。 create table COST_SUMMARY parallel 4 as select /*+ PARALLEL (COSTS, 6) */ Prod_Id, Time_Id, SUM(Unit_Cost) Cost from COSTS group by Prod_Id, Time_Id; 如果不使用 CREATE TABLE AS SELECT 语法,可以首先创建表,然后并行化处理 INSERT 语句,如下面 的程序清单所示。APPEND 提示仅仅用于填充新的数据块,此处使用它只是为了向您展示它的语法。 insert /*+ APPEND PARALLEL (COST_SUMMARY,4) */ into COST_SUMMARY (Prod_Id, Time_Id, Cost) select /*+ PARALLEL (COSTS, 6) */ Prod_Id, Time_Id, SUM(Unit_Cost) Cost from COSTS group by Prod_Id, Time_Id; 27041 rows created. 技巧: 可以在一个 INSERT AS SELECT 语句的多个部分中使用 PARALLEL 提示。并行度为 4 的 插入操作需要将一张表的 MAXTRANS 参数至少设置为 4,并且还要求 4 个空间足够的回 滚段来处理每个事务(设置事务使用回滚段的命令不支持并行化处理)。 260 注意: 如果使用了自动撤消管理,回滚段的资源将不会出现问题。 11.911.911.911.9 通过通过通过通过 V$V$V$V$视图监控并行操作视图监控并行操作视图监控并行操作视图监控并行操作 V$动态性能视图永远是实时监控和评估数据库的当前性能的强有力工具,并行操作也不例外。在系统 级别监控并行执行的关键性能视图是 V$PQ_TQSTAT 和 V$PQ_SYSSTAT。通常情况下,以 V$PQ 开头的 V$视图 提供统计和 DBA 信息(主要是调整信息),而 V$PX 视图在进程级别提供并行会话和操作的详细信息(主要是 结构)。在下面的章节中,您将看到最常用于监视并行操作的 V$视图的示例。 11.9.111.9.111.9.111.9.1 V$PQ_TQSTAT V$PQ_TQSTAT V$PQ_TQSTAT V$PQ_TQSTAT 所有并行服务器进程的详细统计信息和它们之间生产者/消费者的关系均在 V$PQ_TQSTAT 视图得以体 现。此外还将显示每个服务器进程处理的数据行数和字节数的附加信息。DBA 将 V$PQ_TQSTAT 视图用于调 整长时间运行的查询时,可获得最佳效果,这些查询需要在服务器进程间进行特定的调整和数据分布的评 估。下面的程序清单展示了 V$PQ_TQSTAT 视图的数据示例。该视图将有助于在并行执行服务器间定位工作 负载的不均匀分布。 select DFO_Number, TQ_ID, Server_Type, Num_Rows, Bytes, Waits, Process from V$PQ_TQSTAT; DFO_NUMBER TQ_ID SERVER_TYPE NUM_ROWS BYTES WAITS TIMEOUTS PROCE ---------- ----- ----------- -------- ------- ----- -------- ----- 1 0 Consumer 14315 123660 14 0 P000 2 0 Producer 23657 232290 7 0 P003 2 0 Producer 12323 90923 7 0 P002 2 0 Producer 12321 92300 7 0 P001 2 0 Consumer 190535 2234322 48 2 QC 在这个示例中,可以看到两个并行操作的结果。第一个并行操作只牵涉到一个并行执行服务器。第二 个并行操作牵涉到三个并行执行服务器(P001,P002 和 P003)和一个协调进程 QC。对于第二个并行操作 (DFO_Number = 2),您可以看到进程 P003 的工作负载比其他进程更重。需要进行更多的测试,以决定是否 存在问题。同时应当注意到上面程序清单的输出结果的最后一行是一个查询协调进程。它有一个高于平均 水平的等待值,因为它需要和所有其他的查询服务器进程进行通信。 11.9.211.9.211.9.211.9.2 V$PQ_SYSSTAT V$PQ_SYSSTAT V$PQ_SYSSTAT V$PQ_SYSSTAT 这是当前高负载情况下正在执行的服务器数量,以及并行服务器启动和关闭频率的理想工具,如下面的程 序清单所示: select Statistic, Value from V$PQ_SYSSTAT; STATISTIC VALUE --------------------- ------- 261 Servers Busy 12 Servers Idle 0 Servers Highwater 12 Server Sessions 39 Servers Started 13 Servers Shutdown 7 Servers Cleaned Up 0 Queries Initiated 5 DML Initiated 3 DDL Initiated 0 DFO Trees 5 Sessions Active 3 Local Msgs Sent 91261 Distr Msgs Sent 0 Local Msgs Recv'd 91259 Distr Msgs Recv'd 0 技巧: 为了简单地判断是否使用了并行 DML,可以在执行一个并行 DML 语句之前和之后查询 DML 初始化统计信息。 下面的程序清单展示了一个实例的统计信息。这些统计信息解释了本章前面“并行 DML 语句和示例” 一节介绍的并行度为 4 的并行 DML 语句示例中,执行 UPDATE 语句时并行服务器的情况。 select Statistic, Value from V$PQ_SYSSTAT; STATISTIC VALUE --------------------- ------ Servers Busy 4 Servers Idle 0 Servers Highwater 4 Server Sessions 4 Servers Started 0 Servers Shutdown 0 Servers Cleaned Up 0 Queries Initiated 0 DML Initiated 1 DDL Initiated 0 DFO Trees 1 Sessions Active 1 Local Msgs Sent 8 Distr Msgs Sent 0 Local Msgs Recv'd 12 Distr Msgs Recv'd 0 262 正如您所见,该实例使用了 4 个并行执行服务器,而没有启用新的进程。随后,在执行一个并行度为 4 的 INSERT 语句操作后查询 V$PQ_SYSSTAT 视图。下面这个程序清单展示了从 V$pq_sysstat 视图查看到的 INSERT 语句后续执行的操作的统计信息。 STATISTIC VALUE --------------------- ------- Servers Busy 4 Servers Idle 0 Servers Highwater 8 Server Sessions 16 Servers Started 4 Servers Shutdown 4 Servers Cleaned Up 0 Queries Initiated 0 DML Initiated 2 DDL Initiated 0 DFO Trees 3 Sessions Active 2 Local Msgs Sent 108 Distr Msgs Sent 0 Local Msgs Recv'd 122 Distr Msgs Recv'd 0 在一张并行度为 5 的表上执行 SELECT 语句后查询 V$PQ_SYSSTA 视图。下面的程序清单展示了查询后 V$PQ_SYSSTAt 视图的输出结果。注意 Servers Busy 和 Servers Highwater 选项的值。 select Statistic, Value from V$PQ_SYSSTAT; STATISTIC VALUE --------------------- ------ Servers Busy 5 Servers Idle 0 Servers Highwater 8 Server Sessions 20 Servers Started 5 Servers Shutdown 4 Servers Cleaned Up 0 Queries Initiated 1 DML Initiated 2 DDL Initiated 0 DFO Trees 4 Sessions Active 2 Local Msgs Sent 117 Distr Msgs Sent 0 Local Msgs Recv'd 136 Distr Msgs Recv'd 0 263 在这个示例中,提示重写了定义表时默认的并行度,使用了 5 个并行查询服务器进程。 技巧: 如果服务器的数量开始持续增长,就要考虑增加初始化参数 PARALLEL _MIN_ SERVERS 的值。然而,如果并行执行服务器是通过 PARALLEL _MIN_SERVERS 参数启用,它将一直 存在,直到数据库关闭、并行处理中断或者进程终止为止。这将导致进程的内存碎片, 所以只能在您确定需要的时候再增加它。 技巧: 在决定一个操作的并行度时,PARALLEL 提示会改写表定义的默认并行度。 11.9.311.9.311.9.311.9.3 V$PQ_SESSTAT V$PQ_SESSTAT V$PQ_SESSTAT V$PQ_SESSTAT 要提供当前会话的统计信息,请查询 V$PQ_SESSTAT 视图。使用这个视图可以查看当前会话中正在执 行的查询的数量,以及并行化处理的 DML 操作的数量。下面的程序清单展示了通过这个视图看到的一个简 单查询的输出结果。 select Statistic, Last_Query, Session_Total from V$PQ_SESSTAT; STATISTIC LAST_QUERY SESSION_TOTAL ----------------------- ---------- ------------- Queries Parallelized 0 1 DML Parallelized 1 2 DDL Parallelized 0 0 DFO Trees 1 3 Server Threads 6 0 Allocation Height 6 0 Allocation Width 0 0 Local Msgs Sent 27 171 Distr Msgs Sent 0 0 Local Msgs Recv'd 27 167 Distr Msgs Recv'd 0 0 V$PQ_SESSTAT 视图中的输出结果仅仅指当前的会话,所以在通过执行诊断操作测试进程或者寻求问题 的解决方案时特别有用。注意 V$PX_SESSTAT 的名称与此类似,但列的设置完全不同。V$PX_SESSTAT 视图 将 V$PX_SESSTAT 的会话信息与 V$SESSTAT 表连接。V$PX_SESSION 视图也可以提供进程请求的并行度 (req_degree)的信息,并将其与实际使用的并行度(degree)信息相比较。其他与并行操作有关的 V$视图列 表将在本章结束前的“其他并行处理的注意事项”一节中再作介绍。 下面的程序清单展示了一个查询 V$PX_SESSTAT 的简单示例。在这个示例中,如果您想执行一个并行 度为 12 的并行查询,而 PARALLEL_MAX_SERVERS 的值为 10,您将得到以下信息: Select DISTINCT Req_Degree, Degree from V$PX_SESSTAT; 264 REQ_DEGREE DEGREE ------------------- ------------------- 12 10 只有在一个并行操作正在执行时才能应用 V$PX_SESSTAT 视图;只要并行操作一结束,该视图中的内 容就清空了。 11.1011.1011.1011.10 在并行操作时使用在并行操作时使用在并行操作时使用在并行操作时使用 EXPLAINPLANEXPLAINPLANEXPLAINPLANEXPLAINPLAN 和和和和 AUTOTRACEAUTOTRACEAUTOTRACEAUTOTRACE 可以使用 EXPLAIN PLAN 命令查看调整过的并行执行语句。当您为数据库创建了一张 PLAN_TABLE 表后 (通过运行保存在 Oracle 软件根目录下/rdbms/admin 子目录中的 utlxplan.sql 脚本来创建),Oracle 提供 了相应表列,允许您查看并行化处理如何影响查询的执行路径。查询的并行化处理的相关信息保存在 PLAN_TABLE 表的 Object_Node、Other_Tag 以及 Other 列中。 技巧: 每一个新发布的 Oracle 版本都可能会往 PLAN_TABLE 中增加一些新的列。因此在每一次 升级 Oracle 内核后,您应当删除并重新创建 PLAN_TABLE 表。如果您将现有的数据库升 级到一个新的 Oracle 版本,应当删除旧的 PLAN_TABLE 表并重新执行 utlxplan.sql 脚本, 以便查看新版 PLAN_TABLE 表的所有列。也可以使用 SQL 详情页面中的 Oracle 企业管理 器查看该计划。 Object_Node 列提供了所有关于查询服务器进程的相关信息。Other 列提供有关查询服务器进程的信 息。Other_Tag 列描述了 Other 列项的功能。而 Other 列包含了一个衍生出的 SQL 语句—— 源于远程查询 或者并行查询操作。表 11-5 展示了 Other_Tag 可能的值和与它们相关的 Other 列的值。 表 11-5 PLAN_TABLE.Other_Tag 中有关并行操作的可能值 值 描 述 PARALLELCOMBINEDWITHCHILD 该操作的父操作同时执行父操作和子操作;Other 列值为 NULL PARALLEL_COMBINED_WITH_PARENT 该操作的子操作同时执行父操作和子操作;Other 列值为 NULL PARALLEL_TO_PARALLEL 并行执行 Other 列中的 SQL,结果将返回给第二组查询服 务器进程 PARALLELTOSERIAL 并行执行 Other 列中的 SQL,结果将返回给一个串行进程 (通常是查询协调进程) PARALLELFROMSERIAL SQL 操作从一个串行操作中获得数据,而输出结果是并行 的;Other 列为 NULL SERIAL SQL 语句是串行执行的(默认值);Other 列为 NULL. SERIALFROMREMOTE Other 列中的 SQL 语句在远程站点运行 265 当一个操作是并行化处理的,它将基于 ROWID 值的范围划分为多个查询服务器进程;而范围是基于表 中连续数据块的分布情况的。可以使用 Other_Tag 列来验证查询中不同操作的并行化处理情况,也可以查 看在 Other 列中的并行化查询。例如,下面的程序清单在 COMPANY 表和 SALES 表间强制执行合并连接;由 于合并连接牵涉到全表扫描和排序,所以可对多个操作进行并行化处理。可以使用 Other_Tag 列查看并行 操作之间的关系。 select /*+ FULL(company) FULL(sales) USE_MERGE(company sales)*/ COMPANY.Name, Sales.Sales_Total from COMPANY, SALES where COMPANY.Company_ID = SALES.Company_ID and SALES.Period_ID = 3; 下面的程序清单展示了 MERGE JOIN 查询的 EXPLAIN PLAN。 MERGE JOIN SORT JOIN TABLE ACCESS FULL COMPANY SORT JOIN TABLE ACCESS FULL SALES 正如计划所示,Oracle 在每一张表上执行一次全表扫描(TABLE ACCESS FULL),然后将结果排序(使用 SORT JOIN 操作),并合并结果集。上面程序清单中 PLAN_TABLE 表的查询展示了每一个操作的 Other_Tag 值。下面的程序清单中的查询生成了 EXPLAIN PLAN 列表。 select LPAD(' ',2*Level)||Operation||' '||Options ||' '||Object_Name Q_Plan, Other_Tag from PLAN_TABLE where Statement_ID = 'TEST' connect by prior ID = Parent_ID and Statement_ID = 'TEST' start with ID=1; 下面的程序清单显示了上一个程序清单中合并连接示例的查询结果。 Q_PLAN OTHER_TAG ------------------------------ ----------------------------- MERGE JOIN PARALLEL_TO_SERIAL SORT JOIN PARALLEL_COMBINED_WITH_PARENT TABLE ACCESS FULL COMPANY PARALLEL_TO_PARALLEL SORT JOIN PARALLEL_COMBINED_WITH_PARENT TABLE ACCESS FULL SALES PARALLEL_TO_PARALLEL 从上面的程序清单中,可以看到(通过 PARALLEL _TO_PARALLEL 的 Other_Tag 值)每一个 TABLE ACCESS FULL 操作都是并行化处理的,并将结果提供给了一个并行排序操作。在 PLAN _TABLE 表中每一条 266 TABLE ACCESS FULL 操作的记录都将在它们的 Other 列中保存并行查询的文本。在 Other 列中保存的关 于 TABLE ACCESS FULL 操作的值显示了表的扫描是基于 ROWID 值的范围。排序连接操作的参数为 PARALLEL_ COMBINED _ WITH _ PARENT(它们的父操作是合并连接),其 Other 列的值为 NULL。合并连接操作的参数为 PARALLE _TO _ SERIAL(合并操作是并行化处理的,输出结果提供给串行查询的协调进程),其 Other 列的 值将显示合并是如何操作的。 Object_Node 列的值显示了并行操作牵涉到的查询服务器进程的信息。下面的程序清单展示了合并连 接查询对 COMPANY 表执行 TABLE ACCESS FULL 操作时 Object_Node 和 Other 列的值。 set long 1000 select Object_Node, Other from PLAN_TABLE where Operation||' '||Options = 'TABLE ACCESS FULL' and Object_Name = 'COMPANY'; OBJECT_NODE OTHER ----------- ---------------------------------------------- :Q15000 SELECT /*+ ROWID(A1) */ A1."COMPANY_ID" C0, A1."NAME" C1 FROM "COMPANY" A1 WHERE ROWID BETWEEN :1 AND :2 如上面的程序清单所示,Object_Node 列代表了一个并行查询服务器进程(Q15000 是一个内部识别号, 在这个示例中是 Oracle 指派给进程的)。Other 列显示了 COMPANY 表受到 ROWID 值的限制。查询中的每一 个查询服务器进程根据 ROWID 值的不同范围执行全表扫描,如上面的程序清单所示。排序连接和合并连接 操作将扫描表得到的结果排序和合并(以并行的方式)。 技巧: 当在并行查询中使用EXPLAIN PLAN 时,不能仅依赖于查询与操作有关的列来查看EXPLAIN PLAN 内并行操作的信息。至少应当查询 Other_Tag 列来查看哪个操作是并行化处理的。 如果一个操作没有并行处理,而您认为它应当并行处理,就需要在查询中增加相应的提 示,为表设置一个并行度,或者检查查询服务池的大小,以确认在查询时可以使用查询 服务器进程。同样,还可能存在用户组的限制,以及 PARALLEL_ADAPTIVE_USER 和 PARALLEL_MIN_ PERCENT 的设置问题。这些都可能阻止并行化处理的执行。 Oracle 提供了另一个脚本 utlxplp.sql,存放在 Oracle 软件根目录下的/rdbms/admin 子目录中。 utlxplp.sql 脚本查询 PLAN_TABLE 表,重点是查询表内并行查询的数据。必须创建 PLAN_TABLE 表(通过 utlxplan.sql 脚本创建),并在运行 utlxplp.sql 脚本前填充该表(通过 EXPLAIN PLAN 命令)。 技巧: 要在并行操作时使用 EXPLAIN PLAN,可以使用 utlxplp.sql 脚本查看 PLAN_ TABLE 表的内容。 使用 set autotrace on 命令 可以在通过 SQL*Plus 执行操作时为每个事务自动生成 EXPLAIN PLAN。在使用 set autotrace on 命令 267 之后,每个查询都会显示它的执行路径和解析查询过程中牵涉到的进程的高级跟踪信息。 为了使用 set autotrace on 命令,必须先以您的账户来创建 PLAN_TABLE 表。当使用 set autotrace on 命令时,您不需要设置 Statement_ID,也不需要管理 PLAN_TABLE 表中的记录。要禁用自动跟踪功能,可 以使用 set autotrace off 命令。 如果您使用了 set autotrace on 命令,在查询完成之前您是看不到查询的 EXPLAIN PLAN,除非您指 定了 TRACEONLY 选项。EXPLAIN PLAN 命令在查询执行前首先显示执行路径。因此,如果无法获知查询的性 能,那么可以在运行查询前先运行 EXPLAIN PLAN 命令。如果您确定一个查询的性能是可以接受的,就可以 使用 set autotrace on 命令来验证它的执行路径。 下面的程序清单展示了使用 set autotrace on 命令的效果。当执行一个合并查询时,查询返回数据, 随后执行 EXPLAIN PLAN。EXPLAIN PLAN 包含两部分:第一部分展示涉及到的操作,第二部分展示与并行动 作。该程序清单展示了自动跟踪输出结果的第一部分内容 set autotrace on rem rem for this example, disable hash joins rem to force merge joins to occur. rem alter session set hash_join_enabled=FALSE; rem select *+ FULL(company) FULL(sales) USE_MERGE(company sales)*/ COMPANY.Name, Sales.Sales_Total from COMPANY, SALES where COMPANY.Company_ID = SALES.Company_ID and SALES.Period_ID = 3; Execution Plan ---------------------------------------------------------- 0 SELECT STATEMENT Optimizer=CHOOSE (Cost=10 Card=1 Bytes=59) 1 0 MERGE JOIN* (Cost=10 Card=1 Bytes=59) :Q17002 2 1 SORT* (JOIN) :Q17002 3 2 TABLE ACCESS* (FULL) OF 'COMPANY' (Cost=1 Card=1 Bytes :Q17000 =20) 4 1 SORT* (JOIN) :Q17002 5 4 TABLE ACCESS* (FULL) OF 'SALES' (Cost=1 Card=1 Bytes=3 :Q17001 9) AUTOTRACE 命令的输出结果显示了每一行的 ID 列,以及所进行的操作和操作对象。最右侧的信息 (:Q17002 等)唯一标识了查询中所使用的并行查询服务器。 合并查询示例中 AUTOTRACE 命令输出结果的第二部分中,使用了步骤 ID 值来描述执行路径操作的并 行处理情况,如下面的程序清单所示。 268 1 PARALLEL_TO_SERIAL SELECT /*+ ORDERED NO_EXPAND USE_MERGE(A2) * / A1.C1,A2.C1,A2.C2 FROM :Q17000 A1,:Q17001 A2 WHERE A1.C0=A2.C0 2 PARALLEL_COMBINED_WITH_PARENT 3 PARALLEL_TO_PARALLEL SELECT /*+ ROWID(A1) */ A1."COMPANY_ID" C0,A 1."NAME" C1 FROM "COMPANY" A1 WHERE ROWID BE TWEEN :1 AND :2 4 PARALLEL_COMBINED_WITH_PARENT 5 PARALLEL_TO_PARALLEL SELECT /*+ ROWID(A1) */ A1."COMPANY_ID" C0,A 1."SALES_TOTAL" C1,A1."PERIOD_ID" C2 FROM "S ALES" A1 WHERE ROWID BETWEEN :1 AND :2 AND A 1."PERIOD_ID"=3 上面程序清单中的第一列是步骤 ID 的值,用于让您找到它所代表的操作(根据 AUTOTRACE 命令第一部 分输出结果)。第二个值是步骤的 Other_Tag 值。第三列是步骤的 Other 值,显示了并行化处理的 SQL 语句。 同样要注意,如果仅仅设置了 autotrace on 的话,结束时仍然要列表显示统计信息。 11.1111.1111.1111.11 调整并行执行和调整并行执行和调整并行执行和调整并行执行和 Oracle 9iOracle 9iOracle 9iOracle 9i 初始化参数初始化参数初始化参数初始化参数 在一个使用并行操作的数据库中,物理内存的相关参数的设置通常比在没有使用并行操作的数据库中 的设置要高很多。表 11-6 显示了通用的参数设置,但您选择的设置应当基于您所在的商业环境。同样要注 意的是,OPTIMIZER_PERCENT_PARALLEL 参数在 Oracle 9i 中已不再有效。 技巧: 确保您的环境已经得到正确的设置,以支持并行操作产生的进程和事务的增长。 表 11-6 Oracle 10g 并行初始化参数 初始化参数 含 义 建 议 值 COMPATIBLE 设置这个参数为实例的发行版本级 别,使您可以充分利用 RDBMS 引擎 内置的所有功能。Oracle 建议在修 改该参数前备份数据库 通常情况下应设置为数据库版本 的默认值。备用数据库应当使用 对主数据库和备用数据库一致的 设置 *DB_BLOCK_SIZE 设置数据库数据块的大小 通常情况下,应在数据仓库中使 用所支持的最大值,而在 OLTP 中使用较小的尺寸。在Oracle 9i 中,可以使用不同的数据块尺寸 创建缓存和表空间 *DB_CACHE_SIZE(即以前版本的 Oracle 中 DB_ BLOCK_ BUFFERS 乘以 DB_ BLOCK SIZE 的结果) 支持大量的进程来执行并行查询和 DML 操作,增加可用的内存 增加它以支持并行操作 269 *DB_FILE_MULTIBLOCK_ READ_COUNT 决定了全表扫描中一次可以读取多 少数据块。使用表扫描将提高表扫 描时并行操作的性能 依赖于操作系统 *DISK_ASYNCH_IO 支持操作系统的异步写操作,可以 减少潜在的 I/O 瓶颈 DISK_ASYNCH_IO 是否该设置为 TRUE 取决于操作系统是否支持 异步 I/O,以及在这个平台上它 是否能稳定运行 *DML_LOCKS 设置数据库所需要的 DML 锁定的最 大数量。默认值假定每个事务平均 牵涉到 4 张表 默认值是 4* TRANSACTIONS。 增加它以支持并行 DML 操作 (续表) 初始化参数 含 义 建 议 值 *ENQUEUE_RESOURCES 指定当前数 据库中可以 锁定的不同 数据库结构 的数量。如 果使用了并 行 DML,需要 增加该值, 使其超过默 认值 默认值是根据 SESSINOS 得到的。队列资源是一个用于存储锁定 ID、 被锁定对象的信息和锁定请求的内存结构。所以,ENQUEUE_RESOURCES 的设置值减去后台进程占用的队列数量,就是您系统中可以同时激活 其他锁定的数量 *HASH_AREA_SIZE 指定用于散 列连接的最 大内存数, 以字节为单 位 如果并行查询经常用到散列连接,就增加该参数值。只有在您拥有的 空间不大于或 SORT_ AREA_SIZEPGA_AGGREGATE_TARGET 的平均值时, 才应当增加该参数,因为它的默认值来自 SORT_AREA_SIZE LARGE_POOL large pool 分配堆使用 的并行执行 作为消息缓 冲 在 Oracle 10g 中,只有 设置 SGA_ TARGET 后并 行执行才将 缓冲分配到 large pool 之外 默认值通常可行,但如果您增加了 PARALLEL_EXECUTION_ MESSAGE_SIZ E 的值,也应当为这个参数设置一个更高的值 *LOG_BUFFER 增加该值, 以支持并行 默认值是 524288,最小设为 512KB 如果频繁使用并行 DML,就需增加 该值 270 DML 生成的 事务卷 (transacti on volume) PARALLEL_ADAPTIVE_ MULTI_USER 基于活动的 并行用户数 来减少并行 度 如果需要,设置该参数为 FALSE 并通过数据库资源管理器代替它来控 制并行资源 PARALLEL_EXECUTION_ME_SSAGE_SIZE 为所有的并 行操作指定 消息的大 小。若参数 值比默认值 大,则需要 一个更大尺 寸的共享池 依赖于操作系统。值的范围为 2148~65535。若将这个参数设置成一 个更大的值,则并行操作可更好地执行,但需要更多的内存,这可能 对非并行操作或应用程序的性能产生影响 PARALLEL_MAX_SERVERS 允许同时存 在的并行查 询服务器进 程的数量 从 CPU_COUNT、PARALLEL_ THREADS_PER_CPU 和 PGA_ AGGREGATE_TARGET 的值中派生默认值 (续表) 初始化参数 含 义 建 议 值 PARALLEL_MIN_PERCENT 如果无法得到查询需要的并行度 (服务数量)的最小百分比,语句将 会中断并报错(ORA-12827)。当不希 望语句串行化执行时,该参数非常 有用 默认值为 0,范围为 0~100。如果 是 0,并行操作永远按照并行操作 执行。如果是 100,只有在可以获 得所有服务器的情况下,操作才会 并行执行 PARALLEL_MIN_SERVERS 当生成实例时可以创建的服务器的 最小数量。无论服务器是否空闲或 者中断,但服务器的数量永远不会 低于这个参数 0~O/S 的限制值。开始时多是 10~24。如果 V$视图显示并行查 询负载很重,可以考虑改变这个 值。请设置好这个参数 PARALLEL_THREADS_PER_ CPU 指定实例默认的并行度,取决于在 并行操作期间,CPU 可以支持的并行 执行进程的数量 任何非 0 整数;默认值取决于操作 系统。这里的“数字乘以 CPU 数” 是指并行操作时使用的线程数量 PGA_AGGREGATE_TARGET 在牵涉到排序和散列连接等需频繁 使用内存的 SQL 操作时,启用自动 调整 SQL 工作空间大小的功能 一个可以帮助控制页面管理的有 用参数,因为您将 PGA 的目标定位 到系统中供 Oracle 实例使用的总 内存,并减少了 SGA。它可以用于 排序操作以及第 4 章中讨论的其 他相关操作 RECOVERY_PARALLELISM 专用于恢复实例和介质的恢复进程 的数量 在 2 和 PARALLEL_MAX_ SERVERS 之间的一个值。值 0 或 1 271 表示将执行串行恢复操作 *ROLLBACK_SEGMENTS 实例的回滚段的名称 如果要频繁使用并行 DML 操作,使 用该参数可增加回滚段的数量。使 用撤消管理可能会更好。请参阅第 3 章,以获得更多的信息 *SHARED_POOL_SIZE Oracle 共享池的大小。共享池的一 部分用于查询服务器通信使用 当使用高负荷、并行 PQ 时,应当 将当前值增加 5%~10%,但只有在 Parallel_ Automatic_ Tuning (10g 中不支持)设置为 FALSE 时, 该参数才发挥作用。如果使用 SGA _TARGET 并设置正确,就不需要这 个参数 (续表) 初始化参数 含 义 建 议 值 *TAPE_ASYNCH_IO 支持操作系统的异步操作,减少潜 在的 I/O 瓶颈 该参数只影响对串行设备的写操 作。它对并行备份操作和使用 RMAN 有用,但对并行查询和 DML 操作不 重要。默认值是 TRUE *TRANSACTIONS 指定并行事务的数量,如果密集使 用并行 DML 的话,应当增加该参数 默认值从 SESSIONS 的设置得到。 增加它用来支持并行 DML 操作 *表示对并行选项有间接影响 初始化文件中的参数定义并改变了使用并行操作的环境。您可以通过在一条 SQL 语句中使用 PARALLEL 提示,或者在创建/修改表的命令中使用 PARALLE 子句来启用并行操作。当考虑调整任何并行化参数(或删 除不支持的参数)时,请仔细研究Oracle 10g Database Administrator’s Guide、数据库升级指南(Database Upgrade Guide)或适合于您系统的服务器安装指南,而不要在 Oracle 数据库上做实验。 11.1211.1211.1211.12 并行加载并行加载并行加载并行加载 要使用并行数据加载,请使用 PARALLEL 关键字来启动多个 SQL*Loader 会话。每个会话都是独立的会 话,并需要自己的控制文件。下面这个程序清单中显示了三个独立的 Direct path 加载,在命令行中均使 用了 PARALLEL=TRUE 参数。 qlldr USERID=SCOTT/PASS CONTROL=P1.CTL DIRECT=TRUE PARALLEL=TRUE sqlldr USERID=SCOTT/PASS CONTROL=P2.CTL DIRECT=TRUE PARALLEL=TRUE sqlldr USERID=SCOTT/PASS CONTROL=P3.CTL DIRECT=TRUE PARALLEL=TRUE 默认情况下,每个会话创建自己的日志,失败文件和丢弃文件(p1.log,p1.bad 等等)。您可以使用多 个会话来为不同的表加载数据,但仍然需要 APPEND 选项。APPEND 速度很快,因为它只填充未使用的数据 块。SQL*Loader 的 REPLACE、TRUNCATE、INSERT 选项不能用于并行数据加载。如果需要使用 SQL 命令删除 数据,则必须手动删除数据。 272 技巧: 如果您使用并行数据加载功能,SQL*Loader 会话不再负责维护索引,除非您加载单一表 分区。在启动并行加载进程前,您必须删除表上的所有索引,并禁用 PRIMARY 和 UNIQUE 约束条件。当完成并行加载后,您需要重新创构或者重构表的索引。对没有索引的表插 入数据时,使用 APPEND 和 UNRECOVERABLE 是最快的方法。目前外部表可以提供更快的提 取、转换和加载(ETL)操作。 在并行数据加载中,每个加载进程创建临时数据段,用于加载数据;随后临时数据段将并入到表中。如果 并行数据加载进程在加载完成前失效,临时数据段将不会和表合并。如果临时数据段没有和需要加载数据 的表合并,那么就没有数据会通过加载提交到表中。 可以使用 SQL*Loader FILE 参数来指定每一个数据加载会话使用不同的数据文件。通过指定每一个数 据加载会话使用不同的数据文件,您可以平衡加载进程的 I/O 负载。数据加载对 I/O 非常敏感,必须分布 在多个磁盘上,以使数据并行加载获得比串行加载更加显著的性能提升。 技巧: 使用 FILE 参数来导引并行数据加载产生的写操作。 在完成一个并行数据加载后,每个会话都尝试重新启用表的约束条件。只要还有一个加载会话正在执 行,尝试重新启用约束条件的工作就会失败。最后一个完成的加载会话应当尝试重新启用约束条件,并且 应该能够成功。您应当在加载工作完成后,检查约束条件的状态。如果加载数据的表有PRIMARY KEY 和 UNIQUE 的约束条件,应当首先重新创建或重建相关并行索引,再手动启用约束条件。 技巧: 数据加载的 PARALLEL 选项可以提高加载的性能,但如果使用不当,也会造成空间浪费。 11.1311.1311.1311.13 性能比较和监控并行操作性能比较和监控并行操作性能比较和监控并行操作性能比较和监控并行操作 为了显示非并行操作和并行操作间的性能差异,我们将执行以下测试: (1) 使用 12 个并行服务器进程启动数据库,并检查所创建的后台进程。 (2) 运行一个未设置 PARALLEL 参数的查询,并检查其速度。 (3) 运行一个已设置 PARALLEL 参数,并行度为 6,需要排序的查询。 (4) 检查 V$PQ_SYSSTAT 和 V$PQ_SESSTAT 的输出结果。 下面的程序清单显示了所运行的 12 个并行服务器的 ps –ef 输出(ps –ef 是 UNIX 或者 Linux 操作系 统的命令)。我们使用参数 PARALLEL_MIN_SERVERS = 12 来启动数据库。数据库的名称为 fdr1。 #ps –ef 273 oracle 2764 1 0 17:08:30 ? 0:00 ora_pmon_fdrl oracle 2766 1 0 17:08:34 ? 0:00 ora_lgwr_fdrl oracle 2768 1 0 17:08:38 ? 0:00 ora_reco_fdrl oracle 2770 1 0 17:08:42 ? 0:00 ora_d000_fdrl oracle 2769 1 0 17:08:40 ? 0:00 ora_s000_fdrl oracle 2767 1 0 17:08:36 ? 0:00 ora_smon_fdrl oracle 2771 1 4 17:08:44 ? 0:33 ora_p000_fdrl oracle 2772 1 5 17:08:46 ? 0:42 ora_p001_fdrl oracle 2773 1 4 17:08:48 ? 0:33 ora_p002_fdrl oracle 2774 1 4 17:08:50 ? 0:32 ora_p003_fdrl oracle 2775 1 5 17:08:52 ? 0:40 ora_p004_fdrl oracle 2776 1 14 17:08:54 ? 1:26 ora_p005_fdrl oracle 2819 2802 13 17:12:39 ? 1:44 ora_p006_fdrl oracle 2820 2802 1 17:12:41 ? 0:05 ora_p007_fdrl oracle 2821 2802 0 17:12:43 ? 0:01 ora_p008_fdrl oracle 2822 2802 0 17:12:45 ? 0:01 ora_p009_fdrl oracle 2825 2802 2 17:12:47 ? 0:11 ora_p010_fdrl oracle 2826 2802 10 17:12:49 ? 1:18 ora_p011_fdrl 下一步,不使用并行执行服务器来运行查询。下面的程序清单显示了部分结果集(您可以通过多种方 式来计算时间,如第 6 章所述,或者只是通过两次选择 sysdate 来计算)。 select Job_Sub_Code job, SUM(Amount_Cost), SUM(Amount_Credit), SUM(Amount_Debit) from JOB_ORDER_LINE_ITEMS group by Job_Sub_Code; JOB SUM(AMOUNT_COST) SUM(AMOUNT_CREDIT) SUM(AMOUNT_DEBIT) --- ---------------- ------------------ ----------------- 02 9834013.62 20611471.9 0 04 38670782.7 43440986.1 0 05 1252599.77 7139753.85 0 07 8899.66 0 0 12 1689729.94 3355174.16 0 14 103089.64 3287384.45 0 这个测试耗时 2 分 30 秒。 下一步,使用 PARALLEL 运行查询,如下面的程序清单所示: select /*+ PARALLEL (JOB_ORDER_LINE_ITEMS,6) */ Job_Sub_Code, SUM(Amount_Cost), SUM(Amount_Credit), SUM(Amount_Debit) from JOB_ORDER_LINE_ITEMS group by Job_Sub_Code; 在这个测试中,运行时间仅为 1 分钟。在并行度为 6 的条件下,查询速度提高了一倍。 274 技巧: 增加并行操作的并行度并不一定会减少执行的时间。它依赖于您系统的整体设置。并行 度只是指定了操作应当使用的并行执行服务器的数量。并行执行服务器的数量依赖于参 数的设定和数据库资源管理器的设置。 下面的程序清单展示了当使用 PARALLEL 参数、并行度为 12、再执行前面的查询时 V$视图的数据。 select Statistic, Value from V$PQ_SYSSTAT; STATISTIC VALUE --------------------- ----- Servers Busy 12 Servers Idle 0 Servers Highwater 12 Server Sessions 39 Servers Started 13 Servers Shutdown 7 Servers Cleaned Up 0 Queries Initiated 5 DML Initiated 0 HARD RETURNDDL Initiate 0 HARD RETURNDFO Trees 5 Local Msgs Sent 91261 Distr Msgs Sent 0 Local Msgs Recv'd 91259 Distr Msgs Recv'd 0 select * from V$PQ_SESSTAT; STATISTIC LAST_QUERY SESSION_TOTAL ------------------------- ---------- ------------- Queries Parallelized 1 4 DML Parallelized 0 0 HARD RETURNDDL Parallelized 0 0 HARD RETURNDFO Trees 1 4 Server Threads 12 0 Allocation Height 6 0 Allocation Width 1 0 Local Msgs Sent 20934 83722 HARD RETURNDistr Msgs Sent 0 0 Local Msgs Recv'd 20934 83722 Distr Msgs Recv'd 0 0 11.1411.1411.1411.14 优化优化优化优化 RACRACRACRAC 中的并行操作中的并行操作中的并行操作中的并行操作 275 对于 Oracle 数据库而言,针对首先在 7.1 版中提供的特性,使用并行操作的好处显而易见。SQL 语句 在传统的基于 Unix 的对称多处理器(SMP)体系结构上并行执行,极大地增加了服务器的利用率,提高了大 资源集中操作的运行速度。在实时应用集群(RAC)体系结构中,实现了并行 SMP 部署的等价物,同时利用集 群中所有可用的服务器(节点)。在 RAC 中使用并行操作大大增强了向外扩展集群体系结构的能力。 11.14.111.14.111.14.111.14.1 并行操作的目标并行操作的目标并行操作的目标并行操作的目标 并行实现方式的目标是使用数据库平台体系结构的所有可用资源,提高整体处理潜力。 这种部署类 型包括的资源有内存、处理器和 I/O。在所有按比例增大或单系统 SMP 图像环境中实现的并行操作,也可 以在向外扩展 RAC 集群环境中实现。包括的操作有: ● 查询(基于全表扫描) ● Create Table As ● 索引构建/重建 ● 分区表上的 DML 操作(插入、更新、删除) ● 数据加载 本列表中的前 4 个操作可以使用 SQL 提示实现,或通过设置对象级别的并行度实现。 节点组可以配 置为将并行操作限制到特定节点。因此,当实现大 RAC 体系结构(多于两个服务器)时,可以将命名服务器 分配给命名组来限制或激活并行操作。 11.14.211.14.211.14.211.14.2 RAC RAC RAC RAC 并行使用模型并行使用模型并行使用模型并行使用模型 RAC 并行执行有一些使用模型。因为将查询分解给多个节点会导致性能更差,所以在 RAC 中使用并行 查询时要非常小心! 包含的模型有: ● 标准模型 为大型数据集使用并行查询。在这种部署中,通常定义并行度来利用集群中的 所有可用资源。 ● 限制模型 这种部署将处理限制到集群中的特定节点。引用的节点可以按特定类型的操作 进行逻辑分组。 ● 并行索引构建/重建模型 在需要大索引构建的情况下,可以利用并行操作来最大限度地使 用集群节点资源。 11.14.311.14.311.14.311.14.3 初始化参数初始化参数初始化参数初始化参数 可以设置一些标准参数来执行服务器级别的并行进程,如本章前面所述。要考虑的两个通用并行参数 是: 参 数 名 称 类 型 描 述 parallel_max_servers 整数 每个实例并行进程的最大数 parallel_min_servers 整数 每个实例服务器进程的最小数 RAC 专用参数有: 参 数 名 称 类 型 描 述 Instance_groups 整数 定义激活特定服务器处理的逻辑组 276 11.14.411.14.411.14.411.14.4 查看并行统计数据的查看并行统计数据的查看并行统计数据的查看并行统计数据的 V$V$V$V$视图视图视图视图 有一些数据库视图可用来获得并行操作统计数据。这里引用的视图名称用 GV$标识符作为前缀,它描 述 RAC 级别的统计数据: 视 图 名 称 描 述 GV$PQ_SYSSTAT 用于整个 RAC 配置且与并行有关的所有统计数据 GV$PQ_SESSTAT 会话 ID 说明的会话专用并行统计数据 11.14.511.14.511.14.511.14.5 并行配置和相关基线测试并行配置和相关基线测试并行配置和相关基线测试并行配置和相关基线测试 测试环境中设置的初始化参数如下所示。本节的示例使用在 Red Hat 高级服务器下运行的两节点 RAC 体系结构。“*”表示它们作为所有 RAC 实例的全局参数。 *.parallel_max_servers=5 *.parallel_min_servers=2 下面的程序清单表示 TEST1 和 TEST2 实例在数据库启动时分别启动两个并行后台进程。 UID PID PPID C STIME TTY TIME CMD oracle 39414 1 0 11:18 ? 00:00:00 ora_p000_TEST1 oracle 39418 1 0 11:18 ? 00:00:00 ora_p001_TEST1 oracle 520 1 0 11:19 ? 00:00:00 ora_p000_TEST2 oracle 523 1 0 11:19 ? 00:00:00 ora_p001_TEST2 GV$PQ_SYSSTAT 表查询显示 Oracle 内核内并行进程的基本状态。 SELECT inst_id,statistic,value FROM gv$pq_sysstat WHERE value > 0 order by 1, 2; INST_ID STATISTIC VALUE ------- -------------------------------- --------- 1 Servers Busy 1 Servers Idle 1 Servers Highwater 1 Server Sessions 1 2 Servers Busy 1 Servers Idle 1 Servers Highwater 1 Server Sessions 1 11.14.611.14.611.14.611.14.6 并行查询测试示例并行查询测试示例并行查询测试示例并行查询测试示例 本节将使用前一节引用的两节点 RAC 体系结构讨论并行查询的用法。 执行的两个测试是: ● 无限制测试,其中使用两个 RAC 节点执行查询。 ● 限制测试,其中将查询限制到单个 RAC 节点。 277 1.测试 1:无限制测试 在无限制测试中,利用具有并行提示的标准 SQL 运行简单查询。对于任何查询而言,要利用并行操作, 全表扫描必须是语句的一部分。 Select /*+ full(c_test) parallel(c_test,6) */ sum(s_test_quantity) testcnt from q_test; 使用请求 6 个并行进程的并行提示执行查询时,在每个服务器节点上分别启动了 3 个进程。 UID PID PPID C STIME TTY TIME CMD oracle 15888 1 0 11:13 ? 00:00:03 ora_p000_TEST1 oracle 15879 1 0 11:13 ? 00:00:03 ora_p001_TEST1 oracle 15956 1 1 11:23 ? 00:00:02 ora_p002_TEST1 oracle 17811 1 0 11:23 ? 00:00:01 ora_p000_TEST2 oracle 17620 1 0 11:22 ? 00:00:01 ora_p001_TEST2 oracle 17621 1 3 11:24 ? 00:00:01 ora_p002_TEST2 从 GV$PQ_SYSSTAT 视图获得的统计数据显示,每个实例都启动了其他服务器。 INST_ID STATISTIC VALUE ---------- ------------------------------ ---------- 1 DFO Trees 7 Distr Msgs Recv'd 80 Distr Msgs Sent 80 Local Msgs Recv'd 204 Local Msgs Sent 116 Queries Initiated 6 Server Sessions 10 Servers Busy 1 Servers Highwater 3 Servers Idle 1 Servers Shutdown 1 Servers Started 1 Sessions Active 1 2 Distr Msgs Recv'd 12 Distr Msgs Sent 6 Server Sessions 6 Servers Busy 1 Servers Highwater 3 Servers Idle 1 Servers Shutdown 1 Servers Started 1 2.测试 2:限制测试 要将并行处理限制到特定集群节点,就要部署实例组来创建逻辑服务器分组。通过 INIT.ORA 的参数 INSTANCE_GROUPS 控制。 INSTANCE_GROUPS 是与 RAC 相关的参数,它专用于并行模式。与与运行时参数 278 PARALLEL_INSTANCE_GROUP 结合使用,就可以将并行查询操作限制到数量有限的实例。对于本节的测试而 言,将使用如下标识的 INSTANCE _GROUPS: Init.ora Parameter Setting for Parallel Options SALES1.INSTANCE_GROUPS='test1' SALES2.INSTANCE_GROUPS='test2' 在执行查询之前,将下面的会话改变为分配到 FINANCE 组。即使在 test1 节点上启动查询,依据 INSTANCE_ GROUP 设置,所有处理都会在 test2 上执行。 alter session set parallel_instance_group = 'test2'; select /*+ full(q_amount) parallel(q_stock,6) */ sum(q_quant) ocnt from q_stock; 对于前面的程序清单请注意,请求的所有并行进程的确只在 test2 节点上运行,因为 test1 上的进程 没有可用的 CPU 时间。 UID PID PPID C STIME TTY TIME CMD oracle 29994 1 0 14:13 ? 00:00:00 ora_p000_TEST1 oracle 29996 1 0 14:13 ? 00:00:00 ora_p001_TEST1 oracle 2631 1 0 14:51 ? 00:00:01 ora_p000_TEST2 oracle 2633 1 0 14:51 ? 00:00:01 ora_p001_TEST2 oracle 2676 1 4 14:57 ? 00:00:01 ora_p002_TEST2 oracle 2678 1 3 14:57 ? 00:00:01 ora_p003_TEST2 oracle 2680 1 4 14:57 ? 00:00:01 ora_p004_TEST2 GV$PQ_SYSSTAT 表查询也显示其他 3 个服务器在第二个 test2 实例上启动。为什么只有 3 个服务器而 不是 4 个服务器? 请记住 INIT.ORA 参数 parallel_max_servers 的设置。参数的值是 5,因此只有其他 3 个加上最初的两个(虽然高潮标记的确达到 6 个)。 INST_ID STATISTIC VALUE ---------- ------------------------------ ---------- 1 DFO Trees 3 Distr Msgs Recv'd 74 Distr Msgs Sent 74 Local Msgs Recv'd 2 Local Msgs Sent 1 Queries Initiated 3 Server Sessions 1 Servers Busy 1 Servers Highwater 1 Servers Idle 1 Sessions Active 2 2 Distr Msgs Recv'd 22 Distr Msgs Sent 11 Server Sessions 11 Servers Busy 6 279 Servers Highwater 6 Servers Started 3 在前面的示例中,我们使用 TEST_10 实例组将查询限制到测试节点。 下面的 INIT.ORA 示例允许 TEST_10 实例组既能在 test1 又能在 test2 节点上运行。注意,必须显式输入每个组的 INIT.ORA 参数 INSTANCE_GROUPS。 # Init.ora Parameter Setting for Parallel Options TEST1.instance_groups='TEST_20' TEST1.instance_groups='TEST_10' TEST2.instance_groups='TEST_10' 11.14.7 11.14.7 11.14.7 11.14.7 Create Table AsCreate Table AsCreate Table AsCreate Table As Oracle 内的 Create Table As(CTAS)特性对于复制表对象非常有用。对于大表而言,操作可以使用与 前一节并行查询示例相同的方式并行执行。下面的 SQL 语句是使用具有并行选项的 CATS 的示例。也可以使 用实例组将处理限制到特定节点。因此,依据 INSTANCE_GROUPS 参数,查询执行只在 TEST1 节点上实现。 alter session set parallel_instance_group = 'TEST_20'; create table c_district_backup parallel (degree 3) as select * from c_district; 11.14.811.14.811.14.811.14.8 索引构建索引构建索引构建索引构建 为大表创建或重建索引是另一种资源集中的操作,其中使用并行操作可以极大地提高性能。索引创建 语句要求操作的并行度为 6。 与前面的示例类似,这个操作也可以利用 INSTANCE_GROUPS 参数将操作限制到特定节点。 alter session set parallel_instance_group = 'TEST_20'; create unique index C_STOCK_I1 on C_STOCK (s_i_id, s_w_id) tablespace stock_indx parallel (degree 6); 11.14.911.14.911.14.911.14.9 性能考虑因素和小结性能考虑因素和小结性能考虑因素和小结性能考虑因素和小结 并行操作的不利方面是大量消耗服务器资源。要监控的最简单的服务器资源是 CPU 利用率。如果正常 CPU 利用率相对较高,部署大量并行进程就不明智。超过 CPU 的总数也会导致性能退化。数据布局是另一 个亟待考虑的因素。如果当前存在 I/O 瓶颈,使用并行操作会恶化这种状况。要确保并行目标对象的数据 文件适当分散在磁盘上。 使用 RAC 部署内的并行操作可以灵活利用集群结构体系内包含的所有服务器硬件。利用实例组,数据 库管理员可以依据应用程序要求或服务级协议,进一步控制这些资源的分配。 11.1511.1511.1511.15 使用并行处理时的其他注意事项使用并行处理时的其他注意事项使用并行处理时的其他注意事项使用并行处理时的其他注意事项 280 计划(重新设计)数据文件的物理分布是成功并行访问数据的关键。为每一个 SQL 语 句决定一个合适 的并行度,并在创建物理设计时就开始考虑并行化问题。不要依赖于初始化参数来确定并行度。记住,您 经常优化的只是少量的慢速查询,而不是每 一个对表的访问操作。采用稳妥的参数值进行实验,对表或索 引进行并行操作,并利用提示标识最优化的并行度。使用合适的并行提示的语法,否则它们将被忽略。 其 他可能对您有帮助的视图包括:V$px_session(执行并行操作的会话)、V$px_sesstat(执行并行操作的会话 的 统 计 数 据) 、V$px_process( 并 行 进程)、V$px_process_sysstat(并行执行服务器的统计数据)、 V$sesstat(用户会话统计数据)、V$filestat(文件 I/O 统计数据)、V$parameter(init.ora 参数)和 V$pq_tqstat(并行执行服务器的工作负载统计数据)。 在针对特定的目标时,Oracle 提供的并行特性是一项极其强大的工具——大多数数据库可以通过调整 将索引保持在合适的数量和位置上,以得到可以接受的性能。在这些语句中使用并行操作,可以扫描一整 张大表或者定位一张已分区的大表/索引。并行化处理是管理数据仓库或者执行定期维护活动的强大工具。 数据库环境必须经过相关配置,以便可充分利用并行化处理带来的好处。 Oracle 文档联机 所有 Oracle 产品的文档(多个 Oracle 版本)请参阅 http: // tahiti .oracle.com 11.111.111.111.16666 技巧回顾技巧回顾技巧回顾技巧回顾 ● RAC 是下一代关系数据库体系结构。好好学习它吧! ● 可以使用 V$SESSION_WAIT、Statspack 或 AWR Report 查找 RAC 等待事件。 ● 可以使用企业管理器网格控制查找互连问题。 ● OEM 中的数据库或集群性能屏幕(Cluster Performance Screen)是查找系统中性能问题的最快方 法。 ● 可以研究企业管理器中的全局缓存(Global Cache)等待事件。 ● 并行处理一般都牵涉到磁盘访问。如果数据不是分布在多个磁盘上,使用并行操作将会造成 I/O 瓶颈。 ● 当并行度设为 N,那么并行操作总共就需要(2*N)+1 个进程。尽管并行操作是处理进程而不是处 理器,但当有大量的处理器可以使用时,Oracle 将使用其他的处理器来运行并行查询,这通常将增强查询 的性能。 ● 在很小的表或者非常快速的查询中使用并行操作同样也会影响性能,因为协调查询也会消耗性能 资源。您应当评估并行的代价是否会超过非并行的代价。 ● 在 Oracle 9i 中,并行处理的 INSERT 不必仅针对已分区的表上。 ● 在 Oracle 9i 中,取消了内区并行处理的限制。 ● 通过使用 PARALLEL 提示启用并行操作。如果没有使用该提示设置并行度,将使用在创建表时默 认的并行度,或者根据初始化参数计算得到。 ● 使用 NO_PARALLEL 提示将禁用一个语句的并行操作,即使该语句根据并行对象的定义本应当使用 并行处理。 ● 使用提示指定并行度,而不应依赖于表的定义,可以确保给定查询的所有操作都得到了性能调整。 ● 高效的并行操作极大地依赖于数据的物理分布情况。应当避免在数据库中造成 I/O 瓶颈。 ● 为了使用并行 DML,您必须先启动一个并行 DML 会话。 ● 语句失败并不会禁用会话的并行 DML 功能。 ● 并行 DML 被限制用于某些特定类型的表——有时只是表包含的特定列。您必须管理您的表以正确 地启用并行 DML 操作。 281 ● 必须确保在使用并行 DML 语句后执行提交或回滚操作。否则,若在同一张表上的并行 DML 语句之 后立即执行 SELECT 操作时将出现错误。 ● 您可以在一个 INSERT…AS SELECT 语句的多个部分中使用 PARALLEL 提示。 ● 如果服务器的数量开始持续增长,就要考虑增加初始化参数 PARALLEL_MIN _SERVERS 的值。然而, 如果一个并行执行服务器是通过 PARALLEL _MIN _SERVERS 参数启用的,它将一直存在到数据库关闭、并行 处理中断、或者进程终止为止。这将导致进程的内存碎片,所以只能在您确定需要的时候再增加它。 ● 当确定操作的并行度时,PARALLEL 提示将重写并行对象的定义。该提示中指定的并行度将应用 于操作中所有可以并行化执行的语句。 ● 每个新版本的 Oracle 都会往 PLAN_TABLE 中增加一些新的列。因此在每次升级 Oracle 内核后, 都应当删除并重新创建 PLAN_TABLE 表。如果您将现有的数据库升级到新版本的 Oracle,应当删除旧的 PLAN_TABLE 表并重新执行 utlxplan.sql 脚本,以便查看新版 PLAN_TABLE 表的所有列。 ● 当在并行查询中使用 EXPLAIN PLAN 命令时,您不能仅依赖于查询与操作有关的列来查看 EXPLAIN PLAN 内并行操作的信息。您至少应查询 Other_Tag 列来查看哪个操作是并行化处理的。如果一个操作没有 并行处理,而您认为它应当是并行处理的,则需要在查询中增加相应的提示,为表设置一个并行度,或者 检查其他限制并行资源的因素。 ● 可以在执行 EXPLAIN PLAN 后,使用 utlxplp.sql 脚本查询 PLAN_TABLE 表中与并行处理相关的列。 ● 确保环境已经经过正确的设置,以支持并行操作产生的进程和事务的增长。 ● 如果您使用并行数据加载,SQL*Loader 会话不再维护索引。在启动一个并行加载进程前,您必 须删除表上的所有索引,并禁用 PRIMARY 和 UNIQUE 约束条件。当并行加载完成后,您需要重新创建或者重 构表的索引。 ● 使用 FILE 参数来引导并行数据加载产生的写操作。 ● 用于数据加载的 PARALLEL 选项可以提高加载的性能,但如果使用不当,也会造成空间浪费。 ● 增加并行操作的并行度不一定会减少执行的时间。它依赖于您的系统的整体设置。并行度只是指 定了操作应当使用的并行执行服务器的数量。并行执行服务器的数量取决于参数和数据库资源管理器的设 置。登录 http://tahiti.oracle.com 可以快速连接到 Oracle 文档。 11.1711.1711.1711.17 参考文档参考文档参考文档参考文档 Rich Niemiec, Oracle RAC Tuning (Collaborate and Oracle World Conference Paper) Oracle Server Tuning (Oracle Corporation) Oracle Server Concepts (Oracle Corporation) Oracle Server Reference (Oracle Corporation) Oracle Data Warehousing Guide (Oracle Corporation) Jake Van der Vort, Oracle Parallel Query (TUSC) 第第第第 12121212 章章章章 V$V$V$V$视图视图视图视图((((针对开发人员和针对开发人员和针对开发人员和针对开发人员和 DBA)DBA)DBA)DBA) 高级 DBA 经常告诉刚刚入行的 DBA,在Oracle 6年代,他们曾经将每一张 V$视图烂熟于心。在Oracle 6 中,仅仅只有 23 个 V$视图,那时侯的 DBA 可以很轻松地使用它们。而在 Oracle 9i 中,有 259 个 V$视 图以及近 400 个 X$表;现在 Oracle 10gR2 (10.2.0.1.0) 有 372 个 V$视图和 613 个 X$表。 几乎所有的出色调整或者 DBA 产品都有一个共性。它们中的大多数都是通过访问 V$视图来获取从数据 库、单个查询、或者单个用户检索出来的内部信息。通过 Joe Trezzo 和其他 V$宗师们大量的介绍,访问 282 V$视图已经变得越来越普遍。只有在看过 V$视图之后,您才能体会之前的欠缺。V$视图可以全面、准确地 展示 Oracle 数据库的核心信息。它是将普通水平的管理人员变为 DBA 专家的纽带。 第 13 章将更全面地介绍 X$表,该表是 V$视图的底层部分。附录 B 和 C 提供了 V$视图的相关信息,以 及 X$表的创建脚本。遗憾的是,由于篇幅所限,我不能展示每一个 V$脚本,我也不想重复其他章节已经深 入讨论过的内容。请查看我们的网站(www.tusc.com)来获得最新的可使用的 V$脚本。 本章主要内容: ● 创建 V$视图并设置其访问权限 ● 获得所有 V$视图的列表 ● 获得组成 V$视图的 X$脚本的列表 ● 检查组成 DBA_视图的底层对象 ● 查询 V$DATABASE,以获得数据库的创建时间和归档信息 ● 了解自动工作量仓库(Automatic Workload Repository,简称 AWR) ● 查询 V$LICENSE,以查看许可限制和警告设置 ● 访问 V$OPTIONS,以查看所有已经安装的选项 ● 查询 V$SGA 来分配 Oracle 的基本内存 ● 查询 V$SGSSTAT 来详细分配 Oracle 的内存 ● 在 V$PARAMETER 中查找 init.ora 的设置 ● 测定数据的命中率(V$SYSSTAT) ● 测定数据字典的命中率(V$ROWCACHE) ● 测定共享 SQL 和 PL/SQL 的命中率(V$LIBRARYCACHE) ● 识别哪个对象需要固定,以及是否有连续的空闲内存(V$DB_OBJECT_CACHE) ● 通过访问 V$SQLAREA、V$SQLTEXT、V$SESSION 以及 V$SESS_IO 来查找有问题的查询 ● 检查用户的当前操作及其所使用的资源 ● 识别锁定问题并关闭相应的会话 ● 查找使用多会话的用户 ● 使用视图 V$DATAFILE、V$FILESTAT 以及 DBA_DATA_FILES 来平衡 I/O ● 检查确认是否有足够的空闲列表 ● 检查角色和特权设置 ● 使 用 V$SESSION 、 V$SESSION_WAIT 、 V$SESSION_EVENT 、 V$SESSION _WAIT _CLASS 、 V$SESSION_WAIT_HISTORY、V$SYSTEM_EVENT 和 V$SYSTEM_ WAIT_CLASS 查找等待 ● 按所符合的类别使用表分组 V$视图 12.112.112.112.1 V$ V$ V$ V$视图的创建和访问视图的创建和访问视图的创建和访问视图的创建和访问 V$视图是由 catalog.sql 脚本创建的。在 Oracle 10g 中,有将近 372 个 V$视图。实际的数量随版本 和平台的不同而不同。下面是从 Oracle 6 到 Oracle 10gR2 V$视图和 X$表的具体数目的变化: 版 本 V$ 视 图 X$ 表 6 23 (?) 7.1 72 126 (续表) 版 本 V$ 视 图 X$ 表 8.0 132 200 283 8.1 185 271 9.0 227 352 9.2 259 394 10.1 340 543 10.2 372 613 创建时均以 v_$作为这些视图的前缀。catldr.sql 脚本创建了两张视图,用于 SQL*Loader 的直接加 载的统计信息。每个 V$视图的底层视图定义(从技术角度讲,这些视图从没有被创建,它们的定义只是以 二进制形式硬编码)可以通过名为 V$FIXED_VIEW_DEFINITION 的 V$视图查看。视图是通过选取一个或多个 X$表中的信息来创建的。系统为每一个 v_$视图创建了一个可以允许用户访问的视图。用户不能访问实际 的 v$视图(他们实际上是访问 v_$视图;v$对象只对 SYS 用户可见),所以,该方法通过在一个视图上创建 另一个视图的方法提供了对这些视图的访问。然后,视图的前缀改为了 V$。最后,因为 SYS 用户拥有这些 表,每个视图上就创建了一个公共同名视图。下面的程序清单展示了一个用 catalog.sql 创建 V$视图的示 例 create or replace view gv_$datafile as select * from gv$datafile; create or replace public synonym gv$datafile for gv_$datafile; 下文详细描述了整个事件的完整步骤: (1) 当创建数据库时,根据 X$表创建 GV$视图的定义: create or replace view gv$fixed_table as select inst_id,kqftanam, kqftaobj, 'TABLE', indx from X$kqfta union all select inst_id,kqfvinam, kqfviobj, 'VIEW', 65537 from X$kqfvi union all select inst_id,kqfdtnam, kqfdtobj, 'TABLE', 65537 from X$kqfdt; (2) 执行版本特定的目录脚本: SQL> @catalog (3) 根据 V$视图创建 v_$视图: create or replace view v_$fixed_table as select * from v$fixed_table; (4) 再根据 v_$视图创建新的 V$同名视图: create or replace public synonym v$fixed_table for v_$fixed_table; 284 技巧: SYSTEM 访问的 V$视图实际上是指向 v_$视图的同名视图,而 v_$视图是以根据 X$表创建 的原始 V$视图为基础而建立的视图(最好将上面的话多读几遍)。 这些视图上唯一可以执行的操作就是 SELECT。为了用户能够访问 V$视图,必须授于用户访问底层的 v_$视图的权限。不能授于用户访问 V$视图的权限(即使是 SYS 用户): connect sys/change_on_install as sysdba Grant select on v$fixed_table to richn; ORA-02030: can only select from fixed tables/views. 尽管在尝试授权访问 V$FIXED_TABLE 时返回的错误消息反映了不正确的信息(紧接前面的代码),但 授权仍然无法实现。然而,可以授权访问 V$视图底层的 v_$视图。 要与 SYS 超级用户连接,请使用下面的语句: Connect sys/change_on_install as sysdba Connected. 要向指定用户授于访问底层视图的权限,请使用下列语句: grant select on v_$fixed_table to richn; Grant succeeded. 要以指定的用户身份连接,请使用下列语句: conn richn/tusc Connected. 使用下列的语句来访问 V$FIXED_TABLE 视图,它是根据 V_$FIXED_TABLE 创建的同名视图: select count(*) from v$fixed_table; COUNT(*) -------- 1224 尽管已经过授权,仍然不能访问 v_$fixed_table: select count(*) from v_$fixed_table; ORA-00942: table or view does not exist. 如果加上前缀 SYS,就可以访问 v_$fixed_table 了: conn richn/tusc select count(*) from SYS.v_$fixed_table; COUNT(*) -------- 285 1224 为了避免混淆,最好是授于对 v_$表的访问权限,并通知 DBA 用户可访问 V$视图。通过这个方法 , 就可以授权他人访问 V$视图的信息,而不用向他们提供 SYS 或者 SYSTEM 账户的密码。关键是对他 SYS 所 有的原始 v_$视图授予 SELECT 权限。 技巧: 当其他的 DBA 需要访问 V$视图的信息,但是没有 SYS 或 SYSTEM 密码时,可以授予他们 访问 v_$视图的权限。然后这些用户就可以访问与 v_$视图具有公共同名的 V$视图。然 而,所写的脚本必须直接查询 SYS.V_$视图,以避免重引用公共同名视图造成的性能损 失。 警告: 应该仅在需要的时候才授于非 DBA 用户访问 V$视图的权限,并且要小心谨慎。记住,查 询 V$视图会造成性能损失,并且您的环境越复杂,这些损失就越多。 12.1.112.1.112.1.112.1.1 获得所有获得所有获得所有获得所有 V$V$V$V$视图的数量和列表视图的数量和列表视图的数量和列表视图的数量和列表 要获得某个版本的 Oracle 的所有 V$视图的数量,可以查询 V$FIXED_TABLE 视图。即使是同一个版本, V$视图的数量也会变化。下面的示例显示了针对 Oracle 10g 的 V$视图查询。每一个新版本 Oracle 中的 V$ 视图的数量一直在不断膨胀。下面程序清单展示了获得 V$视图数量的查询: select count(*) from v$fixed_table where name like 'V%'; COUNT(*) -------- 372 许多 V$视图仍然没有记入技术资料文档。随着视图数据的增长,在 Oracle 中探索数据的方法变得越 来越多。在 Oracle 8中,引入了 GV$视图。GV$视图(全局 V$)与 V$视图基本一样,只是附加了一个实例 ID 列。 下面的程序清单给出了 GV$视图的部分列表(仅是部分列表;完整的列表见附录 B)。 select name from v$fixed_table where name like 'GV%' order by name; NAME --------------------------------- GV$ACCESS GV$ACTIVE_INSTANCES GV$ACTIVE_SERVICES GV$ACTIVE_SESSION_HISTORY GV$ACTIVE_SESS_POOL_MTH 286 GV$ADVISOR_PROGRESS GV$ALERT_TYPES GV$AQ1 GV$ARCHIVE GV$ARCHIVED_LOG GV$ARCHIVE_DEST GV$ARCHIVE_DEST_STATUS GV$ARCHIVE_GAP GV$ARCHIVE_PROCESSES ... 技巧: 查询 V$FIXED_TABLE 视图可以获得数据库中所有的 GV$视图和 V$视图。GV$视图和 V$视 图几乎完全一样,但实例 ID 列包含了一个标识符。 12.1.212.1.212.1.212.1.2 查找用于创建查找用于创建查找用于创建查找用于创建 V$V$V$V$视图的视图的视图的视图的 X$X$X$X$表表表表 为了理解 V$视图的信息是从何处得到的,可以查询底层的 X$表(查阅第 13 章了解更详细的信息)。有 时候,查询底层的 X$表可能更有利,因为 V$视图通常根据多个 X$表连接得到。X$表非常隐秘,因为它和 Oracle 数据字典的底层表结构很相似。Oracle 在 SGA 中创建 V$视图,使用户可以以一种更便于阅读的格 式来检查 X$表中存储的信息。实际上,当在 V$视图上执行 SELECT 操作时,SELECT 操作负责从 SGA 中提取 信息—— 更明确地说,是从 X$表中提取信息。 在了解 SELECT 所操作的底层 V$视图后,您就有能力创建自定义视图;直接复制 Select 语句底层的 V$视图,然后修改或者创建一个针对 X$表的新定制的 SELECT 语句。这个技术可创建出更具选择性和更加 优化的查询。下面的程序清单用于访问对底层 X$表的查询。为了获得组成 V$视图的 X$表的列表,必须访 问 V$FIXED_TABLE_DEFINITION 视图(输出结果已经过格式设置,更便于阅读)。 select * from v$fixed_view_definition where view_name = 'GV$FIXED_TABLE'; 输出结果 VIEW_NAME VIEW_DEFINITION -------------- ------------------------------------------------ GV$FIXED_TABLE select inst_id,kqftanam, kqftaobj, 'TABLE', indx from X$kqfta union all select inst_id,kqfvinam, kqfviobj, 'VIEW', 65537 from X$kqfvi union all select inst_id,kqfdtnam, kqfdtobj, 'TABLE', 65537 from X$kqfdt 287 技巧: 访问 V$FIXED_TABLE_DEFINITION 视图可以获得组成 V$视图的底层 X$表的所有信息。 同样需要注意的是,在 Oracle 8中,底层的 X$表存在索引,以使在 V$视图上执行的查询可以更快地 执行。可以通过 V$INDEXED_FIXED_COLUMN 视图来查看在底层 X$表上的索引信息(请查阅第 13 章以了解更 详细的信息)。 12.1.312.1.312.1.312.1.3 查找组成查找组成查找组成查找组成 DBA_DBA_DBA_DBA_视图的底层对象视图的底层对象视图的底层对象视图的底层对象 有些人认为 DBA_视图也是从 X$表和/或者 V$视图得到的。它们实际上是从 Oracle 底层数据库的表中 得到的(当然也有些是通过访问 X$表得到的)。下面的程序清单通过访问 DBA_VIEWS 来查看组成 DBA_视图的 对象。 注意: 可能需要将长度设置为 2 000 000 来查看所有的输出结果。 select text from dba_views where view_name='DBA_IND_PARTITIONS’; TEXT ------------------------------------------------------------------------ select u.name, io.name, 'NO', io.subname, 0, ip.hiboundval, ip.hiboundlen SQL> set long 2000000 (RUN IT AGAIN) select text from dba_views where view_name='DBA_IND_PARTITIONS'; TEXT ------------------------------------------------------------------------ select u.name, io.name, 'NO', io.subname, 0, ip.hiboundval,ip.hiboundlen, ip.part#,decode(bitand(ip.flags, 1), 1, 'UNUSABLE', 'USABLE'), ts.name, ip.pctfree$,ip.initrans, ip.maxtrans, s.iniexts * ts.blocksize, decode(bitand(ts.flags, 3), 1, to_number(NULL), s.extsize * ts.blocksize), s.minexts,s.maxexts,decode(bitand(ts.flags,3),1,to_number(NULL),s .extpct), decode(bitand(ts.flags, 32), 32, to_number(NULL), decode(s.lists, 0, 1, s.lists)), decode(bitand(ts.flags, 32), 32, to_number(NULL), decode(s.groups, 0, 1, s.groups)), decode(mod(trunc(ip.flags / 4), 2), 0, 'YES', 'NO'), decode(bitand(ip.flags, 1024), 0, 'DISABLED', 1024, 'ENABLED', null), 288 ip.blevel, ip.leafcnt, ip.distkey, ip.lblkkey, ip.dblkkey, ip.clufac, ip.rowcnt, ip.samplesize, ip.analyzetime, decode(s.cachehint, 0, 'DEFAULT', 1, 'KEEP', 2, 'RECYCLE', NULL), decode(bitand(ip.flags, 8), 0, 'NO', 'YES'), ip.pctthres$, decode(bitand(ip.flags, 16), 0, 'NO', 'YES'),'','' from obj$ io, indpart$ ip, ts$ ts, sys.seg$ s, user$ u where io.obj# = ip.obj# and ts.ts# = ip.ts# and ip.file#=s.file# and ip.block#=s.block# and ip.ts#=s.ts# and io.owner# = u.user# union all select u.name, io.name, 'YES', io.subname, icp.subpartcnt, icp.hiboundval, icp.hiboundlen, icp.part#, 'N/A', ts.name, icp.defpctfree, icp.definitrans, icp.defmaxtrans, icp.definiexts, icp.defextsize, icp.defminexts, icp.defmaxexts, icp.defextpct, icp.deflists, icp.defgroups, decode(icp.deflogging, 0, 'NONE', 1, 'YES', 2, 'NO', 'UNKNOWN'), 'N/A', icp.blevel, icp.leafcnt, icp.distkey, icp.lblkkey, icp.dblkkey, icp.clufac, icp.rowcnt, icp.samplesize, icp.analyzetime, decode(icp.defbufpool, 0, 'DEFAULT', 1, 'KEEP', 2, 'RECYCLE', NULL), decode(bitand(icp.flags, 8), 0, 'NO', 'YES'), TO_NUMBER(NULL), decode(bitand(icp.flags, 16), 0, 'NO', 'YES'),'','' from obj$ io, indcompart$ icp, ts$ ts, user$ u where io.obj# = icp.obj# and icp.defts# = ts.ts# (+) and u.user# = io.owner# union all select u.name, io.name, 'NO', io.subname, 0, ip.hiboundval, ip.hiboundlen, ip.part#, decode(bitand(ip.flags, 1), 1, 'UNUSABLE', decode(bitand(ip.flags, 4096), 4096, 'INPROGRS', 'USABLE')), null, ip.pctfree$, ip.initrans, ip.maxtrans, 0, 0, 0, 0, 0, 0, 0, decode(mod(trunc(ip.flags / 4), 2), 0, 'YES', 'NO'), decode(bitand(ip.flags, 1024), 0, 'DISABLED', 1024, 'ENABLED', null), ip.blevel, ip.leafcnt, ip.distkey, ip.lblkkey, ip.dblkkey, ip.clufac, ip.rowcnt, ip.samplesize, ip.analyzetime, 'DEFAULT', decode(bitand(ip.flags, 8), 0, 'NO', 'YES'), ip.pctthres$, decode(bitand(ip.flags, 16), 0, 'NO', 'YES'), decode(i.type#, 9, decode(bitand(ip.flags, 8192), 8192, 'FAILED', 'VALID'),''), ipp.parameters from obj$ io, indpartv$ ip, user$ u, ind$ i, indpart_param$ ipp, tab$ t where io.obj# = ip.obj# and io.owner# = u.user# and ip.bo# = i.obj# and ip.obj# = ipp.obj# and i.bo# = t.obj# and bitand(t.trigflag, 1073741824) != 1073741824 and io.namespace = 4 and io.remoteowner IS NULL and iolinkname IS NULL 289 切勿修改底层的对象;许多 DBA 就是因为修改了这些对象而导致他们的数据库崩溃。不要执行以下代 码,但要注意它是可以执行的: Connect sys/change_on_install as sysdba Connected. DELETE FROM OBJAUTH$; -- Don’t do this! If you commit this, your database is over! 13923 rows deleted. Rollback; Rollback complete. 技巧: DBA_视图不是从 X$表或者 V$视图派生的。事实上,可以从 obj$中删除行数据这一特性 尽量不要以 SYS 超级用户的身份执行类似操作。 12.1.412.1.412.1.412.1.4 使用有帮助的使用有帮助的使用有帮助的使用有帮助的 V$V$V$V$脚本脚本脚本脚本 本章的剩余部分将致力于介绍有助于分析 Oracle 数据库的不同方面的脚本。大部分脚本是动态的, 并且能够针对需要分析的数据库的相应领域提供有价值的内部信息,以确定在一个时间点上是否有资源争 用现象。通常情况下,DBA 立即执行一些操作,通过调整查询或者增加 init.ora 参数值来减少未来资源争 用。撤消特定查询用户的访问权限,或者通过配置文件来限制其占用的系统资源,也可作为一个紧急的处 理措施。下文的三个部分包括了可以检索以下信息的脚本: ● 基本的数据库信息 ● 有关自动工作量仓库(AWR)的信息 ● 基本的许可信息 ● 数据库中已安装的数据库选项 1. 1. 1. 1. 基本的数据库信息基本的数据库信息基本的数据库信息基本的数据库信息 获得实例的基本信息通常就像登录 SQL*Plus 一样简单,因为所有的信息都显示在标题栏(banner)上。 如果想查看完整的标题栏的题头,可以访问 V$VERSION 视图来显示标题栏。下面的程序清单显示了一种快 速的方法,以查看您使用的数据库版本信息和其他相关信息: 版本信息: SQL> select * from v$version; BANNER ---------------------------------------------------------------- Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 – Prod PL/SQL Release 10.2.0.1.0 – Production CORE 10.2.0.1.0 Production TNS for 32-bit Windows: Version 10.2.0.1.0 – Production NLSRTL Version 10.2.0.1.0 – Production 数据库信息: select name, created, log_mode 290 from v$database; NAME CREATED LOG_MODE --------- --------- --------------- ORCL 03-DEC-04 ARCHIVELOG 访问 V$DATABASE 视图可以获得数据库的基本信息。输出结果中最重要的信息确保您正处在所需要的 ARCHIVELOG 模式中。查看数据库的归档日志状态的另一种方法是使用 ARCHIVE LOG LIST 命令作为 SQL*Plus 中的 SYS 用户。如下所示,输出结果还提供了数据库创建的准确日期。 技巧: 查询 V$VERSION 和 V$DATABASE 视图,可以获得数据库的基本信息,例如版本信息,以确 定创建数据库的信息以及基本的归档信息。 2. 2. 2. 2. 自动工作量仓库自动工作量仓库自动工作量仓库自动工作量仓库(AWR) (AWR) (AWR) (AWR) 的基本信息的基本信息的基本信息的基本信息 随着自动工作量仓库(AWR)的出现,需要注意许多地方。默认情况下,仓库用小时填充,保留期是 7 天。对于 AWR 而言,需要知道一些查询(MMON 后台进程用来让 AWR 数据从内存流到磁盘)。关于 AWR 的详细 信息请参考第 5 章,它依据 AWR 与 V$相关的视图以及为了调整而使用来自 AWR 的信息。 AWR 使用多少空间? Select occupant_name, occupant_desc, space_usage_kbytes from v$sysaux_occupants where occupant_name like '%AWR%'; OCCUPANT_NAME OCCUPANT_DESC SPACE_USAGE_KBYTES ------------- --------------------------------------- ------------------ SM/AWR Server Manageability - Automatic Workload Repository 44352 系统上最原始的 AWR 信息是什么? select dbms_stats.get_stats_history_availability from dual; GET_STATS_HISTORY_AVAILABILITY ------------------------------------------------------------------------ 03-NOV-06 09.30.34.291000000 AM -06:00 什么是 AWR 信息的保留期? select dbms_stats.get_stats_history_retention from dual; GET_STATS_HISTORY_RETENTION --------------------------- 31 将 AWR 信息的保留期更改为 15 天? EXEC dbms_stats.alter_stats_history_retention(15); GET_STATS_HISTORY_RETENTION 291 --------------------------- 15 技巧: 查询 V$SYSAUX_OCCUPANTS 以确保自动工作量仓库(AWR)没有占用过多空间。使用 Use DBMS_STATS 检查历史和保留。 3. 3. 3. 3. 基本的许可信息基本的许可信息基本的许可信息基本的许可信息 V$LICENSE 视图允许 DBA 监控系统内任何时候有关数据库数量的所有系统活动的数量。它提供了一个 记载任何时候最大并发会话数的日志,这就允许公司确保他们获得了正确的许可。当前会话数量与会话警 告级别和会话最大级别一起显示。会话警告级别为 0 表示没有设置 init.ora 会话警告参数,所以系统不会 显示警告信息。会话最大级别为 0 表示没有设置 init.ora 会话最大参数,所以系统不会限制会话的数量。 应该定期执行脚本,以向 DBA 提供系统一天中实际的会话数量,从而保证正确的许可授权。设置 init.ora 参 数 LICENSE_MAX_SESSIONS = 110 , 将 会 话 数 限 制 为 110 。 设 置 init.ora 参 数 LICENSE_SESSIONS_WARNING = 100,系统将向每位在第 100 个会话之后的用户显示警告信息,这样他们就 会通知 DBA,系统因遇到问题而关闭(希望能如此)。init.ora 参数 LICENSE_MAX_USERS 用于设置数据库中 可以创建的已命名的用户数。在以下程序清单中,该值为 0,所以没有限制。 select * from v$license; SESS_MAX SESS_WARNING SESS_CURRENT SESS_HIGHWATER USERS_MAX -------- ------------ ------------ -------------- --------- 110 100 44 105 0 (selected columns listed above) 技巧: 查询 V$LICENSE 视图,以查看所允许的最大会话数。也可以在接近最大数时设置警告。 4. 4. 4. 4. 数据库中已安装的产品项数据库中已安装的产品项数据库中已安装的产品项数据库中已安装的产品项 下面程序清单中的脚本描述了数据库中已经安装的产品项,以及可用状况。如果您购买了一项产品, 该产品没有显示在列表中,则可能是您没有正确地安装它。查询 V$OPTION 视图来检查已经安装的产品,或 者登录到 SQL*Plus 来检查已经安装的产品(开源数据库能做到吗?)。 select * from v$option; 为了获得以下的输出结果,需要按 PARAMETER 排序。 输出结果 PARAMETER VALUE ---------------------------------------- ------------------------- 292 Partitioning TRUE Objects TRUE Real Application Clusters FALSE Advanced replication TRUE Bit-mapped indexes TRUE Connection multiplexing TRUE Connection pooling TRUE Database queuing TRUE Incremental backup and recovery TRUE Instead-of triggers TRUE Parallel backup and recovery TRUE Parallel execution TRUE Parallel load TRUE Point-in-time tablespace recovery TRUE Fine-grained access control TRUE Proxy authentication/authorization TRUE Change Data Capture TRUE Plan Stability TRUE Online Index Build TRUE Coalesce Index TRUE Managed Standby TRUE Materialized view rewrite TRUE Materialized view warehouse refresh TRUE Database resource manager TRUE Spatial TRUE Visual Information Retrieval TRUE Export transportable tablespaces TRUE Transparent Application Failover TRUE Fast-Start Fault Recovery TRUE Sample Scan TRUE Duplexed backups TRUE Java TRUE OLAP Window Functions TRUE Block Media Recovery TRUE Fine-grained Auditing TRUE Application Role TRUE Enterprise User Security TRUE Oracle Data Guard TRUE Oracle Label Security FALSE OLAP TRUE Table compression TRUE Join index TRUE Trial Recovery TRUE Data Mining TRUE 293 Online Redefinition TRUE Streams Capture TRUE File Mapping TRUE Block Change Tracking TRUE Flashback Table TRUE Flashback Database TRUE Data Mining Scoring Engine FALSE Transparent Data Encryption FALSE Backup Encryption FALSE Unused Block Compression TRUE 54 rows selected. 上面的数据库提供了 Partitioning 选项,但它没有安装实时应用群集(Real Application Clusters, RAC)。 技巧: 查询 V$OPTION 视图,可以获取您已安装的 Oracle 产品项。V$VERSION 视图将给出已安 装的基本产品项的版本。 12.1.512.1.512.1.512.1.5 内存分配摘要内存分配摘要内存分配摘要内存分配摘要(V$SGA)(V$SGA)(V$SGA)(V$SGA) 如下所示,V$SGA 视图给出了系统的系统全局区(System Global Area,SGA)内存结构的摘要信息。Data Buffers 是在内存中分配给数据的字节数量。它根据 init.ora 的参数 DB_CACHE_SIZE 得到。Redo Buffers 主要是依据 init.ora 参数 LOG_BUFFER 计算得到,每当 COMMIT 命令提交数据时,它被用于缓存已改变的记 录并将它们保存到重做日志中。 COLUMN value FORMAT 999,999,999,999 select * from v$sga; NAME VALUE ------------------------------ ---------------- Fixed Size 734,080 Variable Size 352,321,536 Database Buffers 2,667,577,344 Redo Buffers 1,335,296 如果使用 SGA_TARGET—— 内部动态调整大小: select ( (select sum(value) from v$sga) - (select current_size from v$sga_dynamic_free_memory) ) "SGA_TARGET" from dual; SGA_TARGET ---------- 138412032 294 这个输出结果说明一个相对较大的 SGA 有超过 2.5GB 的缓冲区,该缓冲区缓存包括 DB_CACHE_SIZE、 DB_KEEP_CACHE_SIZE 和 DB_RECYCLE_CACHE_SIZE。如第 1 章和第 4 章所述,只将 SGA_TARGET 设置为 3G, 将其他参数设置为强制最小值。Variable Size 项中较为突出的部分是共享池(该 SGA 的共享池略大于 200MB)。程序清单中的 SGA 使用了大约 3GB 的实际系统的物理内存。这些信息也可以在 Statspack 报告(参 阅第 14 章)中给出,并且 SYS 超级用户 使用 SHO SGA 命令也可以显示该信息。 技巧: 访问 V$SGA 视图可以得到系统的物理内存分配的基本概念,包括在 Oracle 中为数据、共 享池、large 池、java 池以及日志缓冲区分配的内存。 12.1.612.1.612.1.612.1.6 内存分配的细节内存分配的细节内存分配的细节内存分配的细节(V$SGASTAT)(V$SGASTAT)(V$SGASTAT)(V$SGASTAT) 在 V$视图中,可以查询 V$SGASTAT 视图来提供有关 SGA 更详细的内存分配信息。这个视图提供了 SGA 和内存资源的动态信息(访问数据库时会出现相应变化)。这个语句非常详细地描述了 SGA 的尺寸。在V$SGA 和 V$SGASTAT 视图中均包含记录 FIXED_SGA、BUFFER_CACHE 和 LOG_BUFFER,且它们在这两个视图中的值均 相等。V$SGASTAT 视图中的剩余记录组成了 V$SGA 视图中唯一的其他记录(Variable Size 或 Shared Pool 记录)。 Fixed Size (V$SGA) = fixedsga (V$SGASTAT) Database Buffers (V$SGA) = buffercache (V$SGASTAT) Redo Buffers (V$SGA) = logbuffer (V$SGASTAT) Variable Size (V$SGA) = 39 Other Records (V$SGASTAT) 在 Oracle 9.2 中,V$SGASTAT 视图共有 43 个记录,如下程序清单所示 select * from v$sgastat; POOL NAME BYTES ------------ -------------------------- ---------- fixed_sga 787828 buffer_cache 16777216 log_buffer 262144 shared pool KQR L SO 76800 shared pool KQR M PO 1414752 shared pool KQR M SO 242688 shared pool KQR S PO 157508 shared pool KQR S SO 512 shared pool KTI-UNDO 1235304 shared pool sessions 781324 shared pool sql area 11719164 ...etc. 597 rows selected 在 Statspack 报告中(见第 14 章)也同样可显示这个信息,连同 Statspack 报告持续期间的开始值和 结束值一并给出。 295 技巧: 访问 V$SGASTAT 视图可获取 Oracle SGA 详细的分类列表以及共享池分配中各存储容器的 详细信息。 12.1.712.1.712.1.712.1.7 在在在在 V$PARAMETERV$PARAMETERV$PARAMETERV$PARAMETER 中发现中发现中发现中发现 init.orainit.orainit.orainit.ora 的设置的设置的设置的设置 程序清单中的脚本显示了系统中的 init.ora 参数。它还提供了有关参数的信息,确定每一个参数的 当前值是否就是默认值(ISDEFAULT=TRUE)。该脚本还显示了该参数是否可以通过 alter session 命令修改, 以及是否可以通过 alter system 命令修改(ISSYS_MODIFIABLE= IMMEDIATE)。那些可以被 alter session 和 alte system 命令修改的参数,是通过修改初始化文件然后关闭并重启实例来实现的。程序清单中的示 例(来自 Oracle 9.2)显示了可以被 alter 命令修改的部分初始化参数(IMMEDIATE 意味着它可以被修改并立 即生效)。注意,您可以使用 ALTER 命令,但对于有些参数而言,例如 o7_dictionary_accessibility,就 只能使用 ALTER SYSTEM . . . SCOPE=SPFILE 命令来修改它,然后弹出数据库让它生效。 select name, value, isdefault, isses_modifiable, issys_modifiable from v$parameter order by name; 查询 V$PARAMETER NAME VALUE ISDEFAULT ISSES ISSYS_MOD ---------------------------- ---------- --------- ----- --------- O7_DICTIONARY_ACCESSIBILITY FALSE TRUE FALSE FALSE __db_cache_size 16777216 FALSE FALSE IMMEDIATE __shared_pool_size 58720256 FALSE FALSE IMMEDIATE active_instance_count TRUE FALSE FALSE aq_tm_processes 0 TRUE FALSE IMMEDIATE archive_lag_target 0 TRUE FALSE IMMEDIATE asm_diskgroups TRUE FALSE IMMEDIATE asm_diskstring TRUE FALSE IMMEDIATE asm_power_limit 1 TRUE TRUE IMMEDIATE ...partial output listing) 依赖于版本的列也是可用的。 技巧: 查询 V$PARAMETER 视图,将得到 init.ora 参数的当前值。它还显示了哪些 init.ora 参 数已经改动了原始的默认值:ISDEFAULT = FALSE。它还显示了对于一个给定的会话,只 能修改哪些参数(当 ISSES_MODIFIABLE = TRUE 时)。最后,它显示了在不用关闭和重启 数据库可以修改哪些参数(当 ISSYS_MODIFIABLE = IMMEDIATE 时);而 ISSYS_MODIFIABLE = DEFERRED 说明该参数对所有新登录的,但当前未登录会话的用户有效。如果参数 ISSYS _MODIFIABLE =FALSE,则说明该实例必须关闭并重启,才能使设置生效。 296 12.1.812.1.812.1.812.1.8 测定数据的命中率测定数据的命中率测定数据的命中率测定数据的命中率(V$SYSSTAT)(V$SYSSTAT)(V$SYSSTAT)(V$SYSSTAT) 查询 V$SYSSTAT 视图(如下程序清单所示)可以查看从内存中读取数据的频率。它提供了数据库中设置 的数据块缓存区的命中率。这个信息可以帮助您判断系统何时需要更多的数据缓存(DB_CACHE_SIZE),或者 系统的状态何时调整得不佳(二者均将导致较低的命中率)。通常情况下,您应当确保读数据的命中率保持 在 95%以上。将系统的命中率从 98%提高到 99%,可能意味着性能提高了 100%(取决于引起磁盘读操作的语 句)。 select 1-(sum(decode(name, 'physical reads', value,0))/ (sum(decode(name, 'db block gets', value,0)) + (sum(decode(name, 'consistent gets', value,0))))) "Read Hit Ratio" from v$sysstat; Read Hit Ratio -------------- .996558641 在 Oracle 10g 中,也可以直接获得 V$SYSMETRIC 中的 AWR 信息: select metric_name, value from v$sysmetric where metric_name = 'Buffer Cache Hit Ratio'; METRIC_NAME VALUE ------------------------------------------------------ ---------- Buffer Cache Hit Ratio 100 上面程序清单中的命中率很高,但这并不意味着系统已经调整至最佳状态。很高的命中率也可能意味 着查询使用了过度的索引。如果这个命中率低于 95%,您可能需要增加 init.ora 参数 DB_CACHE_SIZE,或 者调整一些引起磁盘读取操作的查询(仅当这样做是可行的并且确实有效的情况下)。一种例外情况就是分 布在不同块中的数据分布的极不平衡。如果不考虑这种可能性,那么命中率低于 90%几乎总意味着系统调 整得很糟糕,要么就是某些人不切实际地设计,使每个数据块的数据都极不平衡。(参阅第 4 章,查看有关 命中率的其他信息)。 如果需要,也可以使用新的 V$DB_CACHE_ADVICE 视图来帮助改变数据缓存的大小。下面程序清单的查 询通过创建一个值的列表来展示较大的数据缓存和较小的数据缓存的不同效果。 column buffers_for_estimate format 999,999,999 heading 'Buffers' column estd_physical_read_factor format 999.90 heading 'Estd Phys|Read Fact' column estd_physical_reads format 999,999,999 heading 'Estd Phys| Reads' SELECT size_for_estimate, buffers_for_estimate, estd_physical_read_factor, estd_physical_reads FROM V$DB_CACHE_ADVICE WHERE name = 'DEFAULT' AND block_size = (SELECT value FROM V$PARAMETER WHERE name = 'db_block_size') 297 AND advice_status = 'ON'; Estd Phys Estd Phys SIZE_FOR_ESTIMATE Buffers Read Fact Reads ----------------- ------------ --------- ------------ 4 501 63.59 243,243,371 8 1,002 45.65 174,614,755 12 1,503 2.61 9,965,760 16 2,004 1.00 3,824,900 20 2,505 .76 2,909,026 24 3,006 .57 2,165,817 28 3,507 .41 1,555,860 32 4,008 .33 1,253,696 12.1.912.1.912.1.912.1.9 测定数据字典的命中率测定数据字典的命中率测定数据字典的命中率测定数据字典的命中率(V$ROWCACHE)(V$ROWCACHE)(V$ROWCACHE)(V$ROWCACHE) 可以使用 V$ROWCACHE 视图(如程序清单所示)来发现对数据字典的调用是否有效地利用了通过 init.ora 参数 SHARED_POOL_SIZE 分配的内存缓存。这已经在第 4 章详细讨论过。这里的唯一目标就是复 习 V$视图的访问方法。如果字典的命中率不高,系统的综合性能将大受影响。 select sum(gets), sum(getmisses),(1 - (sum(getmisses) / (sum(gets) + sum(getmisses)))) * 100 HitRate from v$rowcache; SUM(GETS) SUM(GETMISSES) HITRATE -------- ------------- ---------- 110673 670 99.3982558 在 Oracle 10g 中,也可以直接获得 V$SYSMETRIC 中的 AWR 信息: select metric_name, value from v$sysmetric where metric_name = 'Library Cache Hit Ratio'; METRIC_NAME VALUE ------------------------------------------------- ----------- Library Cache Hit Ratio 99 推荐的命中率是 95%或者更高。如果命中率低于这个百分比,说明可能需要增加 init.ora 参数 SHARED_POOL_SIZE。但要记住,在 V$SGASTAT 视图中看到的共享池包括多个部分,而这里仅仅就是其中之 一。注意:在大幅度使用公共同名的环境中,字典命中率可能难以超过 75%,即使共享池的尺寸很大。这 是因为 Oracle 必须经常检查不存在的对象是否依旧存在。 12.1.1012.1.1012.1.1012.1.10 测定共享测定共享测定共享测定共享 SQLSQLSQLSQL 和和和和 PL/SQLPL/SQLPL/SQLPL/SQL 的命中率的命中率的命中率的命中率(V$LIBRARYCACHE)(V$LIBRARYCACHE)(V$LIBRARYCACHE)(V$LIBRARYCACHE) 访问 V$LIBRARYCACHE 视图可以显示实际使用的语句(SQL 和 PL/SQL)访问内存的情况。如果 init.ora 的参数 SHARED_POOL_SIZE 设置得太小,内存中就没有足够的空间来存储所有的语句。如果共享池的碎片化 现象很严重,较大的 PL/SQL 程序就无法加载到共享池中。如果不能有效地重用语句,则扩大共享池可能事 与愿违,反而带来更多不利(参阅第 4 章的详细信息)。. 298 这里包括执行率(固定命中率)和重载命中率。推荐的固定对象的命中率是 95%以上,重载命中率应为 99%以上(低于 1%的重载次数)。如果语句先前已经经过分析,但共享池通常不够大,在分析其他的语句时 无法在内存中保存这个语句,则在此时会出现重载。语句的主体被挤出内存(语句头仍然保存下来);当需 要再次使用该语句时,就将重载记录下来,并将语句主体再次加载到内存中。这种情况也会出现在语句的 执行计划发生变动时。如果有任何一个命中率低于这些百分比,就说明应该更仔细地分析共享池的分布情 况。下面的程序清单显示了如何查询上面讨论的所有信息。 查询 v$librarycache,看看是否重用 SQL : select sum(pins) "Executions", sum(pinhits) "Hits", ((sum(pinhits) / sum(pins)) * 100) "PinHitRatio", sum(reloads) "Misses", ((sum(pins) / (sum(pins) + sum(reloads))) * 100) "RelHitRatio" from v$librarycache; Executions Hits PinHitRatio Misses RelHitRatio ---------- ---------- ----------- ---------- ----------- 7002504 6996247 99.9106462 327 99.9953305 查询 v$sql_bind_capture,看看 average binds 是否大于 15 (issue): select sql_id, count(*) bind_count from v$sql_bind_capture where child_number = 0 group by sql_id having count(*) > 20 order by count(*); SQL_ID BIND_COUNT ------------- ---------- 9qgtwh66xg6nz 21 查找有问题的 SQL 并修复它: select sql_text, users_executing, executions, users_opening, buffer_gets from v$sqlarea where sql_id = '9qgtwh66xg6nz' order by buffer_gets; SQL_TEXT ------------------------------------------------------------------------ USERS_EXECUTING EXECUTIONS USERS_OPENING BUFFER_GETS --------------- ---------- ------------- ----------- update seg$ set type#=:4,blocks=:5,extents=:6,minexts=:7, maxexts=:8, extsize =:9,e xtpct=:10,user#=:11,iniexts=:12,lists=decode(:13, 65535, NULL, :13),groups= decod e(:14, 65535, NULL, :14), cachehint=:15, hwmincr=:16, spare1=DECODE(:17,0, NULL,: 17),scanhint=:18 where ts#=:1 and file#=:2 and block#=:3 0 90 0 690 299 查询 v$sql_bind_capture,看看 average binds 是否大于 15 (issue): select avg(bind_count) AVG_NUM_BINDS from (select sql_id, count(*) bind_count from v$sql_bind_capture where child_number = 0 group by sql_id); AVG_NUM_BINDS ------------- 3.35471698 技巧: 查询 V$LIBRARYCACHE 视图可以知道从内存中访问 SQL 和 PL/SQL 的频率的信息。固定命 中率通常应该是 95%或更高,而重载的次数不应该超过 1%。查询 V$SQL_BIND_CAPTURE 视 图,看看每个 SQL 绑定是否太高,是否需要 CURSOR_SHARING。 12.1.1112.1.1112.1.1112.1.11 确定需要固定的确定需要固定的确定需要固定的确定需要固定的 PL/SQLPL/SQLPL/SQLPL/SQL 对象对象对象对象 碎片化现象造成共享池中的可用空间均成为许多零散的片段,而没有足够大的连续空间,这是共享池 中的普遍现象。消除共享池错误(参阅第 4 章和第 13 章以了解更多信息)的关键是理解哪些对象会引起问题。 一旦知道了会引起潜在问题的 PL/SQL 对象,就可以在数据库启动时固定这个代码(这时共享池是完全连续 的)。可以查询 V$DB_OBJECT_CACHE 视图,以决定哪个 PL/SQL 占用空间,而目前尚未固定下来。该查询只 显示缓存中的现有语句。下面程序清单中的示例搜索那些所需空间大于 100KB 的对象。 select name, sharable_mem from v$db_object_cache where sharable_mem > 100000 and type in ('PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'PROCEDURE') and kept = 'NO'; NAME SHARABLE_MEM ----------------------------------- ------------ MGMT_JOB_ENGINE 238590 DBMS_STATS_INTERNAL 220939 STANDARD 435820 DBMS_RCVMAN 354875 WK_CRW 183688 DBMS_BACKUP_RESTORE 258495 DBMS_STATS 446886 技巧: 通过 V$DB_OBJECT_CACHE 视图可以查看尚未固定且所需空间较大,可能会引起潜在问题 的对象。 12.1.1212.1.1212.1.1212.1.12 通过通过通过通过 VVVV$SQLAREA$SQLAREA$SQLAREA$SQLAREA 查找有问题的查询查找有问题的查询查找有问题的查询查找有问题的查询 V$SQLAREA 视图提供了一种识别有潜在问题或者需要优化的 SQL 语句的方法,从而可通过减少磁盘的 300 访问来优化数据库的综合性能。disk_reads 表示系统中正在执行的磁盘读取量。它和执行次数结合起来 (disk_reads/执行情况)就返回了 SQL 语句,构成每一条语句执行时主要的磁盘访问率。上面程序清单中的 disk_reads 设置为 100 000,但它在最终的产品系统(依赖于数据库)中也可以设置得更大或更小,以便仅 揭示系统中问题较大的语句。一旦确定问题以后,最上层的语句应当被重新检查并优化,以提高系统的综 合性能。一般情况下,问题语句都没有使用索引,或者执行路径限制语句无法使用正确的索引。 下面程序清单中的查询有一个部分可能会造成误导,即rds_exec_radio。它表示磁盘读操作的次数除 以执行过程的数目所得的值。实际上,一条语句可以一次使用 100 个磁盘读操作来读取,然后再被强制清 出内存(如果内存空间不足的话)。如果再次读取的话,那么它将再次进行 100 次磁盘读操作,而 rds_exec_radio 就是 100(100+100 次磁盘读操作,再除以 2 次执行过程)。但是,如果第二次读取它时, 该语句正在内存中(内存空间足够),那么磁盘读操作将为 0(第 2 次),所以 rds_exec_radio 将是 50(100+0 次磁盘读操作,再除以 2 次执行过程)。在结果列表顶部的任何语句都存在问题,并需要被调整——定期调 整! 注意: 下面的代码已经经过格式化处理,以方便阅读。 select b.username username, a.disk_reads reads, a.executions exec, a.disk_reads /decode (a.executions, 0, 1,a.executions) rds_exec_ratio, a.command_type, a.sql_text Statement from v$sqlarea a, dba_users b where a.parsing_user_id = b.user_id and a.disk_reads > 100000 order by a.disk_reads desc; USERNAME READS EXEC RDS_EXEC_RATIO STATEMENT -------- ------- ---- -------------- --------------------------------- ADHOC1 7281934 1 7281934 select custno, ordno from cust, orders ADHOC5 4230044 4 1057511 select ordno from orders where trunc(ordno) = 721305 ADHOC1 801715 2 499858 select custno, ordno from cust where decode(custno,1,6) = 314159 在前面语句中的 DISK_READS 列可以用 BUFFER_GETS 列来代替,以提供关于 SQL 语句的信息,该 SQL 语句拥有的磁盘访问率不高(尽管它们通常是这样),但拥有的内存访问率很高(高于所期望的值)。这些语 句使用了大量的内存来分配给数据(DB _CACHE_SIZE)。问题不在于语句是在内存中执行的(这本身是好事), 而在于该语句独占了大量的内存。许多情况下,问题出在当 SQL 语句应做全表扫描或连接操作时,它却使 用了一个索引。这种类型的 SQL 语句也可能牵涉到一个连接操作,该连接会强制使用一个并非所需的索引, 或者使用多重索引以及强制合并索引或数据。记住,大部分的系统性能问题是由于糟糕的 SQL 或 PL/SQL 语 句。 301 技巧: V$SQLAREA 视图可发现有问题的查询(和用户)。 12.1.1312.1.1312.1.1312.1.13 检查用户的当前操作及其使用的资源检查用户的当前操作及其使用的资源检查用户的当前操作及其使用的资源检查用户的当前操作及其使用的资源 将 V$SESSION 和 V$SQLTEXT 连接就可以显示目前每一个会话正在执行的 SQL 语句,如下面的程序清单 所示。这在有些时候是极为有用的,例如 DBA 希望查看某一个给定的时间点上系统究竟执行了哪些操作。 select a.sid, a.username, s.sql_text from v$session a, v$sqltext s where a.sql_address = s.address and a.sql_hash_value = s.hash_value order by a.username, a.sid, s.piece; SID USERNAME SQL_TEXT --- ---------- ------------------------------------ 11 PLSQL_USER update s_employee set salary = 10000 9 SYS select a.sid, a.username, s.sql_text 9 SYS from v$session a, v$sqltext 9 SYS where a.sql_address = s.address 9 SYS and a.sql_hash_value = s.hash_value 9 SYS order by a.username, a.sid, s.piece (...partial output listing) SQL_TEXT 列显示了整个 SQL 语句,但整个语句在 V$SQLTEXT 视图中是作为 VARCHAR2(64)数据类型来 存储的,所以跨越了多条记录。PIECE 列用于对语句排序。为了查看每个用户所使用的资源,可以直接使 用下面程序清单中的查询。这个语句的目标是显示每个会话的物理磁盘命中率和内存命中率。这就非常容 易发现哪些用户执行了大量的物理磁盘和内存读操作。 select a.username, b.block_gets, b.consistent_gets, b.physical_reads, b.block_changes, b.consistent_changes from v$session a, v$sess_io b where a.sid = b.sid order by a.username; USERNAME BLOCK_GETS CONSISTENT_GETS PHYSICAL_READS BLOCK_ CHANGES CONSISTENT_CHANGES ---------- ---------- --------------- -------------- -------------- --------- PLSQL_USER 39 72 11 53 1 SCOTT 11 53 12 0 0 SYS 14 409 26 0 0 SYSTEM 8340 10197 291 2558 419 技巧: 通过查询 V$SESSION、V$SQLTEXT 和 V$SESS_IO 可发现有问题的用户,并可发现一个给定 的时间点上他们在执行什么操作。 302 12.1.1412.1.1412.1.1412.1.14 查找用户正在访问的对象查找用户正在访问的对象查找用户正在访问的对象查找用户正在访问的对象 一旦发现某些用户或者系统中的查询存在问题,查询 V$ACCESS 可以为您指出有潜在问题的对象(可能 缺少索引)。当想修改一个特殊的对象,或者需要知道在一个给定的时间点上谁在使用该对象时,它也非常 有帮助,如下面程序清单所示。 select a.sid, a.username, b.owner, b.object, b.type from v$session a, v$access b where a.sid = b.sid; SID USERNAME OWNER OBJECT TYPE --- -------- ----- --------------------- ------- 8 SCOTT SYS DBMS_APPLICATION_INFO PACKAGE 9 SYS SYS DBMS_APPLICATION_INFO PACKAGE 9 SYS SYS X$BH TABLE 10 SYSTEM PUBLIC V$ACCESS SYNONYM 10 SYSTEM PUBLIC V$SESSION SYNONYM 10 SYSTEM SYS DBMS_APPLICATION_INFO PACKAGE 10 SYSTEM SYS V$ACCESS VIEW 10 SYSTEM SYS V$SESSION VIEW 10 SYSTEM SYS V_$ACCESS VIEW 该脚本显示了所有正被访问的对象,包括同名表、视图、已存储的源代码等。 技巧: 通过查询 V$ACCESS 视图可查看在给定的时间点上用户所访问的所有对象。这有助于查明 有问题的对象,在想修改一个特定的对象时也很有用(查找谁在访问它)。然而,当系统 有一个很大的共享池和数百个用户时,这个操作的开销将很大。 获得详细的用户信息 当 正在测试一个新的或者升版的应用程序模块以决定其开销时,分析用户的统计数据的方法是极为 有用的。当用户遇到性能问题时,它也向用户提供了一个窗口,因为 它提供的统计数据涵盖了每个用户的 各个方面。此外,在设置配置文件来限制特定用户时,也可将其作为一种指导。下面程序清单中的脚本将 统计数据限制为必须有 值的数据(b.value != 0)。注意只列出了 IMU,它只存在于 Oracle 10g 之中: select a.username, c.name, sum(b.value) value from v$session a, v$sesstat b, v$statname c where a.sid = b.sid and b.statistic# = c.statistic# and b.value != 0 group by name, username; USERNAME NAME VALUE ------------------- ------------------------- ---------- SYS DB time 3690 redo size 2143640 SYS redo size 98008 303 user calls 28 SYS user calls 337 IMU Flushes 1 SYS IMU Flushes 2 IMU commits 19 SYS IMU commits 1 redo writes 4443 redo entries 8728 ...etc. 12.1.1512.1.1512.1.1512.1.15 使用索引使用索引使用索引使用索引 Oracle 9i 提供了监控索引使用的功能。这个新的视图表示索引是否被引用,但不能反映索引使用的 频率。要监控的索引需要单独打开和关闭。可以使用 alter index 命令来初始化监控工作,然后通过对视 图 V$OBJECT_USAGE 的查询来实现索引的跟踪。下面的代码提供了 V$OBJECT_USAGE 视图的描述。 SQL> desc v$object_usage Name Null? Type --------------- -------- ---------------------------- INDEX_NAME NOT NULL VARCHAR2(30) TABLE_NAME NOT NULL VARCHAR2(30) MONITORING VARCHAR2(3) USED VARCHAR2(3) START_MONITORING VARCHAR2(19) END_MONITORING VARCHAR2(19) 在监控任何索引前,这个视图没有任何记录: select * from v$object_usage; no rows selected 开始监控 4 个索引: alter index HRDT_INDEX1 monitoring usage; alter index HRDT_INDEX2 monitoring usage; alter index HRDT_INDEX3 monitoring usage; alter index HRDT_INDEX4 monitoring usage; 现在视图显示 4 个索引的启动时间,但也同时指明它们并未被使用: select index_name, table_name, monitoring, used, start_monitoring, end_monitoring from v$object_usage; INDEX_NAME TABLE_NAME MON USE START_MONITORING END_MONITORING ----------- ---------- --- --- ------------------- ------------------ HRDT_INDEX1 HRS_DETAIL YES NO 10/13/2002 03:11:34 HRDT_INDEX2 HRS_DETAIL YES NO 10/13/2002 03:11:38 304 HRDT_INDEX3 HRS_DETAIL YES NO 10/13/2002 03:11:46 HRDT_INDEX4 HRS_DETAIL YES NO 10/13/2002 03:11:52 如果使用 HRDT_INDEX1 进行查询,视图则会显示该索引已经投入使用: select index_name, table_name, monitoring, used, start_monitoring, end_monitoring from v$object_usage; INDEX_NAME TABLE_NAME MON USE START_MONITORING END_MONITORING ----------- ---------- --- --- ------------------- ------------------ HRDT_INDEX1 HRS_DETAIL YES YES 10/13/2002 03:11:34 HRDT_INDEX2 HRS_DETAIL YES NO 10/13/2002 03:11:38 HRDT_INDEX3 HRS_DETAIL YES NO 10/13/2002 03:11:46 HRDT_INDEX4 HRS_DETAIL YES NO 10/13/2002 03:11:52 结束了对 HRDT_INDEX4 的监控,视图现在会显示监控的结束时间: alter index HRDT_INDEX4 nomonitoring usage; select index_name, table_name, monitoring, used, start_monitoring, end_monitoring from v$object_usage; INDEX_NAME TABLE_NAME MON USE START_MONITORING END_MONITORING ----------- ---------- --- --- ------------------- ------------------ HRDT_INDEX1 HRS_DETAIL YES YES 10/13/2002 03:11:34 HRDT_INDEX2 HRS_DETAIL YE NO 10/13/2002 03:11:38 HRDT_INDEX3 HRS_DETAIL YES NO 10/13/2002 03:11:46 HRDT_INDEX4 HRS_DETAIL NO NO 10/13/2002 03:11:52 10/13/2002 03:16:01 技巧: 使用 V$OBJECT_USAGE 视图来查看索引是否已被使用。也许某些索引是不需要的。 12.1.1612.1.1612.1.1612.1.16 确定锁定问题确定锁定问题确定锁定问题确定锁定问题 确定锁定问题将有助于定位正在等待其他某些用户或者某些东西的用户。可以使用这个策略来确定当 前被锁定在系统中的用户。这也使 DBA 们可以确认一个相关的 Oracle 进程是否真地被锁定了,还是仅仅运 行得比较慢。您还能够识别当前的语句是否正在执行锁定用户的操作。下面的程序清单提供了一个确定锁 定问题的示例。 注意: 在本书前一版中,这些语句没有经过调整(很令笔者尴尬)。 select /*+ ordered */ b.username, b.serial#, d.id1, a.sql_text from v$lock d, v$session b, v$sqltext a where b.lockwait = d.kaddr and a.address = b.sql_address 305 and a.hash_value = b.sql_hash_value; USERNAME SERIAL# ID1 SQL_TEXT -------- ---------- ----------- ----------------------------- AUTHUSER 53 393242 update emp set salary = 5000 您还需要识别在系统中是哪个用户造成了前一个用户被锁定的问题,如下面程序清单所示(通常情况 下,这是因为当您靠近用户/开发人员的桌子时,他/她按下了 CTRL-ALT-DEL 而引起的问题)。 select /*+ ordered */ a.serial#, a.sid, a.username, b.id1, c.sql_text from v$lock b, v$session a, v$sqltext c where b.id1 in (select /*+ ordered */ distinct e.id1 from v$lock e, v$session d where d.lockwait = e.kaddr) and a.sid = b.sid and c.hash_value = a.sql_hash_value and b.request = 0; SERIAL# SID USERNAME ID1 SQL_TEXT ------- --- -------- ------ ------------------------------------------- 18 11 JOHNSON 393242 update authuser.emp set salary=90000 在上面的程序清单中,JOHNSON 如果忘记了写至关紧要的 WHERE 子句的话,就皆大欢喜了。但是, JOHNSON 锁定了这张表的授权用户。 也可以详细查看锁定,确切了解运行和中断情况。在第 9 章,我们介绍了代码块级调整;还描述了其 中某些列,也执行了一些对 V$TRANSACTION 的查询(它显示所有最近运行的 DML [update/insert/delete] 事务)。下面的程序清单中,有 4 个在相同信息块上同时运行的事务。这里没有有中断,因为 initrans 设 置为同时处理相同代码块内的 4 个变更(至少设置为 4 ITL 槽)。如果有问题, 在下面第 3 个查询中 LMODE 应该是 0,REQUEST 应该是 6 (TX6)。 4 位用户更新相同代码块中的不同行: select /*+ ordered */ username, v$lock.sid, trunc(id1/power(2,16)) rbs, bitand(id1,to_number('ffff','xxxx'))+0 slot, id2 seq, lmode, request from v$lock, v$session where v$lock.type = 'TX' and v$lock.sid = v$session.sid; USERNAME SID RBS SLOT SEQ LMODE REQUEST -------- ------ -------- ------- ------- ------ ------- SCOTT 146 6 32 85 6 0 SCOTT 150 4 39 21557 6 0 SCOTT 151 5 34 1510 6 0 SCOTT 161 7 24 44 6 0 select xid, xidusn, xidslot, xidsqn, status, start_scn from v$transaction order by start_scn; 306 XID XIDUSN XIDSLOT XIDSQN STATUS START_SCN ---------------- ------- ------- ------- ---------------- ---------- 0600200055000000 6 32 85 ACTIVE 16573480 0400270035540000 4 39 21557 ACTIVE 16573506 05002200E6050000 5 34 1510 ACTIVE 16573545 070018002C000000 7 24 44 ACTIVE 16574420 3 位用户试图更新相同的行: select /*+ ordered */ username, v$lock.sid, trunc(id1/power(2,16)) rbs, bitand(id1,to_number('ffff','xxxx'))+0 slot, id2 seq, lmode, request from v$lock, v$session where v$lock.type = 'TX' and v$lock.sid = v$session.sid; USERNAME SID RBS SLOT SEQ LMODE REQUEST ---------- ------ ----- ------ ---------- ---------- ---------- SCOTT 146 4 47 21557 0 6 SCOTT 150 4 47 21557 6 0 SCOTT 161 4 47 21557 0 6 select xid, xidusn, xidslot, xidsqn, status, start_scn from v$transaction order by start_scn; XID XIDUSN XIDSLOT XIDSQN STATUS START_SCN ---------------- ------- -------- ---------- ---------------- ---------- 04002F0035540000 4 47 21557 ACTIVE 16575501 阻止了两个用户: SELECT sid, blocking_session, username, blocking_session_status FROM v$session WHERE username='SCOTT' ORDER BY blocking_session; SID BLOCKING_SESSION USERNAME BLOCKING_SESSION_STATUS ------- ---------------- ---------- ----------------------- 146 150 SCOTT VALID 161 150 SCOTT VALID 150 SCOTT NO HOLDER 12.1.1712.1.1712.1.1712.1.17 关闭有问题的会话关闭有问题的会话关闭有问题的会话关闭有问题的会话 一个用户可能已经运行了一些他/她也不想运行的东西,或者可能需要在工作时间终止一个有问题的 查询,到晚上再运行。如果需要终止前面一节的操作的话,可以执行下面的程序清单中的语句(用于发现并 终止会话)。 select username, sid, serial#, program, terminal from v$session; 307 alter system kill session '11,18'; You can’t kill your own session though: alter system kill session '10,4'; * ERROR at line 1: ORA-00027: cannot kill current session 以上程序代码中参数的顺序是 SID,然后是 SERIAL#。确保可以使用 V$SESSION,因为它有许多有帮助 的列。在Oralce 的前一版本中,可终止当前的用户会话。令人高兴的是,您不再会意外地终止自己的会话, 如上面的程序清单所示。 技巧: 确定锁定其他用户的用户并终止他们的会话(如果需要)。 12.1.1812.1.1812.1.1812.1.18 查找使用多会话的用户查找使用多会话的用户查找使用多会话的用户查找使用多会话的用户 有些时候,用户喜欢使用多会话来一次完成多个任务,但这会引起问题。开发人员也会有同样的问题, 如果他开发了一个创建了会派生大量进程的糟糕的应用程序。所有这些都可能降低系统的综合性能。用户 名 NULL 是后台进程。下面的程序清单中对 V$SESSION 的查询显示了这几种类型的问题: select username, count(*) from v$session group by username; USERNAME COUNT(*) ----------- -------- PLSQL_USER 1 SCOTT 1 JOHNSON 9 SYS 4 SYSTEM 1 14 在某些 O/S 平台上,如果一个用户开始了一个会话,然后重启 PC,通常当用户再次启动另一个会话时, 原有的进程仍将在后台运行。如果用户在多终端或者多台 PC 上运行多个报表,这也将影响系统的综合性能。 注意: V$SESSION 视图中用户名 NULL 的行是 Oracle 的后台进程。 技巧: 确定使用了多会话的用户,并判定是一个管理问题(用户使用了多终端)还是一个系统问 题(会话没有被清除,或者产生了多余的进程)。 查询当前的配置文件 308 配置文件被限制于一个给定的模式(用户)。要查看系统中的配置文件,请执行下面程序清单中的查询。 select substr(profile,1,10) Profile, substr(resource_name,1,30) "Resource Name", substr(limit,1,10) Limit from dba_profiles group by substr(profile,1,10), substr(resource_name,1,30), substr(limit,1,10); PROFILE Resource Name LIMIT ------- ----------------------------- ---------- DEFAULT IDLE_TIME UNLIMITED DEFAULT PRIVATE_SGA UNLIMITED DEFAULT CONNECT_TIME UNLIMITED DEFAULT CPU_PER_CALL UNLIMITED DEFAULT COMPOSITE_LIMIT UNLIMITED DEFAULT CPU_PER_SESSION UNLIMITED DEFAULT SESSIONS_PER_USER UNLIMITED DEFAULT PASSWORD_LIFE_TIME UNLIMITED DEFAULT PASSWORD_LOCK_TIME UNLIMITED DEFAULT PASSWORD_REUSE_MAX UNLIMITED DEFAULT PASSWORD_GRACE_TIME UNLIMITED DEFAULT PASSWORD_REUSE_TIME UNLIMITED DEFAULT FAILED_LOGIN_ATTEMPTS 10 DEFAULT LOGICAL_READS_PER_CALL UNLIMITED DEFAULT PASSWORD_VERIFY_FUNCTION NULL DEFAULT LOGICAL_READS_PER_SESSION UNLIMITED MONITORING IDLE_TIME DEFAULT MONITORING PRIVATE_SGA DEFAULT MONITORING CONNECT_TIME DEFAULT MONITORING CPU_PER_CALL DEFAULT MONITORING COMPOSITE_LIMIT DEFAULT MONITORING CPU_PER_SESSION DEFAULT MONITORING SESSIONS_PER_USER DEFAULT MONITORING PASSWORD_LIFE_TIME DEFAULT MONITORING PASSWORD_LOCK_TIME DEFAULT MONITORING PASSWORD_REUSE_MAX DEFAULT MONITORING PASSWORD_GRACE_TIME DEFAULT MONITORING PASSWORD_REUSE_TIME DEFAULT MONITORING FAILED_LOGIN_ATTEMPTS UNLIMITED MONITORING LOGICAL_READS_PER_CALL DEFAULT MONITORING PASSWORD_VERIFY_FUNCTION DEFAULT MONITORING LOGICAL_READS_PER_SESSION DEFAULT 32 rows selected. 309 12.1.1912.1.1912.1.1912.1.19 查找磁盘查找磁盘查找磁盘查找磁盘 I/OI/OI/OI/O 问题问题问题问题 视图 V$DATAFILE、V$FILESTAT 和 V$DBA_DATA_FILES 提供了数据库中所有数据文件和磁盘的文件 I/O 活动信息。理想情况下,物理的读和写应当平均分布。如果没有合理的配置系统,其综合性能就会受到影 响。下面程序清单中的脚本展示了实际的分布情况并可以很方便地判断出是否有不平衡的现象存在。第 3 章对这个问题进行了详细的讨论;本节只是显示快捷式查询,以了解其大致情况。 select a.file#, a.name, a.status, a.bytes, b.phyrds, b.phywrts from v$datafile a, v$filestat b where a.file# = b.file#; 下面两个程序清单中的查询提供了有关文件和数据分布问题的报告,并已改进其格式。第一个报告获 得数据文件 I/O 的情况,第二个报告获得磁盘 I/O 的情况。 Set TrimSpool On Set Line 142 Set Pages 57 Set NewPage 0 Set FeedBack Off Set Verify Off Set Term On TTitle Off BTitle Off Clear Breaks Break On Tablespace_Name Column TableSpace_Name For A12 Head "Tablespace" Column Name For A45 Head "File Name" Column Total For 999,999,990 Head "Total" Column Phyrds For 999,999,990 Head "Physical|Reads " Column Phywrts For 999,999,990 Head "Physical| Writes " Column Phyblkrd For 999,999,990 Head "Physical |Block Reads" Column Phyblkwrt For 999,999,990 Head "Physical |Block Writes" Column Avg_Rd_Time For 90.9999999 Head "Average |Read Time|Per Block" Column Avg_Wrt_Time For 90.9999999 Head "Average |Write Time|Per Block" Column Instance New_Value _Instance NoPrint Column Today New_Value _Date NoPrint Select Global_Name Instance, To_Char(SysDate, 'FXDay, Month DD, YYYY HH:MI') Today From Global_Name; TTitle On TTitle Left 'Date Run: ' _Date Skip 1- Center 'Data File I/O' Skip 1 – Center 'Instance Name: ' _Instance Skip 1 select C.TableSpace_Name, B.Name, A.Phyblkrd + A.Phyblkwrt Total, A.Phyrds, A.Phywrts, 310 A.Phyblkrd, A.Phyblkwrt From V$FileStat A, V$DataFile B, Sys.DBA_Data_Files C where B.File# = A.File# and B.File# = C.File_Id order by TableSpace_Name, A.File# / select object_name, statistic_name, value from v$segment_statistics where value > 100000 order by value; OBJECT_NAME STATISTIC_NAME VALUE ------------------------------------ ORDERS space allocated 96551 ORDERS space allocated 134181 ORDERS logical reads 140976 ORDER_LINES db block changes 183600 下面的程序清单获得磁盘 I/O: Column TableSpace_Name For A12 Head "Tablespace" Column Total For 9,999,999,990 Head "Total" Column Phyrds For 9,999,999,990 Head "Physical|Reads " Column Phywrts For 9,999,999,990 Head "Physical| Writes " Column Phyblkrd For 9,999,999,990 Head "Physical |Block Reads" Column Phyblkwrt For 9,999,999,990 Head "Physical |Block Writes" Column Avg_Rd_Time For 9,999,990.9999 Head "Average|Read Time |Per Block" Column Avg_Wrt_TimeFor 9,999,990.9999 Head "Average |Write Time|Per Block" Clear Breaks Break on Disk Skip 1 Compute Sum Of Total On Disk Compute Sum Of Phyrds On Disk Compute Sum Of Phywrts On Disk Compute Sum Of Phyblkrd On Disk Compute Sum Of Phyblkwrt On Disk TTitle Left 'Date Run: ' _Date Skip 1- Center 'Disk I/O' Skip 1 - Center 'Instance Name: ' _Instance Skip 2 select SubStr(B.Name, 1, 13) Disk, C.TableSpace_Name, A.Phyblkrd + A.Phyblkwrt Total, A.Phyrds, A.Phywrts, A.Phyblkrd, A.Phyblkwrt, ((A.ReadTim / Decode(A.Phyrds,0,1,A.Phyblkrd))/100) Avg_Rd_Time, ((A.WriteTim / Decode(A.PhyWrts,0,1,A.PhyblkWrt)) / 100) Avg_Wrt_Time from V$FileStat A, V$DataFile B, Sys.DBA_Data_Files C where B.File# = A.File# 311 and B.File# = C.File_Id order by Disk,C.Tablespace_Name, A.File# / Set FeedBack On Set Verify On Set Term On Ttitle Off Btitle Off 技巧: 视图 V$DATAFILE、V$FILESTAT 和 V$DBA_DATA_FILES 提供了数据库中所有数据文件和磁 盘的文件 I/O 活动信息。确保数据文件和磁盘都处于合理的平衡中,以获得最佳的性能。 12.1.2012.1.2012.1.2012.1.20 查找回滚段的内容查找回滚段的内容查找回滚段的内容查找回滚段的内容 这个有帮助的查询显示了一个回滚段的实际等待数。可以显示回滚信息(包括自动撤消)。还可以从程 序清单显示的视图中查询 Shrink 和 wrap 信息: 注意: 这个数据库中使用了自动或系统托管的撤消。 select a.name, b.extents, b.rssize, b.xacts, b.waits, b.gets, optsize, status from v$rollname a, v$rollstat b where a.usn = b.usn; File Name EXTENTS RSSIZE XACTS WAITS GETS OPTSIZE STATUS ------------ ------- -------- ------- ------ ------ ------------- SYSTEM 6 385024 0 0 164 ONLINE _SYSSMU1$ 6 4317184 0 0 3947 ONLINE _SYSSMU2$ 6 3334144 0 0 2985 ONLINE _SYSSMU3$ 7 450560 1 0 204 ONLINE _SYSSMU4$ 4 253952 0 0 244 ONLINE _SYSSMU5$ 17 2088960 0 1 5426 ONLINE _SYSSMU6$ 7 450560 0 0 1070 ONLINE _SYSSMU7$ 3 188416 0 0 275 ONLINE _SYSSMU8$ 2 122880 0 0 182 ONLINE _SYSSMU9$ 2 122880 0 0 182 ONLINE _SYSSMU10$ 2 122880 0 0 182 ONLINE 技巧: 查询 V$ROLLNAME、V$ROLLSTAT 和 V$TRANSACTION 视图可以提供用户如何使用回滚段和 撤消表空间的信息。通常情况下,在一个时间点上不应让多个用户访问同一个回滚段(尽 管这是被允许的)。 312 注意: 如果使用自动撤消管理,通常就不需要前一个查询。 下面程序清单中的查询显示了整个系统在整体上的等待数。 Set TrimSpool On Set NewPage 0 Set Pages 57 Set Line 132 Set FeedBack Off Set Verify Off Set Term On Ttitle Off Btitle Off Clear Breaks Column Event For A40 Heading "Wait Event" Column Total_Waits For 999,999,990 Head "Total Number| Of Waits " Column Total_Timeouts For 999,999,990 Head "Total Number|Of TimeOuts" Column Tot_Time For 999,999,990 Head "Total Time|Waited " Column Avg_Time For 99,990.999 Head "Average Time|Per Wait " Column Instance New_Value _Instance NoPrint Column Today New_Value _Date NoPrint select Global_Name Instance, To_Char(SysDate, 'FXDay DD, YYYY HH:MI') Today from Global_Name; TTitle On TTitle Left 'Date Run: ' _Date Skip 1- Center 'System Wide Wait Events' Skip 1 - Center 'Instance Name: ' _Instance Skip 2 Select event, total_waits, total_timeouts, (time_waited / 100) tot_time, (average_wait / 100) Avg_time from v$system_event order by total_waits desc / Date Run: Friday 01, 2006 09:24 System Wide Wait Events Instance Name: ORCL Total Number Total Number Total Time Average Time Wait Event Of Waits Of TimeOuts Waited Per Wait ----------------------- ------------ ------------ -------- --------- db file sequential read 2,376,513 0 30,776 313 0.010 db file scattered read 136,602 0 6,069 0.040 rdbms ipc message 103,301 99,481 276,659 2.680 latch: redo writing 57,488 0 0 0.000 ...etc... 12.1.2112.1.2112.1.2112.1.21 检查空闲列表是否充足检查空闲列表是否充足检查空闲列表是否充足检查空闲列表是否充足 如果使用多进程完成大量的插入操作,空闲列表(空闲的数据库数据块的列表)的默认值 1 可能是不够 的。如果没有使用自动空间段管理(Automatic Space Segment Management,简称 ASSM),您可能需要增加 空闲列表,或者空闲列表组(请参阅第 14 章,以获得更多的信息)。为了检查空闲列表组的存储参数是否足 够,请运行下面程序清单显示的报表。 Set TrimSpool On Set Line 132 Set Pages 57 Set NewPage 0 Set FeedBack Off Set Verify Off Set Term Off TTitle Off BTitle Off Column Pct Format 990.99 Heading "% Of |Free List Waits" Column Instance New_Value _Instance NoPrint Column Today New_Value _Date NoPrint select Global_Name Instance, To_Char (SysDate, 'FXDay DD, YYYY HH:MI') Today from Global_Name; TTitle On TTitle Left 'Date Run: ' _Date Skip 1- Center 'Free list Contention' Skip 1 - Center 'If Percentage is Greater than 1%' Skip 1 - Center 'Consider increasing the number of free lists' Skip 1 - Center 'Instance Name: ' _Instance select ((A.Count / (B.Value + C.Value)) * 100) Pct from V$WaitStat A, V$SysStat B, V$SysStat C where A.Class = 'free list' and B.Statistic# = (select Statistic# from V$StatName where Name = 'db block gets') and C.Statistic# = (select Statistic# from V$StatName 314 where Name = 'consistent gets') / Date Run: Friday 01, 2006 09:26 Free list Contention If Percentage is Greater than 1% Consider increasing the number of free lists Instance Name: ORCL % Of Free List Waits --------------- 0.00 (of course... I’m using ASSM) 如果活动比率超过 1%,就需要增加空闲列表组了。 技巧: 在使用多进程完成大量的插入操作时,应确保有足够的空闲列表和空闲列表组。空闲列 表的默认存储值是 1。如果您使用了 ASSM,Oracle 将为您管理这些参数,但是一个有大 量数据交换的事务环境中,在应用 ASSM 前应经过仔细的测试。虽然如此,但通常最好使 用 ASSM。 12.1.2212.1.2212.1.2212.1.22 检查角色和权限设置检查角色和权限设置检查角色和权限设置检查角色和权限设置 本节包含了多个 V$脚本,用于显示各种安全权限。下列程序清单中每个脚本的标题将简要说明了它们 将检索的信息。输出结果依据系统不同可能很大,因此要小心运行。 根据用户名进行授权的对象级特权 select b.owner || '.' || b.table_name obj, b.privilege what_granted, b.grantable, a.username from sys.dba_users a, sys.dba_tab_privs b where a.username = b.grantee order by 1,2,3; 根据被授权人进行授权的对象级特权 Select owner || '.' || table_name obj, privilege what_granted, grantable, grantee from sys.dba_tab_privs where not exists (select 'x' from sys.dba_users where username = grantee) order by 1,2,3; 315 根据用户名进行授予的系统级特权 select b.privilege what_granted, b.admin_option, a.username from sys.dba_users a, sys.dba_sys_privs b where a.username = b.grantee order by 1,2; 根据被授权人进行授予的系统级特权 select privilege what_granted, admin_option, grantee from sys.dba_sys_privs where not exists (select 'x' from sys.dba_users where username = grantee) order by 1,2; 根据用户名授予的角色 select b.granted_role || decode(admin_option, 'YES', ' (With Admin Option)', null) what_granted, a.username from sys.dba_users a, sys.dba_role_privs b where a.username = b.grantee order by 1; 根据被授权人授予的角色 select granted_role || decode(admin_option, 'YES', ' (With Admin Option)', null) what_granted, grantee from sys.dba_role_privs where not exists (select 'x' from sys.dba_users where username = grantee) order by 1; 316 用户名及已被授予的相应权限 select a.username, b.granted_role || decode(admin_option,'YES', ' (With Admin Option)',null) what_granted from sys.dba_users a, sys.dba_role_privs b where a.username = b.grantee UNION select a.username, b.privilege || decode(admin_option,'YES', ' (With Admin Option)', null) what_granted from sys.dba_users a, sys.dba_sys_privs b where a.username = b.grantee UNION select a.username, b.table_name || ' - ' || b.privilege || decode(grantable,'YES', ' (With Grant Option)',null) what_granted from sys.dba_users a, sys.dba_tab_privs b where a.username = b.grantee order by 1; 技巧: 记录系统中已有的特权,这样您就可以应对各种类型的安全场景。 查询用户名及相应的配置文件、默认的表空间和临时表空间 Select username, profile, default_tablespace, temporary_tablespace, created from sys.dba_users order by username; 12.1.2312.1.2312.1.2312.1.23 等待事件等待事件等待事件等待事件 V$V$V$V$视图视图视图视图 本节包含一些显示等待事件的 V$ 脚本。从个人角度来说,我更喜欢使用 STATSPACK 报表、AWR 报表 或企业管理器来查找等待事件。也就是说,有些很好的视图可以查看等待事件。 Oracle 10gR2 中添加了 一些新的视图,但最幸运的是在 V$SESSION_WAIT 中找到的东西现在在 V$SESSION 中可以找到。 马上该谁等待——查询 V$SESSION_WAIT / V$SESSION select event, sum(decode(wait_time,0,1,0)) "Waiting Now", sum(decode(wait_time,0,0,1)) "Previous Waits", count(*) "Total" from v$session_wait group by event order by count(*); WAIT_TIME = 0 means that it’s waiting WAIT_TIME > 0 means that it previously waited this many ms 317 EVENT Waiting Now Previous Waits Total --------------------------- ------------ -------------- ------- db file sequential read 0 1 1 db file scattered read 2 0 2 latch free 0 1 1 enqueue 2 0 2 SQL*Net message from client 0 254 480 ... select event, sum(decode(wait_time,0,1,0)) "Waiting Now", sum(decode(wait_time,0,0,1)) "Previous Waits", count(*) "Total" from v$session group by event order by count(*); EVENT Waiting Now Previous Waits Total --------------------------- ------------ -------------- -------- db file sequential read 0 1 1 db file scattered read 2 0 2 latch free 0 1 1 enqueue 2 0 2 SQL*Net message from client 0 254 480 ... 马上该谁等待;SPECIFIC Waits——查询 V$SESSION_WAIT SELECT /*+ ordered */ sid, event, owner, segment_name, segment_type,p1,p2,p3 FROM v$session_wait sw, dba_extents de WHERE de.file_id = sw.p1 AND sw.p2 between de.block_id and de.block_id+de.blocks – 1 AND (event = 'buffer busy waits' OR event = 'write complete waits') AND p1 IS NOT null ORDER BY event,sid; 谁在等待 – 最后 10 个等待数——查询 V$SESSION_WAIT_HISTORY SELECT /*+ ordered */ sid, event, owner, segment_name, segment_type,p1,p2,p3 FROM v$session_wait_history sw, dba_extents de WHERE de.file_id = sw.p1 AND sw.p2 between de.block_id and de.block_id+de.blocks – 1 AND (event = 'buffer busy waits' OR event = 'write complete waits') AND p1 IS NOT null ORDER BY event,sid; 318 查找 P1, P2, P3 代表什么——查询 V$EVENT_NAME col name for a20 col p1 for a10 col p2 for a10 col p3 for a10 select event#,name,parameter1 p1,parameter2 p2,parameter3 p3 from v$event_name where name in ('buffer busy waits', 'write complete waits'); EVENT# NAME P1 P2 P3 ------------- -------------------- ---------- ---------- ---------- 143 write complete waits file# block# 145 buffer busy waits file# block# id 会话开始后的所有等待数——查询 V$SESSION_EVENT select sid, event, total_waits, time_waited, event_id from v$session_event where time_waited > 0 order by time_waited; SID EVENT TOTAL_WAITS TIME_WAITED ---------- ------------------------------ ----------- ----------- 159 process startup 2 1 167 latch: redo allocation 4 1 168 log buffer space 2 3 166 control file single write 5 4 ... 类的所有会话等待数——查询 V$SESSION_WAIT_CLASS select sid, wait_class, total_waits from v$session_wait_class; SID WAIT_CLASS TOTAL_WAITS ---------- -------------------- ----------- 168 Other 2 168 Concurrency 1 168 Idle 12825 168 User I/O 12 168 System I/O 4448 169 Other 1 169 Idle 12812 170 Idle 13527 系统启动后的所有等待数——查询 V$SYSTEM_EVENT 319 select event, total_waits, time_waited, event_id from v$system_event where time_waited > 0 order by time_waited; EVENT TOTAL_WAITS TIME_WAITED EVENT_ID ----------------------------------- ----------- ----------- ---------- enq: TX - row lock contention 1196 366837 310662678 enq: TM - contention 170 52074 668627480 db file sequential read 17387 3163 2652584166 control file parallel write 12961 23117 4078387448 db file scattered read 4706 15762 506183215 class slave wait 20 10246 1055154682 类的系统等待数——查询 V$SYSTEM_WAIT_CLASS select wait_class, total_waits from v$system_wait_class order by total_waits desc; WAIT_CLASS TOTAL_WAITS -------------------- ----------- Idle 161896 Other 65308 System I/O 24339 User I/O 22227 Application 1404 Commit 524 Network 522 Concurrency 221 Configuration 55 ... 类的系统等待数——查询 V$ACTIVE_SESSION_HISTORY --In the query below, the highest count session is leader in non-idle wait events. select session_id,count(1) from v$active_session_history group by session_id order by 2; In the query below, find the SQL for the leader in non-idle wait events. select c.sql_id, a.sql_text from v$sql a, (select sql_id,count(1) from v$active_session_history b where sql_id is not null group by sql_id 320 order by 2 desc) c where rownum <= 5 order by rownum; 技巧: 在 Oracle 10g 中 V$SESSION_WAIT 中的所有等待事件列现在都在 V$SESSION 中。因此, 确保查询等待信息的 V$SESSION,因为它是一个更快的视图。V$ACTIVE_SESSION_HISTORY (ASH)将许多重要统计数据合并为一个视图或一个报表(ASH 报表)。 12.1.2412.1.2412.1.2412.1.24 一些主要的一些主要的一些主要的一些主要的 V$V$V$V$视图种类视图种类视图种类视图种类 本节中根据视图的主要功能对它们进行了分类。这里的列表并不完整( V$ 视图和 X$表查询的完整列 表请参阅附录 B)。您将经常需要将一类视图与另一类视图进行连接,以检索所需要的信息。可以像对其他 Oracle 视图一样对 V$视图进行查询,但要记住这些表中的信息变化很快。您可以将 V$视图中的信息导入 先前已经创建好的表中,这样就可以花费一定时间编译数据,或者供以后分析使用,或者用于建立统计数 据报表和基于数据库的不同条件产生警告。 目前市场上的绝大多数 DBA 监控工具都使用了 V$视图(和 X$表)。若不使用 DBA 监控工具来查询数据 库信息,需要您对每个视图中存储的信息和如何正确地查询视图有深刻的了解。表 12-1 包含了一个根据它 们的主要功能分类的 V$视图列表。列表中的视图分类与它们监控的操作相关联。这张列表不是完整的,仅 包含了绝大多数常用的视图。有些视图因为 Oracle 版本的不同而不同。这些就是 TUSC V$ Poster 中包含 的信息。 表 12-1 V$视图分类 类 别 描述和相关的 V$视图 顾问 与缓存顾问相关的信息 V$视图: V$PGA_TARGET_ADVICE、V$PGA_TARGET_ADVICE_HISTOGRAM、V$MTTR_TARGET_ADVICE、 V$PX_BUFFER_ADVICE、V$DB_CACHE_ ADVICE、 V$SHARED_POOL_ADVICE、V$JAVA_POOL_ADVICE、V$STREAMS_POOL_ ADVICE (10.2)、V$SGA _ TARGET _ ADVICE (10.2)和 V$ADVISOR _ PROGRESS (10.2) ASM V$ASM_ALIAS(10.1)、V$ASM_CLIENT(10.1)、V$ASM_DISK(10.1)、V$ASM_DISK_ STAT(10.2)、 V$ASM_DISKGROUP(10.1)、V$ASM_DISKGROUP_STAT(10.2)、V$ASM _FILE(10.1)、V$ASM _OPERATION(10.1)、V$ASM_ TEMPLATE(10.1) 备份/恢 复 有关数据库备份和恢复的信息,包括以前的备份、归档日志、备份文件的状态以及恢复信息 V$视图:V$ARCHIVE, V$ARCHIVED_LOG, V$ARCHIVE_DEST, V$ARCHIVE_ DEST_STATUS,V$ARCHIVE_GAP, V$ARCHIVE_PROCESSES,V$BACKUP, V$BACKUP_ASYNC_IO, V$BACKUP_CORRUPTION,V$BACKUP_DATAFILE, V$BACKUP_DEVICE,V$BACKUP_PIECE,V$BACKUP_REDOLOG, V$BACKUP_SET,V$BACKUP_SYNC_IO,V$BLOCK_CHANGE_TRACKING,V$COPY_0CORRUPTION, V$DATABASE_BLOCK_CORRUPTION,V$DATABASEINCARNATION,V$DATAFILE_COPY,V$DELETED_OBJECT, V$FAST_START_SERVERS,V$FAST_START_ TRANSACTIONS,V$INSTANCE_RECOVERY,V$MTTR_TARGET_ADVICE,V$PROXY_ARCHIVEDLOG,V$PROXY_DATAFILE, V$RMAN_CONFIGURATION,V$RECOVERY_FILE_STATUS,V$RECOVERY_LOG,V$RECOVERY_PROGRESS,V$RECOVERY_ STATUS,V$RECOVER_FILE,V$BACKUP_ARCHIVELOG_DETAILS(10.2),V$BACKUP_ARCHIVELOG_SUMMARY(10.2), V$BACKUP_CONTROLFILE_DETAILS(10.2), V$BACKUP_CONTROLFILE_SUMMARY(10.2),V$BACKUP_COPY_DETAILS(10.2),V$BACKUP_COPY_SUMMARY(10.2), 321 V$BACKUP_FILES(10.1),V$BACKUP_PIECE_DETAILS(10.2), V$BACKUP_SET_DETAILS(10.2),V$BACKUP_SET_SUMMARY(10.2),V$BACKUP_SPFILE, V$BACKUP_SPFILE_DETAILS(10.2),V$BACKUP_SPFILE_SUMMARY(10.2), V$DATAFILE_HEADER,V$FLASH_RECOVERY_AREA_USAGE(10.2),V$FLASHBACK_DATABASE_LOG(10.1), V$FLASHBACK_DATABASE_STAT(10.1),V$OBSOLETE_BACKUP_FILES, V$OFFLINE_RANGE,V$PROXY_ARCHIVELOG_DETAILS(10.2),V$PROXY_ARCHIVELOG_SUMMARY(10.2), V$PROXY_COPY_DETAILS(10.2), V$PROXY_COPY_SUMMARY(10.2),V$RECOVERY_FILE_DEST(10,1),V$RESTORE_POINT(10.2), V$RMAN_BACKUPJOB_DETAILS(10.2),V$RMAN_ BACKUP_SUBJOB_DETAILS(10.2),V$RMAN_BACKUP_TYPE(10.2),V$RMAN_ENCRYPTION_ALGORITHMS(10.2), V$RMAN_OUTPUT(10.1),V$RMAN_STATUS(10.1),V$UNUSABLE_BACKUPFILE_DETAILS(10.2) (续表) 类 别 描述和相关的 V$视图 缓存 有关各种缓存的信息,包括对象、库、游标和字典 V$ 视 图: V$ACCESS,V$BUFFER_POOL,V$BUFFER_POOL_STATISTICS, V$DB_ CACHE_ADVICE,V$DB_OBJECT_CACHE,V$JAVA_POOL_ADVICE,V$LIBRARYC- ACHE,V$LIBRARY_CACHE_MEMORY,V$PGASTAT,V$PGA_TARGET_ADVICE, V$PGA_TARGET_ADVICE_HISTOGRAM,V$ROWCACHE,V$ROWCACHE_PARENT, V$ROWCACHE_SUBORDINATE,V$SESSION_CURSOR_CACHE,V$SGA,V$SGASTAT, V$SGA_CURRENT_RESIZE_OPS,V$SGA_DYNAMIC_COMPONENTS,V$SGA_ DYNAMIC_FREE_MEMORY,V$SGA_RESIZE_OPS,V$SGAINFO,V$SHARED_POOL_ADVICE, V$SHARED_POOL_RESERVED,V$SYSTEM_CURSOR_CACHE,V$SUBCACHE, V$JAVA_LIBRARY_CACHE_MEMORY(10.1),V$PROCESS_MEMORY(10.2),V$SGA_TARGET_ADVICE(10.2) 缓存熔 合/RAC V$ACTIVE_INSTANCES,V$BH,V$CLUSTER_INTERCONNECTS(10.2),V$CON-FIGURED_INTERCONNECTS(10.2), V$CR_BLOCK_SERVER,V$CURRENT_BLOCK_SERVER(10.1),V$GC_ELEMENT,VGCSHVMASTER_INFO, V$GCSPFMAS_TER_INFO,V$GES_BLOCKING_ENQUEUE,V$GES_CONVERT_LOCAL,V$GES_CONVERT_ REMOTE,V$GES_ENQUEUE,V$GES_LATCH,V$GES_RESOURCE,V$GES_ STATISTICS,V$HVMASTER_INFO,V$INSTANCE_CACHE_TRANSFER,V$RESOURCE_LIMIT 控制文 件 有关实例控制文件的信息 V$ 视 图: V$CONTROLFILE, V$CONTROLFILE_RECORD_SECTION 游标 /SQL 语 句 有关游标和 SQL 语句的信息,包括开放游标、统计数据和实际的 SQL 文本 V$ 视 图: V$OPEN_CURSOR,V$SQL,V$SQLAREA,V$SQLTEXT, V$SQLTEXT_ WITH_NEWLINES,V$SQL_BIND_DATA,V$SQL_BIND_METADATA,V$SQL_ CURSOR,V$SQL_OPTIMIZER_ENV,V$SQL_PLAN,V$SQL_PLAN_STATISTICS, V$SQL_PLAN_STATISTICS_ALL,V$SQL_REDIRECTION,V$SESSION_CURSOR_ CACHE,V$SQL_SHARED_CURSOR,V$SQL_SHARED_MEMORY,V$SQL_WORKAREA,V$SQL_WORKAREA_ACTIVE, V$SQL_WORKAREA_HISTOGRAM,V$SYSTEM_CURSOR_CACHEV$MUTEX_SLEEP(10.2), V$MUTEX_SLEEP_HISTORY(10.2) (专用于那些不确定的共享闩锁), V$SQL_BIND_CAPTURE(10.1), V$SQL_JOIN_FILTER(10.2),V$SQL_AREA_PLAN_HASH(10.2), V$SQLSTATS(10.2), V$SYS_OPTIMIZER_ENV(10.1), V$VPD_POLICY (续表) 322 类 别 描述和相关的 V$视图 数据库 实例 有关实际数据库实例的信息 V$ 视 图: V$ACTIVE_INSTANCES,V$BGPROCESS,V$DATABASE, V$INSTANCE, V$PROCESS,V$SGA,V$SGASTAT,V$BLOCKING_QUIESCE(10.2),V$CLIENT_STATS(10.1) RAC 视图: V$BH, V$ACTIVE_INSTANCES 直接路 径操作 有关 SQL*Loader 直接加载产品项的信息 V$ 视 图: V$LOADISTAT, V$LOADPSTAT 分布式/ 异类 服务 V$ 视 图: V$DBLINK,V$GLOBAL_TRANSACTION, V$GLOBAL_BLOCKED_LOCKS, V$HS_AGENT,V$HS_PARAMETER,V$HS_SESSION 固定视 图 有关 V$表自身的信息 V$ 视 图: V$FIXED_TABLE,V$FIXED_VIEW_DEFINITION, V$INDEXED_FIXED_COLUMN 通用信 息 有关各种系统信息的通用信息 V$ 视 图:V$DBPIPES,V$CONTEXT,V$GLOBALCONTEXT,V$LICENSE, V$OPTION, V$RESERVED_WORDS,V$TIMER,V$TIMEZONE_NAMES,V$TYPE_SIZE,V$_ SEQUENCES,V$VERSION,V$DB_TRANSPORTABLE_PLATFORM(10.2), V$TRANSPORTABLE_PLATFORM(10.2)V$TRANSPORTABLE_PLATFORM(10.1) V$SCHEDULER_RUNNING_JOBS(10.2) I/O 有关 I/O 的信息,包括文件和统计数据 V$ 视 图: V$DBFILE,V$FILESTAT,V$WAITSTAT,V$TEMPSTAT, V$FILE_ HISTOGRAM(10.1),V$FILEMETRIC(10.1),V$FILEMETRIC_HISTORY(10.1),V$SYSAUX_OCCUPANTS(10.1)。 V$TABLESPACE, V$TEMP_HISTOGRAM(10.1),V$TEMP_SPACE_HEADER, V$TEMPFILE, V$TEMPSEG_USAGE 闩锁/锁 定 有关闩锁和锁定的信息 V$ 视 图:V$ENQUEUE_LOCK,V$ENQUEUE_STAT, V$EVENT_ NAME,V$FILE_ CACHE_TRANSFER,V$GLOBAL_BLOCKED_LOCKS,V$LATCH,V$LATCHHOLDER,V$LATCHNAME, V$LATCH_CHILDREN,V$LATCH_MISSES,V$LATCH_PARENT, V$LOCK,V$LOCKED_OBJECT,V$RESOURCE,V$RESOURCE_LIMIT, V$TRANSACTION_ENQUEUE,V$_LOCK,V$_LOCK1,V$ENQUEUE_STATISTICS RAC 视 图:V$CR_BLOCK_SERVER,V$GCSHVMASTER_INFO, V$GCSPFMASTER _INFO,V$GC_ELEMENT,V$GES_BLOCKING_ENQUEUE,V$GES_ENQUEUE, V$HVMASTER_INFO,V$GES_LATCH, V$GES_RESOURCES (续表) 类 别 描述和相关的 V$视图 日志挖掘 有关日志挖掘(log miner)的信息 V$ 视 图: V$LOGMNR_CALLBACK,V$LOGMNR_CONTENTS, V$LOGMNR_ DICTIONARY,V$LOGMNR_LATCH,V$LOGMNR_LOGFILE,V$LOGMNR_LOGS, V$LOGMNR_PARAMETERS,V$LOGMNR_PROCESS,V$LOGMNR_REGION,V$LOGMNR_SESSION, V$LOGMNR_STATS,V$LOGMNR_TRANSACTION,V$LOGMNR_ DICTIONARY_LOAD(10.2) Metrics 有关 Metrics 的信息 (这在 Oracle 10g 中是全新的!) V$ 视 图:V$METRICNAME, V$SERVICEMETRIC,V$EVENTMETRIC, V$FILEMETRIC,V$FILEMETRIC_HISTORY,V$SERVICEMETRIC_HISTORY, V$SESSMETRIC,V$SYSMETRIC,V$SYSMETRIC_HISTORY,V$SYSMETRIC_SU 323 MMARY,V$THRESHOLD_TYPES,V$WAITCLASSMETRIC,V$WAITCLASSMETRIC_HISTORY 多线程/ 共享 服务器 有关多线程和共享服务器的信息,包括连接、队列、调度和共享服务器 V$ 视 图: V$CIRCUIT, V$DISPATCHER, V$DISPATCHER_RATE, V$QUEUE, V$QUEUEING_MTH,V$REQDIST,V$SHARED_SERVER,V$SHARED_SERVER_ MONITOR,V$DISPATCHER_CONFIG(10.1) 对象使用 有关对象使用和依赖的信息 V$ 视 图: V$OBJECT_DEPENDENCY, V$OBJECT_USAGE 整个系统 有关整个系统性能的信息 V$ 视 图:V$GLOBAL_TRANSACTION, V$SHARED_POOL_RESERVED, V$RESUMABLE, V$SORT_SEGMENT, V$TEMPSEG_USAGE, V$STATNAME, V$SYS_ OPTIMIZER_ENV,V$SYS_TIME_MODEL,V$SYSSTAT,V$SYSTEM_CURSOR_CACHE, V$SYSTEM_EVENT,V$TEMPFILE,V$TEMPORARY_LOBS,V$TEMP_EXTENT_MAP, V$TEMP_EXTENT_POOL,V$TEMP_SPACE_HEADER,V$TRANSACTION, V$ALERT _TYPES(110.1),V$EVENT_HISTOGRAM(10.1),V$OSSTAT(10.1), V$SYSTEM_WAIT_ CLASS (10.1) ,V$TEMP_HISTOGRAM(10.1), V$XML_AUDIT_TRAIL 并行查询 有关并行查询选项的信息 V$ 视 图:V$EXECUTION,V$PARALLEL_DEGREE_LIMIT_MTH, V$PQ_SESSTAT, V$PQ_SLAVE,V$PQ_SYSSTAT,V$PQ_TQSTAT,V$PX_PROCE_SS, V$PX_PROCESS _SYSSTAT, V$PX_SESSION, V$PX_SESSTAT 参数 有关各种 Oracle 参数的信息,包括初始化和每个会话的 NLS V$ 视 图: V$NLS_PARAMETERS,V$NLS_VALID_VALUES, V$OBSOLETE_ PARAMETER,V$PARAMETER,V$PARAMETER2,V$SPPARAMETER,V$SYSTEM_ PARAM_ETER,V$SYSTEM_PARAMETER2,V$PARAMETER_VALID_VALUES(10.2) (续表) 类 别 描述和相关的 V$视图 重做日 志 有关重做日志的信息,包括统计信息和历史 V$ 视 图: V$LOG, V$LOGFILE, V$LOGHIST, V$LOG_HISTORY, V$THREAD (与 RAC 相关) 复制和 物化 视图 有关复制和物化(materialized)视图的信息 V$ 视 图: V$MVREFRESH , V$REPLPROP, V$REPLQUEUE 资源管 理器 有关资源管理的信息 V$ 视 图: V$ACTIVE_SESSION_POOL_MTH, V$ACTIVE_SESSION_POOL_HISTORY, V$RSRC_CONS_GROUP_HISTORY(10.2),V$RSRC_CONSUMER_GROUP,V$RSRC_ CONSUMER_GROUP_CPU_MTH,V$RSRC_PLAN,V$RSRC_PLAN_CPU_MTH, V$RSRC_PLAN_HISTORY(10.2), V$RSRC_SESSION_INFO(10.2) 回滚段 和 撤消 有关回滚段的信息,包括统计数据和事务 V$ 视 图: V$ROLLNAME, V$ROLLSTAT, V$TRANSACTION, V$UNDOSTAT 安全/ 权限 有关安全的信息 V$ 视 图: V$ENABLEDPRIVS,V$PWFILE_USERS,V$VPD_POLICY, V$WALLET (10.2), 和 V$XML_AUDIT_TRAIL(10.2) 324 会话 (包括 某些复 制信息 和不同 种类的 服务) 有关会话的信息,包括访问对象、游标、进程和统计数据 V$ 视 图:V$ACTIVE_SESSION_HISTORY,V$MYSTAT,V$PROCESS, V$SESS_TIME _MODEL,V$SESSION,V$SESSION_CONNECT_INFO,V$SESSION_CURSOR_CACHE,V$SESSION_EVENT, V$SESSION_LONGOPS,V$SESSION_OBJECT_CACHE,V$SESSION_WAIT,V$SESSION_WAIT_CLASS, V$SESSION_WAIT_HISTORY,V$SESSTAT,V$SESS_IO,V$SES_OPTIMIZER_ENV,V$SESSMETRIC, 以及 V$CLIENT_STATS, 和 V$TSM _SESSIONS(10.2) 服务 (在 10.1 中 这些都 是新 的) V$ACTIVE_SERVICES,V$SERV_MOD_ACT_STATS,V$SERVICE_EVENT, V$SERVICE _STATS, V$SERVICE_WAIT_CLASS, V$SERVICES 排序 有关排序的信息 V$视图:V$SORT_SEGMENT, V$TEMPSEG_USAGE, V$TEMP_EXTENT_MAP, V$TEMP_EXTENT_POOL,V$TEMP_HISTOGRAM(10.1),V$TEMP_SPACE_HEADER, V$TEMPFILE, V$TEMPSTAT 备用数 据库 (Data Guard) 有关备用数据库的信息 V$视图: V$DATAGUARD_STATUS, V$LOGSTDBY, V$LOGSTDBY_STATS, V$MANAGED_STANDBY,V$STANDBY_LOG,V$DATAGUARD_CONFIG(10.1), V$DATAGUARD_STATS(10.2),V$LOGSTDBY_PROCESS(10.2),V$LOGSTDBY_ PROGRESS(10.2),V$LOGSTDBY_STATE(10.2),V$LOGSTDBY_TRANSACTION(10.2) (续表) 类 别 描述和相关的 V$视图 文件映射接口 有关文件映射的信息 V$ 视 图: V$MAP_COMP_LIST, V$MAP_ELEMENT, V$MAP_EXT_ELEMENT, V$MAP _FILE,V$MAP_FILE_EXTENT,V$MAP_FILE_IO_STACK,V$MAP_LIBRARY 和 V$MAP _SUBELEMENT 流 有关流的信息 V$ 视 图: V$AQ,V$STREAMS_APPLY_COORDINATOR, V$STREAMS_APPLY _READER,V$STREAMS_APPLY_SERVER,V$STREAMS_CAPTURE, V$BUFFERED _PUBLISHERS(10.1),V$BUFFERED_QUEUES(10.1), V$BUFFERED_SUBSCRIBERS (10.1),V$PROPAGATION_RECEIVER(10.1),V$PROPAGATION_SENDER(10.1), V$RULE(10.1),V$RULE_SET(10.1),V$RULE_SET_AGGREGATE_STATS(10.1), V$STREAMS_TRANSACTION(10.2) 统计数据 有关通用统计数据的信息 V$视图: V$SEGMENT_STATISTICS, V$SEGSTAT, V$SEGSTAT_NAME, V$STATISTICS_LEVEL, V$STATNAME, V$WAITSTAT 事务 有关通用事务的信息 V$ 视 图: V$GLOBAL_TRANSACTION,V$LOGSTDBY_TRANSACTION, V$RESU MABLE,V$STREAMS_TRANSACTION,V$TRANSACTION,V$TRANSACTION_ ENQUEUE 325 注意: V$ROLLNAME 视图的创建和其他 V$视图有一些细微差别。V$ROLLNAME 视图是 X$表和 undo$ 表连接的结果。某些 V$timing 字段的值依赖于 init.ora 的 TIMED_STATISTICS 参数是否 被设置为 TRUE;如果该参数值不是 TRUE,则这些域将没有计时信息。 12.212.212.212.2 技巧回顾技巧回顾技巧回顾技巧回顾 ● 由 SYSTEM 用户身份访问的 V$视图实际上是指向 v_$视图的同名视图,而 v_$视图是建立在以 X$ 表为基础的原始 V$视图上的视图。(最好将上面的话多读几遍!) ● 当其他的 DBA 需要访问 V$视图的信息,但是没有 SYS 或 SYSTEM 密码,您可以授予他们访问 v_$ 视图的权限。然后用户就可以访问与 v_$视图具有公共同名的 V$视图。 ● 在 Oracle 10g 中,查询 V$FIXED_TABLE 视图可以获得数据库中现有的 GV$视图和 V$视图列表。 GV$视图和 V$视图几乎完全一样,除了实例 ID 列包含了一个标识符之外。 ● 查询 V$FIXED_VIEW_DEFINITION 可以检索从 X$表创建 V$和 GV$ 视图的查询 ● DBA_视图不是从 X$表或者 V$视图派生而来的。事实上,能从 obj$中删除行数据这一特性就是很 多人不愿成为 SYS 超级用户的主要原因。 ● 查询 V$DATABASE 视图可以获得数据库创建的时间以及基本的归档信息。 ● 查询 V$SYSAUX_OCCUPANTS 以确保自动工作量仓库(AWR)没有占用过多空间。使用 DBMS_STATS 检 查历史和保留。 ● 查询 V$LICENSE 视图以查看您被允许的最大会话数。您也可以在接近会话最大数时设置告警。 ● 查询 V$OPTION 视图来检索您已经安装的 Oracle 产品项。V$VERSION 视图将给出已经安装的基本 产品项的版本。 ● 访问 V$SGA 视图可以得到系统物理内存分配的基本情况,包括在 Oracle 中数据、共享池以及日 志缓冲区的分配情况。 ● 访问 V$SGASTAT 视图向您提供了 Oracle SGA 详细的分类列表以及共享池分配中各存储容器的详 细信息。 ● 查询 V$PARAMETER 视图将得到 init.ora 参数的当前值。它还显示了哪一个 init.ora 参数已经修 改了原始的默认值(ISDEFAULT = FALSE)以及哪一个参数可能无需关闭和重启数据库即可直接修改。 ● 查询 V$LIBRARYCACHE 视图可以显示从内存中访问 SQL 和 PL/SQL 的频率。固定对象访问率通常应 该是 95%或更高,而重载的数量不应该超过 1%。 ● 查询 V$SQL_BIND_CAPTURE 可以查看每个 SQL 的绑定是否太高,是否需要 CURSOR_SHARING。 326 ● 查询 V$DB_OBJECT_CACHE 视图可查看尚未固定的并且占用空间较大,可能会引起潜在问题的对 象。 ● 查询 V$SQLAREA 视图可发现有问题的查询(和用户)。 ● 查询 V$SESSION、V$SQLTEXT 和 V$SESS_IO 可发现有问题的用户,以及在给定的时间点上他们正 在执行哪些操作。 ● 查询 V$ACCESS 视图可查看在给定的时间点上用户所访问的所有对象,也有助于查明问题对 象。当您需要修改一个特定的对象时,该视图也很有用(查找谁在访问它)。 ● 确定锁定其他用户的用户,并关闭他们的会话(如果需要)。 ● 确定使用了多会话的用户,并判别是管理问题(用户使用了多个终端)还是系统问题(会话没有被 清除,或者衍生了多余的进程)。 ● 视图 V$DATAFILE、V$FILESTAT 和 V$DBA_DATA_FILES 提供了数据库中所有数据文件和磁盘的文件 I/O 活动信息。 ● 查询 V$ROLLNAME、V$ROLLSTAT 和 V$TRANSACTION 视图可以显示用户如何使用回滚段的信息。 V$UNDOSTAT 视图提供了 UNDO 信息(回滚段将很快成为过时的技术)。 ● 在使用多进程完成大量的插入操作时,应确保有足够的空闲列表和空闲列表组。空闲列表的默认 存储值是 1。 ● 将系统中已有的特权记录入档,这样您就可以应对各种类型的安全场景。 12.312.312.312.3 参考文档参考文档参考文档参考文档 1Joe Trezzo, "The V$ Arsenal: Key V$ Scripts Every DBA Should Use Regularly" (TUSC, 1997) Oracle Corporation, Oracle Server: SQL Language Reference Manual Oracle Corporation, Oracle Server: Application Developer's Guide Joe Trezzo, "The V$ Views—A DBA’s Best Friend" IOUG-A Proceedings (TUSC, 1997) Rich Niemiec and Kevin Loney, How I Broke into Your Database (COUG, 2001) asktom.oracle.com Metalink Notes: 276103.1, 296765.1, 287679.1, 1019592.6 (Script Library), 243132.1, 245055.1 327 第第第第 15151515 章章章章 执行快速系统检查执行快速系统检查执行快速系统检查执行快速系统检查((((针对针对针对针对 DBA)DBA)DBA)DBA) Oracle 10 g 引入了许多有助于调整的新特性。有些 DBA 非常擅长规划和实现新版本,但很少有人擅长 评估和实现对系统有帮助的新特性。随着自动工作量仓库(AWR)和 AWR 报表(参见第 14 章查看与 STATSPACK 的不同点)的 出现,您可以用不同方法监控自己的系统;随着调整向导的出现,您可以用不同方法修复系 统。成功系统检查的关键之一是检查您是否实现了某些特性,这些特性能 够满足您的需要,能够很好反馈 特性成本以及执行它所需要的时间。尽管执行一个简单的评估就可以帮助您指出未来可能出现的性能问题 和/或目前存在的问题,但似乎许多人都不喜欢做测试或评估。 尽管执行一个简单的评估就可以帮助您指出未来可能出现的性能问题和/或目前存在的问题,但似乎 许多人都不喜欢做测试或评估。提高和维护优秀系统性能的一种关键方法就是进行年度检查—— 对您的系 统性能进行内部或外部检查。许多公司都开发了测量系统性能和系统综合速度的方法。本章并不想花六个 月来说明众多的非常细节的评估方法;相反,只希 望成为一个简单的晴雨表,来反映您的系统的速度与其 他的行业化产品间的比较情况。商业流程中的各种变化可能会影响到使用这个简单的检查所产生的评测结 果。 所以,需要调整这些测试,令其适应您特定的系统。 本章主要内容: ● 总体性能指数(TPI)及使用它的原因 ● 如何获得教育性能指数(EPI) ● 如何获得系统性能指数(SPI) ● 如何获得内存性能指数(MPI) ● 如何获得磁盘性能指数(DPI) ● 如何获得总体性能指数(TPI) ● 一个综合系统检查的示例 ● 即时动作条目列表 ● 收集系统信息列表 ● 由公正专家完成的 DBA 排序 15.115.115.115.1 总体性能指数总体性能指数总体性能指数总体性能指数(TPI)(TPI)(TPI)(TPI) 我创建了总体性能指数(Total Performance Index,TPI),作为 Oracle 的 DBA 们 测量自己的系统并 和其他系统进行比较的最基本工具,它使用了一种快速而简单的评分方法。该指数只是想作为一个晴雨表, 来反映所做的改进是否会带来好处。许 多系统因为商业案例的不同,从而形成了类别上的差异,但该指数 可以反映出您的系统和其他行业系统之间的差距到底是大还是小。目前有四种分类:教育、系统、 内存以 及磁盘,如表 15-1 所示。本章将向您展示如何使用一些简单的查询来测量您的 TPI 值。对于特定分类的详 细信息,请参阅本书中的相关章节。为了帮助您确定系统的运行进展情况,可以将您目前的 TPI 值和随着 用户数量增长或软硬件改变后的 TPI 值进行比较。您也可以自定义索引以便适应您的常用工具,例如 Oracle 企业管理器(Oracle Enterprise Manager,EM)或自动数据库诊断监控器(Automatic Database Diagnostics Monitor,ADDM)。 表 15-1 测量总体性能指数 分 类 指 数 最 高 分 教育性能指数(EPI) 250 系统性能指数(SPI) 250 328 内存性能指数(MPI) 250 磁盘性能指数(DPI) 250 总体性能指数(TPI) 1000 15.215.215.215.2 教育性能指数教育性能指数教育性能指数教育性能指数(EPI)(EPI)(EPI)(EPI) 该指数测量技术人员的知识和教育程度。表 15-2 解释了如何获得一个满意的 EPI 分数。评级系统并 不意味着对知识和受教育程度的全方位评估,而只是一个晴雨表,反映提高员工的教育水平是否能获得好 处。 .表 15-2 完美的教育性能指数 分 类 所 需 等 级 最 高 分 调整数据库所需的 DBA 是 30 调整所写的代码而需要的开发人员 是 30 DBA 上一次接受调整培训的时间 < 1 年 30 开发人员上一次接受调整培训的时间 < 1 年 30 精通 V$视图的 DBA 是 30 精通 ADDM 或 EM(如果使用)的DBA 是 20 接受过 EXCUTION PLAN 培训的 DBA 是 20 接受过 EXCUTION PLAN 的培训的开发人员 是 20 接受过使用 SQL 调整向导培训 DBA 是 20 接受过使用 SQL 调整向导培训的开发人员 是 20 教育性能指数(EPI) 总和 250 评估您的系统 问 题 答 案 点 数 得 分 需要 DBA 调整数据库吗? 是 否 30 点 0 点 30 需要开发人员调整所写的代码吗? 是 否 30 点 0 点 0 DBA 上一次接受调整培训的时间是什么时候? < 1 年 1~2 年 > 2 年 30 点 20 点 0 点 20 开发人员上一次接受调整培训的时间是什么时候? < 1 年 1~2 年 > 2 年 30 点 20 点 0 点 20 329 DBA 精通 V$视图吗? 是 否 30 点 0 点 30 (续表) 问 题 答 案 点 数 得 分 DBA 精通使用 ADDM、EM 或类似的性能调整工具吗? 是 否 20 点 0 点 20 DBA 接受过 EXPLAIN PLAN 的培训吗? (请参阅第 6 章) 是 没有 20 点 0 点 20 开发人员接受过 EXPLAIN PLAN 的培训吗? 是 没有 20 点 0 点 0 DBA 接受过使用 SQL 调整向导的培训吗? 是 没有 20 点 0 点 20 开发人员接受过使用 SQL 调整向导的培训吗? 是 没有 20 点 0 点 0 教育性能指数(EPI)的示例 总分 160(等级 :B) 评估系统等级 EPI 等 级 评 论 分 数 A+ 大多数系统中的前 10% 250 A 大多数系统中的前 20% 210~249 B 大多数系统中的前 40% 150~209 C 大多数系统中的前 70% 90~149 现在需要帮助 大多数系统中的最后 30% <90 技巧: 测量教育性能指数(EPI)可以帮助您确定提高哪部分的教育会带来好处。 15.315.315.315.3 系统性能指数系统性能指数系统性能指数系统性能指数(SPI)(SPI)(SPI)(SPI) 这个指数用于测试总的系统问题。表 15-3 解释了如何获得一个完美的 SPI 分数。评分系统并不意味 着对总的系统问题的全方位评估,但是它将是一个晴雨表,可以反映所做的改进是否能获得好处。 表 15-3 获得一个完美的 SPI 分数 330 分 类 需要的水平 最 高 分 内部团体上次检查数据库的时间 < 1 年 50 (续表) 分 类 需要的水平 最 高 分 最近一次运行 AWR 或 STATSPACK 的时间 < 1 个月 30 用户提出有关性能的问题的时间 < 2 个月 30 为恢复速度而测试过的备份 是 30 外部团体上次检查数据库的时间 < 1 年 30 外部团体上次检查操作系统的时间 < 1 年 30 统计数据/AWR 频率 自动 20 设计是完全或部分反范式化 部分反范式化 20 为获得增益而使用或测试过并行查询 是 10 系统性能指数(SPI) 全部总和 250 评估系统 问 题 所 需 等 级 最 大 值 分 数 数据库最近一次由商业用户检查是什么 时候? < 1 年 1~2 年 >2 年 50 分 30 分 0 分 50 分 您最近一次运行和检查 AWR 报表或 STATSPACK 的 结果是何时(参见第 14 章)? < 1 个月 1~3 个月 4~6 个月 >6 个月 30 分 20 分 10 分 0 分 20 分 系统中用户上一次提出有关性能的问题是何时, 哪里可以做出改进? < 2 个月 3~6 个月 7~12 个月 >1 年 30 分 20 分 10 分 0 分 20 分 您是否为确定恢复操作所需的时间而测试过备份 计划? 是 否 30 分 0 分 30 分 数据库最近一次由外部团体做检查是什么 时候? < 1 年 1~2 年 30 分 20 分 20 分 331 >2 年 0 分 操作系统最近一次由外部团体做检查是什么 时候? < 1 年 1~2 年 >2 年 30 分 20 分 0 分 30 分 统计数据收集的频率是多少? 自动 每月 不确定 20 分 10 分 0 分 20 分 (续表) 问 题 所 需 等 级 最 大 值 分 数 设计人员是否严格按照第三范式或更高的范式来 设计数据库? 是 *没有设计人员 使用范式 *只有需要时才 使用反范式 10 分 20 分 0 分 20 分 是否评估过并行查询并将其用于有益的 位置? 是 不需要 未测试 10 分 10 分 0 分 10 分 系统性能指数(SPI)的示例 总分 220(A+) 评估系统等级 SPI 等 级 评 论 分 数 A+ 大多数系统中的前 10% > 210 A 大多数系统中的前 20% 180~210 B 大多数系统中的前 40% 140~179 C 大多数系统中的前 70% 80~139 现在需要帮助 大多数系统中的最后 30% <80 技巧: 测量系统性能指数(EPI)可以帮助您确定提高哪部分的系统综合性能可获得好处。 15.415.415.415.4 内存性能指数内存性能指数内存性能指数内存性能指数(MPI)(MPI)(MPI)(MPI) 这个指数衡量内存的使用和分配状况。表 15-4 解释了如何获得一个完美的 MPI 分数。评分系统并不 意味着对内存的使用和分配的全方位评估,而只代表一个晴雨表,反映改进内存的使用和分配状况是否能 获得好处。 332 表 15-4 获得完美的 MPI 分数 分 类 所 需 等 级 最 高 分 缓冲区命中率 > 98% 30 数据字典命中率 > 98% 30 库命中率 > 98% 30 (续表) 分 类 所 需 等 级 最 高 分 内存中的 PGA 排序 > 98% 30 state = 0 时 X$BH 中的缓存 10%~25 % 30 使用最多的前 10 个 SQL 语句占用的内存 < 5% 60 已经调整过的前25个SQL语句(内存使用情况 最糟糕时) 是 30 固定/高速缓存经常使用的对象 是 10 内存性能指数(MPI) 总和 250 15.4.115.4.115.4.115.4.1 缓冲区命中率缓冲区命中率缓冲区命中率缓冲区命中率 缓冲区命中率表示在不需要进行磁盘访问的情况下在内存结构中找到常用数据块的频率。在第三方调 整产品中,使用命中率比以前任何时候都频繁,Oracle 使用它们的次数也比以前更多,主要是因为它们是 很好的晴雨表。但是,命中率也会产生误导,它应该总是用作您展开进一步研究的晴雨表和指示器。没有 人像有些人声称的那样将命中率用作调整系统的唯一方法(我广泛地问过这个问题,没有人这样做)。声称 根本不应该使用它们的人通常不理解它们的价值,也不知道如何使用它们。 不检查它们的 DBA 可能会遗漏主要问题,这些问题本来能够以低成本潜在修复。命中率通常不是优秀 性能的指示器,但通常是不良性能的指示器。最好将它们用作性能改变的晴雨表或指示器。使用动态性能 视图 V$SYSSTAT 可以计算这些统计数据。 查询缓冲区命中率 select (1 - (sum(decode(name, 'physical reads',value,0)) / (sum(decode(name, 'db block gets',value,0)) + sum(decode(name, 'consistent gets',value,0))))) * 100 "Hit Ratio" from v$sysstat; 示例输出 Hit Ratio ---------- 98.8249067 评估 OLTP 系统 问 题 所 需 等 级 最 大 值 分 数 您的缓冲区命中率是多少? < 90% 0点 30 分 333 90%~94% 95%~98% >98% 10 点 20 点 30 点 也可以扩展下面程序清单中的查询,以便在您的结果中包含实际的命中率。下面程序清单中的查询展 示了怎样使用 DECODE 函数来完成这个任务。如果您想在结果中使用分数值,您也可以在本章中的其他查询 中应用这个策略。在 TUSC 中,我们使用一个 PL/SQL 过程来实现这个结果(还将它们图形化显示)。 使用评级查询命中率 select (1 - (sum(decode(name, 'physical reads',value,0)) / (sum(decode(name, 'db block gets',value,0)) + sum(decode(name, 'consistent gets',value,0))))) * 100 "Hit Ratio", decode(sign((1-(sum(decode(name, 'physical reads',value,0)) / (sum(decode(name, 'db block gets',value,0)) + sum(decode(name, 'consistent gets',value,0))))) * 100 - 98),1,30, decode(sign((1-(sum(decode(name, 'physical reads',value,0)) / (sum(decode(name, 'db block gets',value,0)) + sum(decode(name, 'consistent gets',value,0))))) * 100 - 95),1,20, decode(sign((1-(sum(decode(name, 'physical reads',value,0)) / (sum(decode(name, 'db block gets',value,0)) + sum(decode(name, 'consistent gets',value,0))))) * 100 - 90),1,10,0))) "Score" from v$sysstat / 示例输出 Hit Ratio Score ----------- ---------- 99.8805856 30 V$SYSSTAT 中的数据反映所有缓冲池的逻辑和物理读操作的统计数据。要单独获得缓冲池的命中率, 可以查询动态性能视图 V$BUFFER_POOL_STATISTICS。 缓存命中率可用来确认动态性能视图 V$DB_CACHE_ADVICE 中模拟的物理 I/O。该动态性能视图通过提 供预测每个可能缓存大小的物理读操作数量的信息,来提供有助于调整缓存大小的信息。物理读操作因素 包含在数据内,它预测物理读操作的数量,如果将缓存大小调整为指定值,就可能需要改变这些物理读操 作。要使用 V$DB_CACHE_ADVICE,参数 DB_CACHE_ADVICE 应该设置为 ON;在查询视图之前,允许稳定典型 工作量。下面是验证缓存向导模拟的物理 I/O 的查询。 验证物理 I/O 的查询 COLUMN size_for_estimate FORMAT 999,999,999,999 heading 'Cache Size in MB' COLUMN buffers_for_estimate FORMAT 999,999,999 heading 'Buffers' COLUMN estd_physical_read_factor FORMAT 999.99 heading 'Estd Phys Read Fctr' COLUMN estd_physical_reads FORMAT 999,999,999 heading 'Estd Phys Reads' 334 SELECT size_for_estimate, buffers_for_estimate, estd_physical_read_factor, estd_physical_reads FROM V$DB_CACHE_ADVICE WHERE name = 'DEFAULT' AND block_size = (SELECT value FROM V$PARAMETER WHERE name = 'db_block_size') AND advice_status = 'ON' / 示例输出 Cache Size in MB Buffers Estd Phys Read Fctr Estd Phys Reads ---------------- ------------ ------------------- --------------- 4 501 1.36 11,130 8 1,002 1.27 10,427 12 1,503 1.19 9,743 16 2,004 1.00 8,205 20 2,505 .96 7,901 24 3,006 .84 6,856 28 3,507 .81 6,629 32 4,008 .76 6,249 (...Simplistic Listing Displayed) 15.4.215.4.215.4.215.4.2 数据字典缓存命中率数据字典缓存命中率数据字典缓存命中率数据字典缓存命中率 数据字典缓存命中率显示了对数据字典和其他对象的内存读操作所占的百分比。 查询数据字典缓存命中率 select (1-(sum(getmisses)/sum(gets))) * 100 "Hit Ratio" from v$rowcache; 示例输出 Hit Ratio 95.4630137 评估系统 问 题 所 需 等 级 最 大 值 分 数 您的数据字典缓存命中率是多少? < 85% 86%~92% 92%~98% >98% 0 点 10 点 20 点 30 点 20 分 335 15.4.315.4.315.4.315.4.3 库缓存命中率库缓存命中率库缓存命中率库缓存命中率 库缓存命中率显示了对实际语句和 PL/SQL 对象的内存读操作所占的百分比。注意,很高的命中率并 不总是一件好事。请参阅第 4 章的详细解释。 查询库缓存命中率 select Sum(Pins) / (Sum(Pins) + Sum(Reloads)) * 100 "Hit Ratio" from V$LibraryCache; 示例输出 Hit Ratio ---------- 99.9670304 命中率是 99.97%,这表示只有 0.03%的执行会导致再分解。 评估系统 问 题 所 需 等 级 最 大 值 分 数 您的库缓存命中率是多少? < 90% 90%~95% 95%~98% >98% 0 点 10 点 20 点 30 点 30 分 15.4.415.4.415.4.415.4.4 PGA PGA PGA PGA 内存排序命中率内存排序命中率内存排序命中率内存排序命中率 自动 PGA 内存管理简化了分配 PGA 内存的方法。在默认情况下,会激活 PGA 内存管理。在这种模式下 运行时,Oracle 动态调整工作区 PGA 内存的大小(以 SGA 内存大小的 20%为基础)。在自动 PGA 内存管理模 式下运行时,所有会话的工作区大小都是自动的。实例中活动工作区可用的 PGA 内存总量自动由 SORT_AREA_SIZE 或 PGA _ AGGREGATE_ TARGET(首选)初始化参数导出。目标是通过在可能的时候使用 I/O 子系统(在磁盘上),在内存中完成 PGA 中的排序操作。与 PGA 内存排序相关的统计数据可以按照下面的查 询或从 AWR 报表中导出。AWR 报表反映内存和磁盘中排序的综合值,以及它们在内存中的百分比。这些值 反映从实例开始以来的活动。PGA 内存排序率的值应该大于 98%。依据初始化参数 PGA_AGGREGATE_TARGET(或 者用于向后兼容的 SORT _AREA _ SIZE)的值,用户排序可能在内存或者在指定的临时表空间中的磁盘上完 成,如果这个初始化参数不是太高的话。 1. 1. 1. 1. 查询查询查询查询 PGAPGAPGAPGA 内存排序命中率内存排序命中率内存排序命中率内存排序命中率 运行下面的查询可以接收详细的排序统计数据(内存、磁盘和行),或者通过查看 AWR 报表或 STATSPACK 输入文件(report.txt)来接收这些统计数据(有关 STATSPACK 和 AWR 报表的更多信息请参阅第 14 章)。 获得内存和磁盘排序的查询 select a.value "Disk Sorts", b.value "Memory Sorts", round((100*b.value)/decode((a.value+b.value),0,1,(a.value+b.value)),2) 336 "Pct Memory Sorts" from v$sysstat a, v$sysstat b where a.name = 'sorts (disk)' and b.name = 'sorts (memory)'; 示例输出 Disk Sorts Memory Sorts Pct Memory Sorts 16 66977 99.98 评估系统 问 题 需要的水平 最 大 值 分 数 在内存中执行的排序操作所占的比例是多少? < 90% 90%~94% 95%~98% >98% 0 点 10 点 20 点 30 点 30 分 15.4.515.4.515.4.515.4.5 空闲的数据缓冲区的比例空闲的数据缓冲区的比例空闲的数据缓冲区的比例空闲的数据缓冲区的比例 从您首次启动 Oracle 数据库的那一天开始,用户们的查询就开始使用内存。尽管当用户的查询完成 时,这些内存还可以重用,但当下面的查询在系统中运行了两个小时之后,可以很明显地看出缓冲区是如 何被迅速地耗尽的(高容量系统可能要将时间帧变得更短)。空闲的记录数除以 X$BH 表中的记录总数(即所 分配的数据块缓冲区的总数)就得到这个百分比。同时请注意,您必须以 SYS 的权限来运行该查询。此外, 拥有众多的空闲缓冲区并不一定是就最佳环境。请参阅第 13 章有关该表中查询的详细信息。 查询空闲的数据缓冲区 select decode(state,0, 'FREE', 1,decode(lrba_seq,0,'AVAILABLE','BEING USED'), 3, 'BEING USED', state) "BLOCK STATUS", count(*) from x$bh group by decode(state,0,'FREE',1,decode(lrba_seq,0,'AVAILABLE', 'BEING USED'),3, 'BEING USED', state); 示例输出 BLOCK STATUS COUNT(*) ---------------------------------------- ---------- AVAILABLE 7790 BEING USED 1540 FREE 1670 评估系统 问题 所 需 等 级 最 大 值 分 数 在该查询运行了 2 个小时之后,X$BH 表中 state = 0(表空闲)的缓冲区比例是多少? < 5% 0 点 30 分 337 5%~10% 10%~25% >25% 30 点 20 点 0 点 注意: 当空闲比例高于 25%时,您却得 0 分,其理由是因为数据缓冲区设置得太大了,可能会 浪费资源。记住,这只是一个通用的基准—— 您一定需要根据特定的系统作相应修订。 15.4.615.4.615.4.615.4.6 最浪费内存的前最浪费内存的前最浪费内存的前最浪费内存的前 10101010 个语句占所有语句的比例个语句占所有语句的比例个语句占所有语句的比例个语句占所有语句的比例 在没有调整的情况下,大多数系统中 10 个最常使用的 SQL 语句的访问量占了整个系统中内存读操作 的 50%以上。本节测量了最影响性能的代码对整个系统所造成危害的严重性,以百分比表示。 获得这个百分比的脚本 set serverout on DECLARE CURSOR c1 is select buffer_gets from v$sqlarea order by buffer_gets DESC; CURSOR c2 is select sum(buffer_gets) from v$sqlarea; sumof10 NUMBER:=0; mybg NUMBER; mytotbg NUMBER; BEGIN dbms_output.put_line('Percent'); dbms_output.put_line('-------'); OPEN c1; FOR i IN 1..10 LOOP FETCH c1 INTO mybg; sumof10 := sumof10 + mybg; END LOOP; CLOSE c1; OPEN c2; FETCH c2 INTO mytotbg; CLOSE c2; dbms_output.put_line(sumof10/mytotbg*100); END; / 338 示例输出 percent ------- 44.07087097075974761701818030622745652422 PL/SQL procedure successfully completed. 另一个 SQL 语句,但速度更快一点(仅运行于 Oracle 9i 和 10g 中) select sum(pct_bufgets) "Percent" from (select rank() over ( order by buffer_gets desc ) as rank_bufgets, to_char(100 * ratio_to_report(buffer_gets) over (), '999.99') pct_bufgets from v$sqlarea ) where rank_bufgets < 11; Percent ---------- 44.03 评估系统 问 题 所 需 等 级 最 大 值 分 数 在V$SQLAREA 视图中获得使用最多的10 个使 用内存读语句占全部内存读操作的比例是多 少? > 25% 20%~25% 5%~19% < 5% 0 点 30 点 50 点 60 点 60 分 15.4.715.4.715.4.715.4.7 调整前调整前调整前调整前 25252525 个最浪费内存的语句个最浪费内存的语句个最浪费内存的语句个最浪费内存的语句 在没有做调整的情况下,在绝大多数的系统中,访问量占前 25 位的语句的内存读操作将占用整个系 统所有的内存读操作的 75%。下面的代码展示了对这些语句的评测,并解释了如何找到这 25 个最占用内存 的语句。 查询获得 25 个最占用内存的语句 set serverout on size 1000000 declare top25 number; text1 varchar2(4000); x number; len1 number; cursor c1 is select buffer_gets, substr(sql_text,1,4000) from v$sqlarea order by buffer_gets desc; begin dbms_output.put_line('Gets'||' '||'Text'); dbms_output.put_line('----------'||' '||'----------------------'); 339 open c1; for i in 1..25 loop fetch c1 into top25, text1; dbms_output.put_line(rpad(to_char(top25),9)||' '||substr(text1,1,66)); len1:=length(text1); x:=66; while len1 > x-1 loop dbms_output.put_line('" '||substr(text1,x,66)); x:=x+66; end loop; end loop; end; / 部分示例输出 SQL> @pl4 Gets Text 16409 select f.file#, f.block#, f.ts#, f.length from fet$ f, ts$ t where " e t.ts#=f.ts# and t.dflextpct!=0 6868 select job from sys.job$ where next_date < sysdate order by next " t_date, job 6487 SELECT BUFFER_GETS,SUBSTR(SQL_TEXT,1,3500) FROM V$SQLAREA ORDER " BY BUFFER_GETS DESC 3450 SELECT BUFFER_GETS,SUBSTR(SQL_TEXT,1,4000) FROM V$SQLAREA ORDER " BY BUFFER_GETS DESC (...Simplistic Partial Listing Displayed) 评估系统 问 题 所 需 等 级 最 大 值 分 数 在 V$SQLAREA 视图中前 25 个最占用内存的 语句中,您尝试着调整了多少个? 0 1~5 6~15 16~25 0 点 10 点 20 点 30 点 30 分 15.4.815.4.815.4.815.4.8 固定固定固定固定////缓存对象缓存对象缓存对象缓存对象 如果这些对象是常用对象,您就可以使用 DBMS_SHARED_POOL.KEEP 将它们固定在内存中,如第 10 章 所述。您还可以在创建表时将该表固定在内存中,或者使用一个 ALTER 命令来缓存表。请参阅第 7 章有关 缓存表的详细信息。 我推荐您固定下面的数据包: DBMS_ALERT DBMS_DESCRIBE 340 DBMS_DDL DBMS_LOCK DBMS_OUTPUT DBMS_PIPE DBMS_SESSION DBMS_SHARED_POOL DBMS_STANDARD DBMS_UTILITY STANDARD 评估系统 问 题 所 需 等 级 最 大 值 分 数 您在需要的时候会固定 PL/SQL 对象或者缓 存表吗? 是/不需要 不会 10 点 0 点 10 分 内存性能指数示例 总分 230(A) 评估系统等级 MPI 等 级 评 论 分 数 A+ 大多数系统中的前 10% > 230 A 大多数系统中的前 20% 200~230 B 大多数系统中的前 40% 160~199 (续表) MPI 等 级 评 论 分 数 C 大多数系统中的前 70% 100~159 现在需要帮助 大多数系统中的最后 30% <100 技巧: 测量内存性能指数(MPI)可以帮助您确定对内存分配和使用做哪些改进会有所帮助。 15.515.515.515.5 磁盘性能指数磁盘性能指数磁盘性能指数磁盘性能指数(DPI)(DPI)(DPI)(DPI) 这个指数测量磁盘的使用状况。表 15-5 解释了如何获得一个完美的 DPI 分数。评分系统并不意味着 对磁盘使用的全方位评估,而只是一个晴雨表,反映改进磁盘的使用是否能获得好处。随着 SAN 和其他磁 盘及磁盘缓存技术的出现,您需要修改该评分系统,使其更加适合您的系统。Oracle 特性例如 LMT(本地管 理的表空间)和 ASSM(自动段究竟管理)应该重点考虑(请参阅第 4 章参考这些特性的更多信息。) 表 15-5 获得一个完美的 DPI 分数 分 类 所 需 等 级 最 高 分 已调整过使用最多的前 25 个语句(磁盘性能 糟糕) 是 40 使用最多的前 10 个语句的磁盘使用率 < 5% 60 配置的表/索引 无 30 LMT/ASSM 中的关键任务表 是 30 重做日志/撤消/数据分离 是 30 341 自动撤消管理磁盘 是 30 用于临时表空间的磁盘数 > 2 30 磁盘性能指数(DPI) 总和 250 15.5.115.5.115.5.115.5.1 调整滥用磁盘读操作的调整滥用磁盘读操作的调整滥用磁盘读操作的调整滥用磁盘读操作的 25252525 个主要语句个主要语句个主要语句个主要语句 我发现在没有作调整的情况下,在绝大多数的系统中,访问量占前 25 位的语句的磁盘读操作将占用 整个系统所有磁盘和/或内存读操作的 75%。本节列举了整个系统中 25 个最严重的磁盘读操作。下面的示 例展示了一个调整过的系统,其中只显示了对数据字典的查询: 查询 25 个滥用磁盘读操作的最主要语句 set serverout on size 1000000 declare top25 number; text1 varchar2(4000); x number; len1 number; cursor c1 is select disk_reads, substr(sql_text,1,4000) from v$sqlarea order by disk_reads desc; begin dbms_output.put_line('Reads'||' '||'Text'); dbms_output.put_line('----------'||' '||'----------------------'); open c1; for i in 1..25 loop fetch c1 into top25, text1; dbms_output.put_line(rpad(to_char(top25),9)||' '||substr(text1,1,66)); len1:=length(text1); x:=66; while len1 > x-1 loop dbms_output.put_line('" '||substr(text1,x,66)); x:=x+66; end loop; end loop; end; / 部分示例输出: Reads Text 1156 select file#, block#, ts# from seg$ where type# = 3 122 select distinct d.p_obj#,d.p_timestamp from sys.dependency$ d, obj " j$ o where d.p_obj#>=:1 and d.d_obj#=o.obj# and o.status!=5 342 111 BEGIN sys.dbms_ijob.remove(:job); END; (...Simplistic Partial Listing Displayed) 评估系统 问 题 所 需 等 级 最 大 值 分 数 在 V$SQLAREA 视图中的前 25 个最浪费磁盘 读操作的语句中,您尝试着调整了多少个? 0 1~5 6~15 16~25 0 点 10 点 20 点 40 点 40 分 15.5.215.5.215.5.215.5.2 最浪费磁盘读操作的前最浪费磁盘读操作的前最浪费磁盘读操作的前最浪费磁盘读操作的前 10101010 个语句占所有语句的比例个语句占所有语句的比例个语句占所有语句的比例个语句占所有语句的比例 本节测量了最影响性能的代码对整个系统所造成危害的严重性,以百分比表示。 检索这个百分比的脚本 Set serverout on; DECLARE CURSOR c1 is select disk_reads from v$sqlarea order by disk_reads DESC; CURSOR c2 is select sum(disk_reads) from v$sqlarea; Sumof10 NUMBER:=0; mydr NUMBER; mytotdr NUMBER; BEGIN dbms_output.put_line('Percent'); dbms_output.put_line('-------'); OPEN c1; FOR i IN 1..10 LOOP FETCH c1 INTO mydr; sumof10 := sumof10 + mydr; END LOOP; CLOSE c1; OPEN c2; FETCH c2 INTO mytotdr; CLOSE c2; dbms_output.put_line(sumof10/mytotdr*100); END; 343 / 示例输出 Percent 5.5183036 另一条/简单快速的 SQL 语句 select sum(pct_bufgets) from ( select rank() over ( order by disk_reads desc ) as rank_bufgets, to_char(100 * ratio_to_report(disk_reads) over (), '999.99') pct_bufgets from v$sqlarea ) where rank_bufgets < 11; SUM(PCT_BUFGETS) ---------------- 68.59 评估系统 问题 所需等级 最大值 分数 在V$SQLAREA视图中获得10 个使用磁盘读操 作最多的语句。它们占全部内存读操作的比 例是多少? > 25% 20%~25% 5%~19% < 5% 0 点 30 点 50 点 60 点 50 分 15.5.315.5.315.5.315.5.3 表表表表////索引的分离索引的分离索引的分离索引的分离 表和与它们相关联的索引应当放置在不同的物理磁盘上,以便减少文件 I/O。这当然更难实现,因为 DBA 通常不知道它们在哪里(由于管理 SAN 的方法的原因)。第 3 章已经详细讨论过这个问题,并提供了便 于理解该问题的查询。注意,如果使用 ASM(也在第 1 章和第 3 章讨论过),您应该确保添加采用重新平稳 提示的新磁盘的时间,第 3 章列出了这些提示。 评估系统 问 题 所 需 等 级 最 大 值 分 数 表和与它们相关联的索引是否放置在同一 个物理磁盘或磁盘阵列上? 是 磁盘阵列 否 0 点 20 点 30 点 30 分 15.5.415.5.415.5.415.5.4 关键任务表管理关键任务表管理关键任务表管理关键任务表管理 TUSC 通常建议的管理关键任务表的方法是:将它们保存在使用自动段空间管理(ASSM)的本地管理表空 间(LMT)中。同时,行链接(通常当表保存在字典管理的表空间时)也需要重构某些对象。 使用自动段空间管理实现的表空间有时称为位图表空间。有些本地管理的表空间使用位图段空间管 344 理。要使用自动段空间管理,就要创建本地管理的表空间,同时将段空间管理子句设置为 AUTO。本地管理 的表空间中的自动段空间管理不需要指定 PCTUSED、FREELISTS 和 FREELIST GROUPS 参数。如果可能,请从 手动空间管理转换为自动段空间管理。 更新表并且记录更新的数据块没有足够空间保存更改时,记录就会“链接”到另一个数据块。在这种 情况下,记录跨越多个数据块,在大多数情况下,会创建其他 I/O。通过分析链接行的表和查询 CHAINED_ROWS 表,就可能确定具有链接记录的表。 使 用 脚 本 utlchain.sql 创 建 CHAINED_ROWS 表,它驻留在 $ORACLE_HOME/rdbms/admin 目录下的一个文件里,Oracle 软件就在这个目录下(注意,准确名称和位置可 能随着操作平台不同而不同)。要填充 CHAINED_ROWS 表,可以使用 ANALYZE 命令。ANALYZE 命令有一个选 项可以确定表中链接的行,如下所示: ANALYZE table TABLE_NAME list chained rows into chained_rows; Table analyzed. 该命令会将输出放到名为 CHAINED_ROWS 的表中。下面的查询会选择 CHAINED _ROWS 表中信息量最大 的列: SELECT OWNER_NAME, /*owner of object*/ TABLE_NAME, /*Name of table*/ CLUSTER_NAME, /*Name of cluster if applicable*/ HEAD_ROWID, /*ID of the first part of the row*/ ANALYZE_TIMESTAMP /*Timestamp associated with last analyze*/ FROM CHAINED_ROWS; 评估系统 问 题 所 需 等 级 最 大 值 分 数 关健任务表保存在使用 ASSM 的 LMT 中吗?您 讨论过链接问题吗? 否 是 0 点 30 点 30 分 15.5.515.5.515.5.515.5.5 分离关键的分离关键的分离关键的分离关键的 OracleOracleOracleOracle 文件文件文件文件 让通常访问的 Oracle 数据文件彼此分离,这样有助于排除 I/O 瓶颈,也有助于消除磁盘内存缓冲的 潜在饱和度。分离重点文件(特别是重做日志)通常会提高性能。 评估系统 问 题 需 要 等 级 最 大 值 分 数 撤消日志与数据库数据文件是否在不同的磁 盘上? 是 磁盘阵列 无 30 点 20 点 0 点 20 分 15.5.615.5.615.5.615.5.6 自动撤消管理自动撤消管理自动撤消管理自动撤消管理 TUSC 建议尽可能地使用自动撤消管理。以这种方式配置时,数据库以要运行的时间查询为基础,自动 确定保存撤消数据的时间。据说保存在时间窗口内的撤消数据处于未终止状态。这个时间之后,撤消数据 345 的状态就改变为终止。撤消数据只有当处于终止状态时才是重写的最好选择。Oracle 将撤消数据保持在未 终止状态下的时间长度取决于表空间的配置。使用数据库配置助手(Database Configuration Assistant, DBCA)创建数据库时,撤消表空间被设置为默认来自动扩充自己,为运行时间最长的查询维护未终止的撤消。 使用固定大小的撤消表空间时,Oracle 自动将指定大小的表空间的撤消数据尽可能长时间地保持在未 终止状态。如果撤消表空间没有足够的空闲或终止空间来保存由当前事务生成的活动的撤消数据,那么 Oracle 可能被迫重写未终止的撤消数据。这种情况可能导致长时间运行的查询失败,并会发出错误或警报。 在不可能使用自动撤消管理的情况下,可以激活自动扩展,这需要手动调整表空间的大小。在这种情 况下,要确保表空间足够大,足以满足长时间运行查询读操作一致性的需要。 如果使用闪回特性,就要确保表空间足够大,能够容纳闪回操作。下面列举的查询评估在不同条件下 调整撤消表空间大小时所需的字节数。 下面的信息适用于撤消查询 A、B 和 C。调整撤消表空间大小需要 3 个方面的信息: ● (UR) UNDO_RETENTION,以秒计。 ● (UPS) 每秒生成的撤消数据块数量。 ● (DBS) 依据范围和文件大小变化的系统开销(db_block_size) UndoSpace = (UR * (UPS * DBS) + DBS) 或者,当估计的数值等于 0 时,就可以给系统开销(DBS)添加一个乘数(24),以便得出更合理的结果: UndoSpace = [UR * (UPS * DBS)] + (DBS * 24) 可以从初始化文件 UNDO_ RETENTION 和 DB_BLOCK_SIZE 中获得这两个方面的信息。第三块公式需要查 询数据库。每秒生成的撤消数据块数量可以从 V$UNDOSTAT 视图中获得,如下所示: SELECT (SUM(undoblks))/ SUM ((end_time - begin_time) * 86400) FROM v$undostat; 要将天数转换成秒数,可以乘以一天的秒数 86400。查询结果返回每秒撤消数据块的数量。这个值需 要乘以每个撤消数据块的大小,这个大小与 DB_BLOCK_SIZE 中定义的数据库数据块的大小相同。 下面的查询表示以执行时间内每秒生成的撤消数据块为基础的时间点评估。没有分配撤消空间时,可 以使用这个查询。如果这个时间帧在高活动过程中或者是撤消的最糟糕情况中,那么导出的结果就能提供 一个好的评估。 撤消查询 A SELECT (UR * (UPS * DBS) + DBS) AS "Bytes" FROM (SELECT value AS UR FROM v$parameter WHERE name = 'undo_retention'), (SELECT (SUM(undoblks) / SUM( ((end_time - begin_time) * 86400))) AS UPS FROM v$undostat), (SELECT value AS DBS 346 FROM v$parameter WHERE name = 'db_block_size'); Bytes ---------- 126630.554 如果撤消查询 A 导出的结果由于在时间帧过程中的活动而偏低,那么就可以利用撤消查询 B 来导出结 果,如下所示: 撤消查询 B SELECT (UR * (UPS * DBS)) + (DBS * 24) AS "Bytes" FROM (SELECT value AS UR FROM v$parameter WHERE name = 'undo_retention'), (SELECT (SUM(undoblks)/SUM(((end_time - begin_time)*86400))) AS UPS FROM v$undostat), (SELECT value AS DBS FROM v$parameter WHERE name = 'db_block_size'); Bytes ---------- 335717.434 在已经分配撤消空间并且数据库已经运行一段时间的情况下,下面的查询才有效: 撤消查询 C SELECT /*+ ordered */ d.file_name, v.status, TO_CHAR((d.bytes / 1024 /1024), '99999990.000'), NVL(TO_CHAR(((d.bytes - s.bytes) / 1024 /1024), '99999990.000'), TO_CHAR((d.bytes / 1024 / 1024), '99999990.000')), d.file_id, d.autoextensible, d.increment_by, d.maxblocks FROM sys.dba_data_files d, v$datafile v, (SELECT file_id, SUM(bytes) bytes FROM sys.dba_free_space WHERE tablespace_name ='NAME_OF_UNDO_TABLESPACE' GROUP BY file_id) s WHERE (s.file_id (+)= d.file_id) AND (d.tablespace_name = 'NAME_OF_UNDO_TABLESPACE') AND (d.file_name = v.name); 如果您捕获了撤消查询 A 的顶点,那么撤消查询 C 可能同样处于这个时间点(此时两个查询会遇到一 起)。 理想情况下,撤消段应该彼此分离,最好是与保存表和索引的磁盘分离(它们在这些表和索引上运行)。 如果您的系统很小,那么就不可能分离撤消段。同时,用户执行的 DML 语句的数量应该确定最佳撤消段的 真实数量。这会随着情况不同而存在巨大变化(请在您的系统上使用自己感觉最好的)。 347 查询 select segment_name, file_name from dba_data_files, dba_rollback_segs where dba_data_files.file_id = dba_rollback_segs.file_id; 示例输出(旧的回滚段方法) SEGMENT_NAME FILE_NAME RBS1 /disk1/oracle/rbs1.dbf RBS2 /disk2/oracle/rbs2.dbf RBS3 /disk3/oracle/rbs3.dbf RBS4 /disk4/oracle/rbs4.dbf RBS5 /disk1/oracle/rbs1.dbf RBS6 /disk2/oracle/rbs2.dbf RBS7 /disk3/oracle/rbs3.dbf RBS8 /disk4/oracle/rbs4.dbf 示例输出(新的撤消表空间方法) SEGMENT_NAME FILE_NAME ------------- ------------------------------------------------------- SYSTEM C:\ORACLE\PRODUCT\10.1.0\ORADATA\ORCL\SYSTEM01.DBF _SYSSMU11$ C:\ORACLE\PRODUCT\10.1.0\ORADATA\ORCL\UNDOTBS01.DBF _SYSSMU10$ C:\ORACLE\PRODUCT\10.1.0\ORADATA\ORCL\UNDOTBS01.DBF _SYSSMU9$ C:\ORACLE\PRODUCT\10.1.0\ORADATA\ORCL\UNDOTBS01.DBF _SYSSMU8$ C:\ORACLE\PRODUCT\10.1.0\ORADATA\ORCL\UNDOTBS01.DBF _SYSSMU7$ C:\ORACLE\PRODUCT\10.1.0\ORADATA\ORCL\UNDOTBS01.DBF _SYSSMU6$ C:\ORACLE\PRODUCT\10.1.0\ORADATA\ORCL\UNDOTBS01.DBF _SYSSMU5$ C:\ORACLE\PRODUCT\10.1.0\ORADATA\ORCL\UNDOTBS01.DBF _SYSSMU4$ C:\ORACLE\PRODUCT\10.1.0\ORADATA\ORCL\UNDOTBS01.DBF _SYSSMU3$ C:\ORACLE\PRODUCT\10.1.0\ORADATA\ORCL\UNDOTBS01.DBF _SYSSMU2$ C:\ORACLE\PRODUCT\10.1.0\ORADATA\ORCL\UNDOTBS01.DBF _SYSSMU1$ C:\ORACLE\PRODUCT\10.1.0\ORADATA\ORCL\UNDOTBS01.DBF 评估系统 问 题 需 要 等 级 最 大 值 分 数 自动撤消管理? 否 是 0 点 30 点 30 分 15.5.715.5.715.5.715.5.7 临时段的平衡临时段的平衡临时段的平衡临时段的平衡 当在 init.ora 中指定的 SORT_AREA_SIZE 的大小不足以满足排序操作时,用户将要在他们先前指定的 临时表空间中进行排序操作。如果磁盘上存在大量的排序操作,您应当确保用户在不同的磁盘上完成排序 工 作 。 如 果 您 使 用 了 TEMPFILE,那么在下面程序清单的查询中您应当用 DBA_TEMP_FILES 代 替 DBA_DATA_FILES 来获得输出结果。在 Oracle 10g 中,您应该使用 TEMPFILES,它是本地管理的临时表空间 的另一个名称。TEMPFILES 的一些优点包括: 348 ● 不需要检查数据字典临时表空间的空闲空间,因为 TEMPFILES 使用本地管理的表空间(LMT)。 ● 使用 TEMPFILES 的本地管理范围更广,它自动跟踪邻近的空闲空间,因此不需要结合。 ● TEMPFILES 总是设置为 NOLOGGING,您不能重命名 TEMPFILE,不能将 TEMPFILE 设置为只读,也 不能使用 alter database 命令来创建 TEMPFILE。 查询 select username, file_name from dba_data_files, dba_users where dba_data_files.tablespace_name = dba_users.temporary_tablespace; 示例输出 USERNAME FILE_NAME SYS /disk1/oracle/sys1orcl.dbf TEDP /disk1/oracle/tmp1orcl.dbf SANDRA /disk1/oracle/tmp1orcl.dbf TEDR /disk1/oracle/tmp2orcl.dbf ROB /disk1/oracle/tmp2orcl.dbf DIANNE /disk1/oracle/tmp2orcl.dbf RICH /disk1/oracle/tmp2orcl.dbf DONNA /disk1/oracle/tmp3orcl.dbf DAVE /disk1/oracle/tmp3orcl.dbf ANDREA /disk1/oracle/tmp3orcl.dbf MIKE /disk1/oracle/tmp3ora.dbf 评估系统 问 题 所 需 等 级 最 大 值 系统中的用户在使用临时表空间进行排序 时,可以得到警告。您为此使用了多少个 磁盘? 系统中的全部磁盘 1 2 > 2 分数 0 分 10 分 20 分 30 分 10 分 磁盘性能指数的示例(DPI) 总分 210(A) 评估系统等级 DPI 等 级 评 论 分 数 A+ 大多数系统中的前 10% > 235 A 大多数系统中的前 20% 205~235 B 大多数系统中的前 40% 170~204 C 大多数系统中的前 70% 110~169 现在需要帮助 大多数系统中的最后 30% <110 349 技巧: 测量磁盘性能指数(DPI)可以帮助您确定改进哪些领域的磁盘操作可能会带来好处。 15.615.615.615.6 总体性能指数总体性能指数总体性能指数总体性能指数(TPI)(TPI)(TPI)(TPI) 总体性能指数是内存、磁盘、教育和系统指数的总和,如下表所示。 分 类 指 数 最 高 分 教育性能指数(EPI) 250 系统性能指数(SPI) 250 内存性能指数(MPI) 250 磁盘性能指数(DPI) 250 总体性能指数(TPI) 1000 指 数 总 分 教育性能指数的示例(EPI) 150(B) 系统性能指数的示例(SPI) 220(A+) 内存性能指数的示例(MPI) 230(A) 磁盘性能指数的示例(DPI) 200(B) 总体性能指数的示例(TPI) 800(A) 评估系统等级 TPI 等 级 评 论 分 数 A+ 大多数系统中的前 10% > 925 A 大多数系统中的前 20% 795~924 B 大多数系统中的前 40% 620~794 C 大多数系统中的前 70% 380~619 现在需要帮助 大多数系统中的最后 30% <380 技巧: 测量总性能指数(TPI)可以帮助您确定系统瓶颈;它是评测系统综合性能的一个简单的晴 雨表。 15.715.715.715.7 系统综合检查的示例系统综合检查的示例系统综合检查的示例系统综合检查的示例 下面是评估等级的示例。您可以使用下面的评分规则来对您的系统进行年度检查。有些项(例如备份 和恢复评测)涉及的并不是很深。本节的目标是帮助您考虑可能需要检查的领域。这并不是一个实际的客户 系统的检查,但对一些评测稍做修改就可以帮助您生成自己的评测模板中的讨论项。主要的目的是为了使 您对评测有个概念。 15.7.115.7.115.7.115.7.1 评级系统评级系统评级系统评级系统 将下面的评级报表示例作为您进行详细的评测和定级的基准。如果评测内容中包含了亟待改进或关注 350 (在适当的地方)的评级项,您将可以更轻松地得到管理者支持。在大多数情况下,DBA 需要得到管理层的 支持来赢得时间,以便解决系统中主要的问题。有些时候,只有系统已经启动并运行后,更高的管理层才 认识到改变的必要性。这种评测将是确定所需改变的催化剂。 等 级 分 数 评 论 A+ 系统检查的前 5% 优秀 A 前 10% 优良 A- 前 15% 很好 (续表) 等 级 分 数 评 论 B、B+、B- 前25% 好/可以进一步提高 C、C+、C- 前50% 需要提高 D、D+、D- 最后 50% 亟待提高 F 最后 10% 需要立即更正错误 技巧: 您的系统应当由一个外部团体来进行年度检查,或者,至少由您公司内部的某个人来检 查。 15.7.215.7.215.7.215.7.2 系统检查评级分类的示例系统检查评级分类的示例系统检查评级分类的示例系统检查评级分类的示例 表 15-6 总结了该系统检查的结果。尽管前文已经对 TPI 的部分分类进行了讨论,但本节对 TPI 做了 更深一步的探讨。本节的后面简要描述了一些本书推荐进行的改变,而 TPI 的评级可以出现在本节之前或 之后。本节的观点是非常主观的,所以您所尊敬的有经验的人应当对这些方法做出判断。评级系统应当包 含比这里提供的示例更加细节的评论。应详细描述推荐的改变,并提供技术支持文档。 注意: 这是一个示例,不是实际的检查。 表 15-6 系统评测结果的示例 分 类 等 级 评 论 综合检查 C- 这个系统运行状况很糟糕,因为给数据处理分配的内存数量不足。不 少地方需要马上改正,以尽量提高系统性能,尤其是增加了其他用户 时 体系结构 B 总体结构较好,但需检查使用缓存的 SAN,以便改进 I/O 在可用控制 器中的分布 硬件大小 A- 硬件的大小对商业活动正合适,但系统没有被调整,未充分利用硬件 的潜力 安全性 F 密码从来没有改变过,即使有雇员离开了公司。许多没有保护的文件 中保存有硬编码的密码。默认账户在不用的情况下没有锁定。这是不 可接受的!没有检查 Oracle 在 otn.oracle.com/ deploy/security 上 提供的安全性检查列表 351 内存分配 B+ 既然拥有 2GB 的内存,应可以为 DB_CACHE_SIZE 分配更多的内存 (续表) 分 类 等 级 评 论 数据库调整 D- 使用最多的前 25 个查询占用了全部资源的 98%。没有调整这些查询 的举措 磁盘配置 B 磁盘 I/O 的平衡分布较合理,但可以对一些访问量非常高的表和索 引使用独立分区,以提高性能 重做日志 B+ 重做日志的大小较适合,但您可能想再增加一些,这样在批处理时 切换的速度可以更快一些 归档日志文件 A+ 包含归档日志文件的文件系统和其他 Oracle 的文件系统相独立。 归档文件被保存在磁带上,但仍然可保存在磁盘上,以提供快速恢 复 回滚段 A+ 实现并调整过自动撤消管理 控制文件 A- 多个控制文件分别位于不同的物理磁盘上,但不存在用于 TRACE 的 控制文件的备份 初始化参数 A+ 系统可用的内存有 2GB 表设计 C- 没有数据库级的参照完整性 表 C- 表没有得到合理的分区,某些大型表上没有设置并行化处理。某些 较小的表需要做适当的修改,以便缓存到内存中 索引 C- 应当对索引执行更多的分区。对于只提供查询的表中低基数(少量 唯一的数据行)的列,没有使用位图索引 表空间 C+ 对于未来的增长而言,表空间显得严重不足 15.7.315.7.315.7.315.7.3 需要立刻解决的问题项需要立刻解决的问题项需要立刻解决的问题项需要立刻解决的问题项 在评测了系统之后,您需要列出需要立即解决的所有问题项。下面的列表是总结了部分需要立即采取的行 动: ● 不使用默认账户时就锁定它!马上这样做。 ● 改变所有默认的密码。改变所有用户的密码,因为当前的安全性是折中处理后的结果。 ● 立即增加 DB_CACHE_SIZE 的尺寸!如果 SGA_MAX_SIZE 不够大,可在 Oracle 10g 的系统启动时修 改该参数。您也可以使用 SGA_TARGET,这样 DB_CACHE_SIZE 只用途最小值(更多信息请参阅第 1 章和第 4 章)。 ● 调整引起磁盘和内存读操作的前 25 个查询。 技巧: 系统评测应当永远包含需要立即解决的项目。这将确保改进措施所需的时间和费用可以 及时得到保证。 15.7.415.7.415.7.415.7.4 其他需要解决的问题项其他需要解决的问题项其他需要解决的问题项其他需要解决的问题项 在绝大多数紧迫的问题被解决之后,下文列出了第 2 张列表,包含需特别注意的一些问题项目。您的 列表应当包含如何采取更正行动的细节信息: 352 ● 在当前的系统增长率下,至少应保证每一个季度就监测前文文档中详细描述的项目。 ● 确保产品中的 SYS 和 SYSTEM 的密码不同于开发期间的密码。 ● 对目前数据库中过大或过小的对象进行调整。 ● 每个季度至少更换一次密码。 ● 修改文件保护策略,使用户不能删除 Oracle 的系统软件。 ● 从脚本和备份工作中删除硬编码的密码。 ● 考虑为产生低性能磁盘读操作最多的前 25 个查询增加额外的索引,以便提高查询性能。 如果对 init.ora 做出了修改,您应当编译一个列表来包含当前值和建议的值。请参阅附录 A 中关于 init.ora 参数的完整列表和详细描述。最后,请确保在改变之后重新检查,以确保一切运行正常。 15.815.815.815.8 系统信息列表系统信息列表系统信息列表系统信息列表 本节描述了一些您应当在评测过程中搜集并保存的信息。当您回顾以前的评测时,您需要知道当时做 评测时系统中的参数设置。任何参与评级的项目(例如备份和恢复)都放在其中。我还提供了一个 DBA 评测 示例,用来解释一些可能需要评测的领域。明智的做法是请其他人来评定您 DBA 的水平,这样您才能不断 地提高。本节的内容为适应本书而做了极大的简化。它是一个快速列表,旨在提供系统的全局图。 15.8.115.8.115.8.115.8.1 与与与与内存有关的值内存有关的值内存有关的值内存有关的值 考察系统中与内存有关的问题(下面还提供了一些答案示例): ● 当前硬件中内存大小为多少?4GB ● 当前用户数是多少?总计 500 个/ 50 个并发用户 ● 将来的用户数会达到多少?未来三个月中可能会有 100~150 个并发用户 ● 系统中还使用了其他什么软件?均不造成重要的影响 ● 系统是客户机/服务器模式,还是浏览器/服务器模式?浏览器/服务器模式 ● 所需要的反映时间?秒级以下。OLTP 事务占主要部分 ● 数据库有多大?数据库目前是 9.5TB,还有 100GB 空闲空间 ● 经常访问的表有多大?平均 100 万行 ● 未来有什么软件将影响内存的使用?没有 ● 您将应用其他的特性或选项吗?6 个月内将应用 Oracle Streams,两年内应用 Oracle Grid。 15.8.215.8.215.8.215.8.2 与磁盘有关的值与磁盘有关的值与磁盘有关的值与磁盘有关的值 下面是与磁盘有关的问题及答案: ● 可以为硬件提供的最大 SAN 容量是多少?当前容量的 20 倍 ● 磁盘可用空间有多大?不知道 ● 一年以后数据库会变成多大?当前基础上增加 10% ● 数据库文件系统/OS 使用了 RAID(删除)吗?是的,RAID1+0 ● 将来会有多路复用的重做日志吗?是的 ● 将要安装新的软件吗?短时间内不会 ● 将要安装哪些系统实用程序?Quest 实用程序、Veritas 存储基础 ● 每晚将发生什么样的 EDI 转移操作?批量有序转移操作 353 15.8.315.8.315.8.315.8.3 与与与与 CPUCPUCPUCPU 有关的值有关的值有关的值有关的值 下面是与 CPU 相关的问题及答案: ● 当前硬件中处理器数量的最大值是多少?当前 6 个/最多 12 个 ● 未来有升级的途径吗?是的,可以升级到 64 个处理器 ● 事务处理负载多大?CPU 平均负载 60%/最大 90% ● 批量加载的情况如何?晚间负载较大/白天没有问题 ● 应用了热备份吗?在存档时使用了 RMAN 备份 ● 白天运行批处理操作吗?未运行任何影响性能的批处理操作 ● 以后将使用并行查询吗?目前部分处理中已经使用 ● 以后是否采用分布式配置?是的,通过 Oracle Streams 15.8.415.8.415.8.415.8.4 与备份和恢复有关的信息与备份和恢复有关的信息与备份和恢复有关的信息与备份和恢复有关的信息 下面是与备份和恢复有关的问题及答案: ● 系统是按照 7 天/24 小时全天候运行的吗?不是,是 6 天/24 小时运行 ● 恢复操作(或者磁盘备份)的速度有多快?最多 12 小时 ● 有“备用”的磁盘来防止系统故障吗?没有,HP 主机每 4 小时轮转一次 ● 已备份多少数据?它们是带奇偶校验的“磁带”备份吗?不知道 ● 使用了 UPS 吗?是的 ● 采用了导出文件吗?没有 ● 冷备份过程的状况如何?不适用 ● 导出过程的状况如何?需要提高 ● 热备份过程的状况如何?很好 ● 闪存恢复尺寸合适吗?是的 ● 灾难恢复过程的状况如何?需要提高(建议使用数据哨兵) 下面是一个您对备份和恢复操作评级时,可能需评估领域的示例。Oracle DBA Tips and Techniques 一书中对该评级系统进行了深入讨论。该布局与您的系统评测布局应当是一样的。 分 类 等 级 评 论 综合的备份和恢复 A 应当生成替代所有已备份文件的脚本 备份过程 A 优秀 归档过程 A 优秀 恢复过程 A- 应当包含准备用于恢复的脚本 备份的知识 A 很好 恢复的知识 A 很好 灾难备份 A+ 优秀 灾难恢复 A 很好,仍将采用前滚 15.8.515.8.515.8.515.8.5 命名约定和命名约定和命名约定和命名约定和////或标准以及安全信息或标准以及安全信息或标准以及安全信息或标准以及安全信息 354 下面是与命名约定、标准或安全信息相关的问题及答案: ● 复查所使用的命名约定:优秀 ● 检查 Oracle 关键文件的保护:糟糕 ● 检查数据库安全过程:糟糕 ● 检查密码过程:糟糕 ● 复查 otn.oracle.com/deploy/security checklist:未完成 15.8.615.8.615.8.615.8.6 DBA DBA DBA DBA 知识评级知识评级知识评级知识评级 由一个公正的专家来执行所有的 DBA 评测,这对识别和提高一个 DBA 的技巧极为重要。通常情况下, 主管 DBA 太忙,以至于没有时间参加新版本的 Oracle 软件技能的培训和提高。该评级系统有助于指出他们 的强项和弱点。如果该评测只是用于反对一个人的话,那不会起到任何作用。它必须用于确认和提高 DBA 技能的目的: 分 类 等 级 DBA 的综合知识 A Oracle 体系结构 A- Oracle 对象 B+ Oracle 内部设置 B+ Oracle 初始化参数 B+ Oracle 查询的调整 A Oracle 数据库的调整 A Oracle 备份 A Oracle 恢复 A Oracle 实用程序 A 操作系统 B+ 注意: 您应当只有在出于提高 DBA 技能的目的下,才去评测 DBA 的能力。评测一个人是非常敏 感的问题,必须由以提高 DBA 技能为首要目的的人来完成这件工作。 15.915.915.915.9 TPI TPI TPI TPI 和系统检查需要考虑的其他项和系统检查需要考虑的其他项和系统检查需要考虑的其他项和系统检查需要考虑的其他项 正如本章前面所述,我将提供最基本的晴雨表作为启动指南。对于您特定的系统而言,有些事情可能 重要,有些可能不重要。我认为在您开发的系统检查和为系统自定义的 TPI 中要重点考虑下面这些项: ● 所有 Oracle 工具都是 SYSAUX 表空间中的计划对象吗? ● 您考虑过在存储体系结构中使用 ASM 吗? ● 如果您经常移动大量数据,您都会使用可传输的表空间吗? ● 您有合适的闪回技术(它可以作为加速恢复和高可用性的实现方式)吗?任何查询都比访问宕机 系统的查询要快! ● 经常收集统计数据吗?经常在通常是静态的表上收集它们吗? 355 ● 您使用 AWR 和 ADDM(更多信息请参阅第 1、5 和 14 章)来诊断和修复潜在问题吗? ● 使用企业管理器栅格控制能够让您更快地配置新节点和诊断 RAC 问题吗? ● 您有足够的测试和开发系统来确保在将代码引入产品之前进行正确的测试吗? ● 您考虑过在更小型系统上使用 SGA_TARGET 和 Oracle 的自动调整性能吗?如果考虑过,那您设置 过需要最小设置的参数(例如,DB_CACHE_SIZE、SHARED_ POOL_SIZE 和 JAVA_POOL_SIZE)吗? ● 您有加密备份吗?或者您的系统能够应对会导致产品性能严重延迟的宕机吗?重申一下,任何查 询都比访问宕机系统的查询要快! 15.1015.1015.1015.10 技巧回顾技巧回顾技巧回顾技巧回顾 ● 测量您的教育性能指数(EPI)可以帮助您确定从哪些方面提高教育程度可带来好处。 ● 测量您的系统性能指数(SPI)可以帮助您确定从哪些方面提高系统综合性能可获得好处。 ● 测量您的内存性能指数(MPI)可以帮助您确定从哪些方面改进内存的分配和使用可获得好处。 ● 测量您的磁盘性能指数(DPI)可以帮助您确定从哪些方面提高磁盘操作可带来好处。 ● 测量您的总性能指数(TPI)可以帮助您确定瓶颈;它是系统综合性能的一个简单的晴雨表。 ● 您的系统应当由一个外部团体来进行年度检查,或者,至少由您公司内部的某个人来检查。 ● 一个系统评测始终应当包含需要立即解决的项目。这将确保改进措施所需的时间和费用可以及时 得到保证。 ● 您应当只有在出于提高 DBA 技能的目的下,才去检查 DBA 的能力。评测一个人是非常敏感的问题, 必须由以提高 DBA 技能为首要目的的人来完成这件工作。 ● 如果您不能有效地监控自己的系统,那么就联系具备此能力的 DBA。维护一个数据库的成本通常 远远低于当问题出现时宕机的成本。 ● 在您的条目中包含新的 10g 条目(遍布整本书),例如 SYSAUX、可传输的表空间(Transportable Tablespaces)、闪回、AWR、ADDM、栅格控制和加密备份,以方便检查。 15.1115.1115.1115.11 参考文档参考文档参考文档参考文档 Oracle10g SQL Language Reference Manual Versions (Oracle Corporation) Memory Performance Index, Disk Performance Index, Education Performance Index, Total Performance Index, MPI, DPI, EPI, SPI and TPI (Copyright TUSC 1998–2007) Maurice Aelion, Dr. Oleg Zhooravlev, and Arie Yuster, Tuning Secrets from the Dark Room of the DBA
还剩354页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享pdf获得金币 ] 29 人已下载

下载pdf

pdf贡献者

openkk

贡献于2010-09-28

下载需要 20 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf