
1. 为什么要先啃体系结构而不是急着写SQL不少朋友接触Oracle 19c的第一反应是先装个库然后建表、写SQL、跑数据遇到性能问题再说。这个思路不能说错但走不远。真正干过几年数据库的人都有体会绝大多数让你焦头烂额的问题——内存命中率上不去、日志切换频繁、ORA-01555、归档空间爆掉、监听时不时掉线——源头都在体系结构而不在你写的SQL本身。Oracle 19c是Oracle 12c之后的长期支持版本也是目前企业里部署比例非常高的一个版本。它的体系结构沿用并强化了12c引入的多租户架构CDB/PDB同时把内存管理、自动化诊断、在线维护这类能力做得更成熟。理解这套体系结构不是让你背一堆名词而是让你在读AWR报告时知道“Shared Pool”和“Log Buffer”哪个才是瓶颈在执行一条UPDATE时知道它是怎么从客户端走到数据块、再从重做日志回到磁盘的以及在出故障时知道应该去看alert日志、trace文件还是监听日志。这篇是系列第2篇我会把Oracle 19c体系结构里最核心的几大块拆开讲全局视角、内存结构、存储结构、进程结构、多租户架构最后附上我整理的一些排查思路和常用查询脚本。阅读时建议打开一个测试库边看边执行光看不练隔两周就忘干净了。提示本系列假设你至少能装好一个Oracle 19c数据库并可以登录。如果还没装好先把安装这一步过了再回来看。2. 全局观一条SQL从客户端到磁盘的全过程理解体系结构最有效的方式不是背图而是跟着一条SQL走一遍。就好比你想了解一家餐厅怎么运转与其背菜单不如观察一个客人从进门到吃完出门后厨、传菜、收银各环节分别发生了什么。一个SELECT语句的典型旅程是这样的客户端比如SQL*Plus、Navicat、Java程序通过网络连接到数据库监听进程Listener监听进程确认连接后会分配或者复用一个服务器进程Server Process来为这个会话服务。这个服务器进程接收你发来的SQL文本接下来它要做几件事检查语法、解析语义、生成执行计划然后拿着执行计划去内存里要数据。要数据这一下就很有意思了。服务器进程先查数据库缓冲缓存Database Buffer Cache里有没有目标数据块如果有直接返回这叫逻辑读如果没有就得去磁盘把数据文件里对应的数据块读进缓存这叫物理读。物理读的成本比逻辑读高一到两个数量级这也是为什么DBA天天盯着buffer cache命中率不放的原因。如果是INSERT或UPDATE这类写操作路径略有不同。服务器进程改数据块之前会先把变化记录到重做日志缓冲Redo Log Buffer由日志写进程LGWR统一把重做记录刷到重做日志文件。这里有个关键设计数据块可以先留在内存里不立即写磁盘但重做日志必须尽快持久化。因为万一数据库崩溃Oracle可以从重做日志恢复出你提交过的操作。数据块晚点写没事重做日志要是丢了那才是真正的灾难。这条完整链路涉及的对象包括监听进程、服务器进程、SGA里的共享池和缓冲缓存、PGA里的排序区/游标区、后台进程LGWR和DBWn以及磁盘上的数据文件、控制文件、重做日志文件。任何一个环节堵住你都能遇到对应的症状共享池堵了解析慢缓冲缓存不足物理读暴涨日志文件太小频繁触发checkpoint归档空间满直接卡住所有事务。我的建议是第一次学体系结构不要试图一天消化全部内容先记住这条主链路然后把每个环节展开去理解。等这条链路在心里像一条溪流一样清晰了回头再看隐含参数、事件、等待模型都是水到渠成的事。3. 内存结构详解SGA和PGA到底怎么配合3.1 SGA数据库在内存里的小宇宙系统全局区System Global AreaSGA是Oracle在内存里分配的共享内存区域所有会话都能访问。19c里默认采用自动内存管理AMM或ASMM之后你不需要手工给每一个池指定大小但理解每个池是干什么的仍然非常必要因为出问题的时候自动管理不一定能救你。SGA的核心组件包括共享池Shared Pool缓存SQL文本、解析过的执行计划、数据字典信息。如果你看到大量“library cache miss”或“hard parse”比例升高基本就是这里不够用或者在大量使用非绑定变量SQL。数据库缓冲缓存Database Buffer Cache缓存从数据文件读出来的数据块。大小直接影响物理读频率。重做日志缓冲Redo Log Buffer暂存重做记录LGWR定期刷盘。正常情况下几十MB就够但如果日志缓冲太小会出现“log buffer space”等待事件。Java池和大池Java池用于JVM相关组件大池用于共享服务器模式、并行查询、备份恢复操作。在RAC环境中大池还承担了集群内通信的额外负担。3.2 PGA每个会话自己的小房间程序全局区Program Global AreaPGA是服务器进程私有的内存区域不共享。排序、哈希连接、位图连接等操作都在PGA里完成。你执行一个ORDER BY如果数据量在PGA的sort area里放得下就是内存排序放不下就得使用临时表空间做磁盘排序这就是为什么临时表空间爆满是排序大户的经典信号。19c里PGA自动管理非常好用你只需要设置一个PGA_AGGREGATE_TARGET参数Oracle会动态给各会话分配内存。但要注意比设置更重要的是监控查询v$pgastat里的over allocation count如果持续大于0且增长明显说明当前PGA总量已经不够。3.3 19c内存管理的变化与启示从11g开始到19c内存管理的趋势就是“让Oracle自己管更多”。19c默认就是自动共享内存管理ASMM自动PGA管理DBA需要手工调内存的场景比10g时代少了很多。但是自动管理并不等于放任不管。SGA的auto-tuned就是基于你在参数里给定的上限数据库在内部动态调整各组件比例。这个机制的基础是内存顾问它需要足够的历史运行统计来做出决策。所以刚上线的新库前一两周的AWR快照和统计信息收集非常重要没有统计数据自动管理就是“盲调”。我在实际项目中见过一个案例系统运行半年后共享池比例被自动调整得非常小原因是应用侧大量使用动态SQL导致硬解析持续走高AWR报告里shared pool的命中率已经惨不忍睹。这种情况仅仅靠调大SGA总量是不能根治的你还需要同时治理应用端的SQL写法配合游标共享参数cursor_sharing做过渡。4. 存储结构数据文件、控制文件和重做日志Oracle的存储结构分为逻辑层面和物理层面两个视角。逻辑层面是表空间、段、区、块逐级拆分物理层面就是实实在在的文件。日常运维你接触最多的就是三个东西控制文件、数据文件和重做日志文件。这三样的重要性怎么强调都不过分。数据文件承载实际的数据。一个表空间对应一个或多个数据文件数据文件内部按“块Block”组织块是数据库最小的I/O单位。Oracle默认块大小是8KB建库时可以指定但一旦建好不能修改除非重建库。块大小影响很深比如一个一行数据就有10KB的大宽表放在8KB块里就会出现行链接Row Chaining导致一次读取需要访问多个块性能明显下降。控制文件是数据库的“中枢神经”体积不大但极端重要。它记录了数据库的名字、数据文件和重做日志文件的路径、SCN系统改变号、检查点信息等。如果库里面所有控制文件都丢了又没有二进制备份或者没有rman备份中的控制文件恢复难度会直线上升。Oracle强制要求至少有两个镜像一个标准做法是放三个控制文件而且放在不同的物理磁盘上。重做日志文件记录所有数据的变更。每组日志至少两个成员数据库以循环方式写日志。当写到一组末尾就发生一次日志切换触发检查点并让DBWn把脏缓冲块写入数据文件。日志组太小、太少切换频率就会过高带来连续不断的检查点严重时撑爆归档空间。19c一个重要改进是UNDO表空间的自动扩展能力。UNDO用于实现读一致性和回滚。一个运行中的事务如果生成了大量UNDO而且UNDO表空间是固定大小就可能出现快照太旧Snapshot Too Old错误俗称ORA-01555。这个错误很多老DBA都处理过19c里如果设置了自动扩展这类风险会小很多但代价是UNDO表空间可能悄无声息地蚕食磁盘。另一件值得说的是临时表空间。排序、哈希连接、临时表的数据都会进临时段。临时表空间不需要记录重做日志所以性能比普通表空间高但它在内存不足时的表现直接影响大型查询的成败。我见过一个16GB的临时表空间跑一个报表任务被撑爆的案例最终定位是两条大表关联缺了索引Hash Join的build side估算错误导致临时段暴涨。从学习角度你应该能够随手回答这几个问题你的数据库有几个控制文件重做日志组几组几成员UNDO表空间用了多少临时表空间是否设置了自动扩展这些问题直接对应建库和日常巡检脚本答不上来的话说明你对存储结构的理解还停留在背概念阶段。5. 进程结构后台那些兢兢业业的守护者们Oracle基于进程架构Windows平台是线程但逻辑模型相同。整个模型分三类用户进程、服务器进程、后台进程。用户进程就是你开的SQL*Plus或者应用程序客户端那一端服务器进程是数据库为你的会话服务的那个进程后台进程是数据库自身的守护者不管有没有人登录它们都在跑。5.1 必须认识的几个后台进程SMON系统监控进程负责崩溃恢复、临时段清理、合并相邻空闲区。数据库异常宕机后重启做前滚和回滚恢复的就有SMON参与的份。PMON进程监控进程。负责清理异常终止的会话、回滚该会话未提交的事务、向监听进程注册实例信息。如果你发现某段时间大量“PMON failed to acquire a resource”错误通常意味着资源泄漏或者并发异常。DBWn数据库写进程把脏缓冲块写入数据文件。它从来不是统一的单进程19c里可以有多个DBW0、DBW1等。DBWn的写策略比较保守主要依赖检查点和内存不足时才会触发写盘这也是“数据库不会实时把数据写进数据文件”这句话的原因。LGWR日志写进程把重做日志缓冲写到重做日志文件。LGWR在下列情况下会触发写日志提交操作、日志缓冲达到1/3满、恰好超过1MB、每3秒定时或日志切换前。LGWR写日志成功后事务的提交才算成功这就是“commit后不丢数据”的底层保障。CKPT检查点进程。它负责更新控制文件和数据文件头部的检查点信息。检查点频率影响实例恢复时间RMAN增量备份也会依赖检查点信息。ARCn归档进程。数据库在归档日志模式下日志切换后由ARCn把写满的重做日志文件复制到归档目录。如果归档进程跟不上数据库会等待这就是经典的“ARCH waiting for log”问题。5.2 19c中不太显眼但很重要的变化19c里自动维护任务Automatic Maintenance Tasks已经非常成熟包括自动收集统计信息、自动运行AWR报告、自动清理工作负载仓库等。这些任务由被称为“后台任务进程”的一部分内部进程统一调度。不少DBA忽略了它们但其实很多“半夜突然CPU飙升”的案例追根溯源都是自动统计信息收集在扫描大表。5.3 一个真实的“进程问题”案例曾经有个生产库每隔一周就出现一次连接数满载应用方抱怨数据库“没反应”。排查后发现问题不在数据库本身而在于应用服务器的连接池没有正确回收连接导致大量会话处于“空闲”或“假死”状态。PMON虽然在持续清理但清理速度跟不上新连接创建的速度。这种情况单纯调大processes参数只是延后问题根本解法是修应用连接池的超时参数和校验策略。这个案例告诉我们进程结构不只是数据库内的事它和外部应用、监听配置、网络环境是一个整体。学体系结构的时候别把眼光锁死在数据库这半边。6. 多租户架构19c下绕不开的CDB与PDB从12c开始Oracle引入了多租户架构。到了19c这已经是主流部署形态。多租户架构的核心是一个容器数据库CDB里装多个可插拔数据库PDBPDB之间既共享CDB的底层资源又保持逻辑上的独立性。你可以把CDB理解成一栋写字楼PDB就是楼里一个个独立办公室水电网是共享的但每个办公室的门禁和装修是自己的。6.1 连接方式的改变在非多租户Non-CDB时代你连一个ORCL数据库服务名基本等于数据库名。进入19c多租户以后需要分清楚连接对象连接CDB roote看的是整个容器的视图连接某个PDB比如PDB1你的业务对象都在里面。这带来的直接变化是很多运维操作需要指定容器。比如你执行ALTER SYSTEM语句在19c里可以加SCOPE和CONTAINER参数指定在当前容器生效还是全部容器生效。建索引、收集统计信息、管理表空间都要在对应的PDB里去做而不是像以前那样“全局一把梭”。6.2 学习19c体系结构要先过“容器”这一关不少从11g、12c早期转过来的DBA初期最不适应的就是“我明明连上了数据库怎么看不到以前的表”原因很简单你连到了CDB root业务数据在PDB里跟root本来就不在同一层。想要在PDB之间切换需要用ALTER SESSION SET CONTAINER或者直接以对应的服务名重新连接。6.3 PDB的资源管理与隔离多租户在带来便利的同时也引入了一个问题资源隔离。一个PDB爆发的SQL如果影响整个CDB其他PDB的业务也会跟着遭殃。19c提供了资源管理器Resource Manager来对PDB做CPU和I/O限制具体是配置PDB Performance Profile可以给每个PDB设定CPU使用百分比上限还可以限制并行度。企业里使用多租户最常见的几个场景是把多个旧库整合到一个CDB里省资源、创建PDB作为开发和测试环境、使用PDB Refresh功能实现跨环境的数据同步。这些操作都建立在多租户架构之上如果你不了解容器机制连建PDB的SQL都不知道该在哪里执行答案在CDB root下执行CREATE PLUGGABLE DATABASE。6.4 我的一个操作建议学习阶段建议你从19c开始就使用多租户方式部署哪怕只有一个库也建立一个SEED PDB并创建自己的业务PDB。这样做的好处是你从一开始就习惯了“容器上下文”这个概念将来遇到真实的集成环境不会懵。相反如果一直用11g模式的直连方式等哪天到了客户环境都是19c多租户你会在最基础的连接这一关卡很久。7. 从体系结构到实战必会的一组查询体系结构学明白了下一步就是会看状态。Oracle提供了一组动态性能视图它们把内存、进程、存储的状态直接暴露出来。我把日常工作里最常用的一小组查询整理出来每一段都附带一点解释你可以在测试库上直接执行。7.1 查看SGA和PGA总体大小-- 查看SGA各组件内存分配 SELECT component, current_size/1024/1024 size_mb FROM v$sga_dynamic_components ORDER BY component; -- 查看PGA总体使用情况 SELECT name, value FROM v$pgastat WHERE name IN (aggregate PGA target parameter, aggregate PGA auto target, global memory bound);这组查询帮你看清楚内存分配的现状。如果current_size和参数设置的目标差距很大说明数据库仍在动态调整过程中如果长时间无法达到目标就要怀疑是否有外部压力比如物理内存不够。7.2 查看数据文件、控制文件和日志文件-- 数据文件位置与大小 SELECT file_name, tablespace_name, bytes/1024/1024 size_mb FROM dba_data_files; -- 控制文件位置 SELECT name FROM v$controlfile; -- 日志组成员和状态 SELECT group#, type, member, status FROM v$logfile; SELECT * FROM v$log WHERE statusCURRENT;每次巡检我都习惯顺手跑一遍确认文件路径没有被误改、磁盘空间没有被日志或归档占满。特别是v$logfile如果某个目录的IO性能明显比别的目录差长期下去整个日志切换都会受影响。7.3 查看后台进程与当前活动会话-- 查看核心后台进程状态 SELECT paddr, username, program, background FROM v$process WHERE background IS NOT NULL; -- 查看当前活动会话正在做什么 SELECT sid, serial#, event, wait_class, sql_id, seconds_in_wait FROM v$session WHERE statusACTIVE ORDER BY seconds_in_wait DESC;活动会话的event是判断数据库健康状态的关键输入。如果大量会话都卡在“log file sync”说明日志写盘路径有IO瓶颈或者重做日志文件太小如果卡在“buffer busy waits”说明数据块缓存或并发访问逻辑有问题。7.4 查看DB Time和等待事件分布-- 查看当前等待事件Top 5 SELECT event, wait_class, total_waits, time_waited FROM v$system_event WHERE wait_class ! Idle ORDER BY time_waited DESC FETCH FIRST 5 ROWS ONLY;这条语句用到了19c的FETCH FIRST语法这在11g里要写ROWNUM到19c里简洁了很多。等待事件分布就是数据库的“体检报告”多看几次之后你看到一份AWR报告就大致能判断系统瓶颈在哪了。这组SQL不是背答案而是要理解每一条输出背后对应的体系结构组件。比如v$event里的“db file sequential read”对应的是数据文件单块读本质上就是buffer cache miss后去磁盘读单个块的成本。8. 常见问题与排查技巧实录这一节分享几个我真正处理过的问题。每个问题的根因都落在体系结构的某个模块上希望帮你建立“症状→定位→解法”的直觉。8.1 问题一日志切换频繁导致IO飙升现象AWR报告显示平均每分钟日志切换20次以上伴随较高的“log file parallel write”等待。应用侧反馈系统整体变慢。排查思路先确认日志文件的组数和大小。如果一组日志只有几百MB而重做日志产生速度又快切换必然频繁。再看磁盘类型如果没有使用SSD大量同步日志写会让IO队列爆掉。解决方法扩大日志文件尺寸增加日志组成员。注意修改重做日志文件是online操作但需要组内至少两个成员都可用。具体操作是先加一个更大的日志组删除旧组再把新组扩展到原数量。教训日志组大小的设定不能拍脑袋。建议参考业务高峰期的redo生成速率——大约是每秒多少MB目标就是让每次日志切换间隔不少于10到20分钟。低于这个水平就是在拿切换频率换取IO压力。8.2 问题二ORA-01555快照太旧现象一个长时间运行的查询在快照点前需要读UNDO里的旧版本数据但相应的UNDO信息已被新事务覆盖于是报错ORA-01555。排查思路先看UNDO表空间大小和AWR里的UNDO retention再找到具体是哪个长时间查询。很多时候不是UNDO真的不够而是某个大查询跑了太久或者有长时间未提交的事务锁住了UNDO空间。解决方法如果确认是查询时间过长优化SQL或扩大UNDO retention设置如果是未提交事务那就得找到那个会话和业务方协商提交或回滚。一个容易踩的坑很多人一看到ORA-01555就去改UNDO表空间大小但根本原因是应用生成了“查询时刻不一致”的数据视图。只调大小不优化SQL问题会周期性复发。8.3 问题三归档空间持续上涨现象归档日志目录磁盘使用率持续高位DBA几乎每周都要清理一次。某天突然数据库挂起日志显示“ARCH waiting for redo log”。排查思路归档涨得快要先看是否有批量任务在大量生成重做记录。如果有考虑在对应会话里用NOLOGGING或UNRECOVERABLE方式减少日志生成。再看归档目录是否有空间、归档进程是否正常、存储上的IO是否存在瓶颈。解决方法一次性方案是清理归档、扩充磁盘中长期方案是调整日志切换频率、检查备份策略是否落后。如果RMAN备份不及时归档日志只能无限积累。注意直接删归档文件很危险一旦控制文件里的归档记录与实际文件不一致后续RMAN恢复会报错甚至无法恢复。规范做法是使用RMAN的DELETE命令来清理过期归档而不是手动rm文件。8.4 问题四连接数满数据库毫无响应现象客户端报“ORA-12520: TNS:listener could not find available handler for requested type of server”或“ORA-00020: maximum number of processes (xxx) exceeded”。排查思路用ps查看当前进程数量对比processes参数和实际连接数。然后查v$session看哪些会话状态异常、哪个应用占用了大量连接。很多时候是连接池泄漏数据库本身没有严重负载。解决方法临时加大processes参数并重启实例只能救急。长期策略是修改应用连接池配置设置空闲连接回收、连接最大寿命并对特定主机或应用做资源限制。复盘体会这个案例最能说明“全局视角”的重要。如果只盯着数据库内部会陷入内存、SQL、IO的死胡同实际上问题出在外部应用的资源管理之上只是以数据库的报错形式暴露了出来。9. 学习19c体系结构的几个有效方法这一节没有太多新概念是我自己学习过程中觉得最有效、也最愿意推荐给别人的几条经验。第一把体系结构和AWR报告对照着学。AWR报告里的等待事件、实例活动、表空间统计、内存概要几乎就是体系结构各组件运行状态的“体检摘要”。拿到一份真实的生产AWR尝试解释其中TOP 5事件为什么发生是理解体系结构最快的方式。第二养成看视图而不是只看GUI的习惯。19c有很多图形工具比如Oracle Enterprise Manager表格同时提供了v$视图和DBA_*视图。直接从命令行查询逼自己面对字段名、连接关系和原始数值这个过程比点鼠标学到的扎实得多。第三用破坏性实验加深印象。在测试库里故意把日志文件设小观察切换频率变化把SGA_TARGET压低看看shared pool出现哪些等待故意不提交事务看UNDO表空间占用上升。每一个“坑”都是自己踩过的记忆深刻度远超看十页文档。第四重视RMAN备份恢复和体系结构的关联。备份恢复是理解存储结构的最佳途径。当你需要做一次完整恢复就知道控制文件里存了什么、重做日志如何支撑前滚、UNDO如何支撑回滚。RMAN里的完整恢复、不完全恢复、闪回数据库每一个都直接对应体系结构里的存储和一致性格局。10. 写在最后的几点经验我现在看到很多初学者一上来就看专家的调优案例盯着18c、19c的新特性不放结果基础体系结构一片模糊看什么都像看天书。我更推荐的做法是先花两到三周把SGA、PGA、数据文件、重做日志、进程这几条主线理清能画出自己的结构图然后带着图去看AWR报告、看等待事件、做一次真实的故障排查遇到不懂的词汇回来翻文档补齐而不是一开始就想读懂所有文档。在这个过程中最重要的不是记住参数名和视图名而是理解“数据是如何流动的、瓶颈可能在哪里、失败之后如何恢复”。Oracle这套体系结构从诞生到现在核心骨架没有发生颠覆性变化每一次新版本都只是让它在性能、自动化、稳定性上更进一步。把骨架打牢之后无论你遇到21c、23c还是未来更新的版本迁移成本都很低。最后分享一个小技巧学习时准备一个本子把“症状、根因、解决手段”三条线对应起来记录。比如日志切换频繁对应重做日志配置和IO压力这个映射关系越丰富你处理生产问题的时候反应就越快。体系结构不是一门背书的学问它本质上是一张“故障地图”你心里这张地图越细致数据库出问题的时候你就越不慌。