
好的,我们将对 Oracle 数据字典(Data Dictionary)进行一场深入的、系统性的剖析。数据字典是 Oracle 数据库的“大脑”和“元数据中心”,理解其内部原理是进行高级管理和性能优化的基石。
第一部分:官方技术详解
一、核心概念与作用
1. 什么是数据字典?
Oracle 数据字典是一组只读的表和视图的集合,它描述了数据库自身的结构和状态。它包含了所有数据库对象的元数据(Metadata),例如:
- 用户和权限信息(用户、角色、系统权限、对象权限)
- 对象定义信息(表、视图、索引、序列、同义词、存储过程等的定义)
- 存储结构信息(表空间、数据文件、段、区、块的控制信息)
- 约束信息(主键、外键、检查约束等)
- 运行状态信息(性能视图
V$系列,虽不属于严格的数据字典,但密切相关)
2. 核心作用
- 系统可靠性:确保数据库对象引用的有效性和一致性。例如,当你查询
SELECT * FROM emp时,数据库需要从数据字典中查找emp表是否存在、你有无权限、它由哪些列组成。 - 优化器决策基础:SQL 优化器依赖数据字典中的统计信息(如
NUM_ROWS,BLOCKS,LAST_ANALYZED)来生成高效的执行计划。 - 数据库管理:DBA 通过查询数据字典来监控、管理和维护数据库。
- 依赖关系维护:记录对象间的依赖关系。例如,一个视图依赖于某个表,当表结构改变时,Oracle 可以标记该视图为
INVALID。
二、内部结构:基表、视图与内部机制
数据字典在物理上和逻辑上分为多个层次。
1. 核心内部结构
-
基表 (Base Tables):
- 定义:数据字典最底层的、存储原始元数据的表。例如
OBJ$,TAB$,COL$,USER$,IND$等。 - 存储:这些基表存储在
SYSTEM表空间中,由SYS用户所有。 - 特性:基表的结构非常晦涩(列名如
NAME,TYPE#,OBJ#),直接查询极其困难且强烈不推荐。它们的更改由 SQLDDL语句自动触发和维护。 - 初始化:在数据库创建时(
CREATE DATABASE),由sql.bsq脚本创建这些基表并插入初始数据。
- 定义:数据字典最底层的、存储原始元数据的表。例如
-
数据字典视图 (Data Dictionary Views):
- 定义:为了便于用户和 DBA 查询,Oracle 在基表之上创建了多组易于理解的视图。这些视图对基表的信息进行了转换、连接和格式化。
- 前缀与作用域:
DBA_*:显示数据库中所有对象的信息。例如DBA_TABLES。查询需要SELECT ANY DICTIONARY权限。ALL_*:显示当前用户有权限访问的所有对象的信息。例如ALL_TABLES。USER_*:显示当前用户所拥有的对象的信息。例如USER_TABLES。这是最常用的视图集。
- 静态视图 (Static Views):上述
DBA_/ALL_/USER_视图主要基于基表,其内容相对持久,直到下一次 DDL 发生。
-
动态性能视图 (Dynamic Performance Views) -
V$视图:- 定义:虽不属于严格意义上的数据字典,但它们是元数据生态的核心部分。它们提供实时的、内存中的数据库运行状态信息,如锁、会话、内存使用、系统统计等。
- 存储:其数据来源于数据库实例的控制文件和SGA 内存结构。
- 特性:这些视图是虚拟的,在实例启动时动态创建,在实例关闭时消失。其内容持续变化。
- 底层:
V$视图通常基于GV$(Global V$) 视图,后者在 RAC 环境中提供所有实例的信息。
2. 内部管理与访问机制
-
递归 SQL (Recursive SQL):
- 当你执行一条简单的
SELECT * FROM emp时,数据库在为你服务的过程中,自己会在后台发出大量的 SQL 语句来访问数据字典。这些 SQL 就是递归 SQL。 - 过程举例:
- 解析
SELECT * FROM emp。 - 在共享池中查找是否有现成的执行计划?没有,则进行硬解析。
- 硬解析过程中:需要发出递归SQL:
- 检查
emp是否存在? (SELECT ... FROM OBJ$ WHERE NAME='EMP' AND OWNER#=...) - 当前用户是否有
SELECT权限? (SELECT ... FROM SYSAUTH$ ...) emp有哪些列? (SELECT ... FROM COL$ WHERE OBJ#=...)- 获取
emp的统计信息 (SELECT ... FROM TAB_STATS$ ...)
- 检查
- 基于收集到的元数据,优化器生成执行计划。
- 执行查询,返回结果。
- 解析
- 大量的硬解析会导致大量的递归 SQL,增加系统负载。
- 当你执行一条简单的
-
引导阶段 (Bootstrap):
- 这是一个“先有鸡还是先有蛋”的问题:数据库如何读取
SYSTEM表空间中的数据字典基表,如果连描述这些基表结构的元数据都还没有被读取? - 解决方案:在实例启动挂载数据库后,会执行一个引导过程。数据库首先核心的、最小化的元数据信息(如
OBJ$,COL$,TAB$的块地址)是“硬编码”在内存中的。通过这部分信息,它可以找到并加载其他的数据字典基表。这个过程是完全内部的、自动的。
- 这是一个“先有鸡还是先有蛋”的问题:数据库如何读取
三、数据字典的管理
- 保护:
SYSTEM表空间是数据字典的物理家园,必须确保其高度可用和完整。绝不能将非字典的用户对象放入SYSTEM表空间。 - 统计信息:数据字典基表自身也有统计信息。Oracle 通过自动任务
gather_stats_job或在手动调用DBMS_STATS.GATHER_DICTIONARY_STATS或GATHER_SCHEMA_STATS('SYS')时收集它们。保持字典统计信息最新对于优化递归 SQL 的性能至关重要。 - 损坏与恢复:数据字典的损坏是灾难性的。恢复通常需要从备份中还原
SYSTEM表空间,并应用归档日志到最新状态。
第二部分:场景、争用、排查与解决
四、性能争用与排查
数据字典的争用通常发生在内存访问层,因为物理 I/O 访问字典基表是极力要避免的。
1. 场景:行缓存争用 (Row Cache / Dictionary Cache)
- 原理:
- 数据字典信息在 SGA 的共享池 (Shared Pool) 中的一个特定区域进行缓存,称为字典缓存或行缓存 (Row Cache)。
- 它缓存的是从数据字典基表中取出的数据行(不同于缓冲区缓存缓存的是数据块)。
- 在高并发、大量硬解析的场景下,无数会话需要访问相同的字典信息(如检查权限、解析对象名),会导致对字典缓存中“行”的访问争用。
- 等待事件:
row cache lock:当会话正在修改字典缓存中的某一行(如 during DDL) 时,其他会话想要读取这一行就会被阻塞,等待此事件。latch: row cache objects:闩锁用于保护字典缓存内部结构的短暂、独占访问。当大量进程试图同时搜索或读取字典缓存信息时,会产生对这个闩锁的争用。这是更常见的字典缓存争用事件。
- 排查:
- 查询
V$EVENT_NAME确认等待事件。 - 使用
V$SESSION/V$ACTIVE_SESSION_HISTORY查看当前或历史会话是否在等待这些事件。 - 查询
V$ROWCACHE视图,观察不同子缓存(如dc_tablespaces,dc_objects,dc_users) 的GETS,GETMISSES,MODIFICATIONS。高GETMISSES和MODIFICATIONS可能预示争用。
SELECT parameter, gets, getmisses, modifications, count(*) FROM v$rowcache WHERE gets > 0 GROUP BY parameter, gets, getmisses, modifications ORDER BY gets DESC; - 查询
- 解决:
- 减少硬解析:这是根本方法。使用绑定变量(Bind Variables)替代字面值,使SQL可以软解析/软软解析,极大减少对字典缓存的访问。
- 保持共享池大小合理:确保
SHARED_POOL_SIZE足够大,以减少字典缓存条目被换出(age out)的风险。 - 避免频繁的DDL:在生产高峰期间,避免执行
TRUNCATE,DROP,GRANT等DDL操作,因为它们会无效化(invalidate)相关的字典缓存条目并获取row cache lock。
2. 场景:库缓存争用 (Library Cache Lock)
- 原理:虽然库缓存主要缓存执行计划、SQL文本等,但它与数据字典紧密相关。在解析SQL时,需要获取对象上的库缓存锁(Library Cache Lock) 和句柄(Handle) 来防止对象被并发修改。
- 等待事件:
library cache lock:通常由并发 DDL 和 DML 引起。例如,一个会话正在查询一个大表,另一个会话试图DROP该表,查询会话就会等待此事件。library cache: mutex X:更轻量级的保护机制,但在高并发解析下也会出现争用。
- 排查与解决:与行缓存争用类似,根源在于硬解析和DDL。解决方法同样是使用绑定变量和避免高峰DDL。
五、常用查询与管理 SQL
-
查看数据字典缓存命中率:
SELECT (1 - (SUM(getmisses) / SUM(gets))) * 100 "Row Cache Hit Ratio" FROM v$rowcache; -- 理想值应高于95% -
查找具体的行缓存争用:
SELECT parameter, gets, getmisses, ROUND((getmisses / DECODE(gets,0,1,gets)) * 100, 2) miss_ratio, modifications FROM v$rowcache WHERE gets > 0 ORDER BY miss_ratio DESC; -
收集数据字典统计信息:
EXEC DBMS_STATS.GATHER_DICTIONARY_STATS; -- 或 EXEC DBMS_STATS.GATHER_SCHEMA_STATS('SYS'); -
查询库缓存和字典缓存的相关信息:
SELECT * FROM v$sgastat WHERE pool = 'shared pool' AND name LIKE '%row%'; SELECT namespace, pins, pinhits, reloads, invalidations FROM v$librarycache; -
查找正在等待字典相关事件的会话:
SELECT sid, serial#, event, state, seconds_in_wait, blockingsession FROM v$session WHERE event LIKE '%row cache%' OR event LIKE '%library cache%';
第三部分:通俗易懂的解释
想象一下 Oracle 数据库是一座巨大的图书馆。
-
数据字典基表:就是图书馆总管理员的终极 master 清单。这张清单用只有管理员才懂的简写和代码写成,记录了:“书A(obj#=101)放在3楼B区2架,作者是X,共500页,属于科幻区…” 这张清单本身也存放在图书馆里的一个特殊保险柜里(
SYSTEM表空间)。 -
数据字典视图 (
DBA_,ALL_,USER_):就是图书馆为不同读者准备的查询台。USER_查询台:你走过去,它只显示你本人借阅和捐赠的所有书籍的清单,用的是你能看懂的正常语言。ALL_查询台:显示整个图书馆所有你被允许阅读的书籍的清单。DBA_查询台:只有图书管理员能用,显示图书馆里的一切,包括那些禁书区和内部档案。
-
动态性能视图 (
V$):这是图书馆大门口的实时监控显示屏。它不关心书本身,它显示的是:“当前入馆人数:153人,最拥挤区域:科幻区,最近一小时借阅量:300本…” 这些信息是实时变化的。 -
递归 SQL:当你想找一本《三体》时,你不会直接去翻 master 清单(基表)。你会去问查询台(数据字典视图):“请问《三体》在哪?” 而查询台的工作人员,为了回答你的问题,他需要背后去查阅好几次那个 master 清单。他为你做的这几步查询,就是“递归 SQL”。如果你每次问“《三体》在哪?”都要他现去查(硬解析),那他就很忙。但如果他记得答案(缓存),他就能马上告诉你(软解析)。
-
行缓存争用 (
latch: row cache objects):在借书高峰期,无数人同时挤在查询台前问问题:“《三体》在哪?”“《哈利波特》呢?”。查询台工作人员只有一个,他一次只能回答一个人的问题。其他人必须排队等待,这就产生了争用。解决方案是:1) 让大多数人通过记忆(缓存)自己知道答案,别老来问(使用绑定变量减少解析);2) 多设几个查询台(调整共享池和内部参数,但这治标不治本)。 -
库缓存锁 (
library cache lock):你正在读《三体》的某一页,这时图书管理员推着车过来要把这本书下架回收 (DROP TABLE)。为了保证你的阅读不被干扰,系统会给你这把书加一个“有人在读”的锁。管理员必须等你读完释放了这把锁,才能进行他的操作。这个协调过程就可能引起等待。
总结:
Oracle 数据字典是数据库自我认知和自我管理的核心系统。它的高效运作依赖于共享池中字典缓存和库缓存的命中率。DBA 的核心任务就是通过使用绑定变量和审慎进行DDL操作来最大限度地减少对数据字典的递归访问和争用,确保这个“图书馆查询系统”畅通无阻,从而让整个“图书馆”(数据库)高效运转。
欢迎关注我的公众号《IT小Chen》
825

被折叠的 条评论
为什么被折叠?



