Oracle REDO日志的向量化组装与更改记录是什么?

在这里插入图片描述


第一部分:官方严谨的详细阐述

一、 核心概念:什么是更改向量(Change Vector)和REDO记录(REDO Record)

传统的理解可能认为REDO日志记录的是SQL语句或数据块的前后镜像,这是一种过度简化且不准确的观点。Oracle的REDO机制是物理的基于块的向量化的

  1. 更改向量 (Change Vector)

    • 定义: 描述对单个数据块所做的一次原子性物理更改的最小单位。它是最底层的构成模块。
    • 内容: 一个更改向量包含以下关键信息:
      • 操作码 (Opcode): 指定要执行的操作类型(例如,insert a row, delete a row, update a row piece)。
      • 数据块地址 (DBA - Data Block Address): 指明要修改的块是哪个文件、哪个块。
      • 版本号 (SCN/Sequence): 保证更改的顺序性和可重演性。
      • 更改数据: 操作所需的数据。例如,对于插入,这里就是新行的数据;对于更新,这里可能包含列号和新值(而不是整行数据)。关键点:它记录的是“增量”或“差异”,而非完整镜像。
  2. REDO记录 (REDO Record)

    • 定义: 一个原子性数据库操作(如一条DML语句)所产生的所有更改向量的集合。一个REDO记录代表一个完整的、不可分割的数据库变更单元。
    • 原子性保证: 在实例恢复期间,系统要么应用整个REDO记录中的所有更改向量,要么一个都不应用。这保证了即使是一个会影响多个块的复杂操作,也能被完整地恢复或跳过。
    • 结构: 一个REDO记录由一个记录头和一系列更改向量组成。记录头包含了诸如SCN、事务ID(XID)、时间戳等控制信息。
二、 内部原理与组装过程

当执行一条DML语句(例如 UPDATE employees SET salary = 10000 WHERE employee_id = 123;)时,其内部过程如下:

  1. 服务器进程工作

    • 服务器进程在内存中定位(或从磁盘读取)包含目标行(employee_id=123)的数据块
    • 为了支持回滚和读一致性,服务器进程首先需要生成Undo。它会在Undo表空间中分配空间,准备写入“前映像”。
    • 这个过程触发了两个主要的物理更改:
      a. 更改Undo块: 将旧薪水(例如8000)写入Undo块。
      b. 更改数据块: 将数据块中的薪水值从8000更新为10000。
  2. 向量化组装 (Vectoring)

    • 服务器进程不会直接等待这些更改写入磁盘,而是立即在内存的重做日志缓冲区 (Redo Log Buffer) 中构建REDO信息。
    • 对于上述操作,它会组装至少两个更改向量
      • 更改向量 1 (针对Undo块): {Opcode: ‘kdu undo’, DBA: [Undo Block Address], Data: (旧薪水8000, 行ID, …) }
      • 更改向量 2 (针对数据块): {Opcode: ‘kdu update’, DBA: [Data Block Address], Data: (新薪水10000, 行ID, …) }
    • 这两个更改向量被封装(组装) 在一起,形成一个原子性的REDO记录。这个记录代表了“更新员工薪水”这个原子操作。
  3. 写入与持久化

    • 当用户提交事务(COMMIT)时,LGWR进程会将日志缓冲区中包含该事务所有REDO记录的内存区域批量、连续地写入在线重做日志文件(Redo Log Files)。
    • 关键点: LGWR写入的是物理的字节流,这些字节流就是由一个个REDO记录和其内部的更改向量组成的。这种向量化、结构化的设计使得写入极其高效,因为它是顺序的、最小化的I/O。
三、 实例恢复:更改向量的威力

实例崩溃后,SMON进程进行实例恢复,其前滚(Roll Forward) 阶段完美展示了更改向量的价值:

  1. SMON顺序读取重做日志文件,解析出其中的REDO记录和更改向量。
  2. 对于每一个更改向量,SMON执行以下操作:
    • 读取更改向量中的DBA,定位到需要恢复的数据块(可能是数据块、Undo块、索引块等)。
    • 根据更改向量中的操作码(Opcode)数据,在数据块上重演(Replay) 这个精确的物理操作。
  3. 由于一个REDO记录内的所有更改向量是原子的,SMON会确保一个记录中的所有向量都被应用。例如,它既会重演对Undo块的更改,也会重演对数据块的更改。这使得数据库被恢复到崩溃前的确切状态,包括所有未提交的事务(它们的Undo信息也被恢复了)。
  4. 随后在回滚阶段,利用前滚阶段已恢复的Undo数据,回滚所有未提交的事务。
四、 争用、等待事件、排查与解决

场景: 高并发DML负载,特别是频繁的提交(如OLTP系统)。

1. Log Buffer Space 等待事件
  • 争用原因: 服务器进程生成REDO记录(即组装更改向量)的速度超过了LGWR将日志缓冲区内容写入磁盘的速度。进程需要空间来分配新的REDO记录,但缓冲区已满,因此必须等待LGWR腾出空间。
  • 具体影响: DML操作变慢,系统响应延迟增加。
  • 排查
    • 查看 V$SESSION_WAITV$SYSTEM_EVENT,关注 log buffer space 事件的等待时间和次数。
    SELECT event, total_waits, time_waited, average_wait
    FROM v$system_event
    WHERE event = 'log buffer space';
    
  • 解决
    • 增大日志缓冲区: 调整 LOG_BUFFER 初始化参数。但这是一个静态参数,需要重启数据库。
    • 优化I/O: 确保LGWR写入的redo日志文件位于高速、低延迟的存储上(如SSD),并且与其他活跃数据文件隔离,以减少I/O竞争。
    • 减少提交频率: 在应用层面,如果可行,将多个DML操作批量处理后再提交,而不是每一条语句后都提交。
2. Log File Sync 等待事件
  • 争用原因: 用户会话在执行 COMMIT 时,会触发LGWR执行一次写操作,并等待此次写操作完成。会话在此等待事件上花费的时间,本质上就是LGWR将日志缓冲区中从上次写操作到现在的所有REDO记录(包含本事务的)写入磁盘所需的时间。
  • 具体影响: 提交操作变慢,直接影响用户体验和事务吞吐量。
  • 排查
    • 这是最常见的提交相关等待事件。
    SELECT event, total_waits, time_waited, average_wait
    FROM v$system_event
    WHERE event = 'log file sync';
    
    • 同时检查 log file parallel write 事件(LGWR等待的事件),如果这个事件也很高,确认是磁盘I/O问题。
  • 解决
    • 优化底层存储I/O: 这是最根本的解决方案。使用更快的磁盘(SSD)、优化存储阵列配置、确保日志文件独占磁盘或LUN。
    • 调整提交模式: 同上,考虑批量提交。
    • 评估 COMMIT_WRITE 参数(12c+): 可以配置提交的持久性行为(如 COMMIT WRITE BATCH NOWAIT),但这会牺牲一定的持久性来换取性能,需谨慎评估业务需求。
五、 常用管理SQL语句
-- 1. 查看系统REDO生成统计(帮助评估负载)
SELECT *
FROM v$sysstat
WHERE name LIKE '%redo%';

-- 关键指标:
-- redo size: 自实例启动以来生成的REDO总量(字节)
-- redo entries: REDO记录的数量
-- redo synch writes: 由COMMIT触发的同步写次数

-- 2. 查看会话级别的REDO生成量(找出谁在大量写REDO)
SELECT s.sid, s.serial#, s.username, s.program, t.value AS "REDO SIZE"
FROM v$session s, v$sesstat t
WHERE t.sid = s.sid
AND t.statistic# = (SELECT statistic# FROM v$statname WHERE name = 'redo size')
AND t.value > 0
ORDER BY t.value DESC;

-- 3. 监控日志缓冲区等待和LGWR性能
SELECT event, total_waits, time_waited, average_wait
FROM v$system_event
WHERE event IN ('log buffer space', 'log file sync', 'log file parallel write');

-- 4. 查看当前日志文件状态和信息
SELECT group#, sequence#, bytes, members, status, archived
FROM v$log;

-- 5. 查看日志切换频率(历史信息)
SELECT sequence#, first_time, next_time, round((next_time-first_time)*24*60, 2) minutes
FROM v$log_history
ORDER BY sequence# DESC;

第二部分:通俗易懂的解释

让我们用一个比喻来理解这个复杂的过程。

把Oracle数据库想象成一个正在拍摄大型电影的片场。

  • 数据块: 电影的一帧帧胶片。
  • DML操作(UPDATE): 导演说:“把主角的帽子从蓝色换成红色。”
  • 更改向量: 这是最精妙的部分。片场不会为了换一顶帽子就重拍整个场景(记录整帧图像)。相反,他们会生成一份极其精确的**“修改清单”**:
    • 更改向量1(针对“历史记录盒”): “在第24号胶片帧中,主角的帽子原本是蓝色。”
    • 更改向量2(针对“正片”): “在第24号胶片帧中,将主角的帽子颜色改为红色。”
  • REDO记录: 导演的指令“换帽子”是一个不可分割的原子操作。所以场记把上述两份“修改清单”(更改向量)订在一起,形成一张总工作单(REDO记录),上面写着“场景X,镜头Y:换帽子操作”。
  • 重做日志缓冲区: 场记手边的临时备忘录本,他迅速地把这份总工作单抄录在本子上。
  • LGWR进程: 负责归档的秘书。她定期(或者在导演喊“卡!”即COMMIT时)跑过来,把备忘录本上最新几页的内容誊写到永久的、安全的总档案册(在线重做日志文件) 中。她的誊写是顺序的、快速的,只抄写文字(更改向量),而不是搬运整卷胶片(数据块)。
  • DBWR进程: 负责最终剪辑的剪辑师。他比较懒,会等积累了很多修改后,才把修改真正地、永久地写入到最终母带(数据文件)中。

当发生意外(实例崩溃)
电影片场突然停电,所有正在进行的剪辑(内存中的修改)都丢失了。来电后,恢复专家(SMON) 出场。

  1. 前滚: 他拿出秘书归档的总档案册(重做日志文件),从上次确认的地方开始,严格按照工作单的指示,一单一单地执行。例如,他看到“换帽子”的工作单,就:

    • 先根据第一份清单,在“历史记录盒”里记下帽子原是蓝色。
    • 再根据第二份清单,把正片里的帽子改成红色。
    • 不关心这个操作本身是否合理,他只是机械地、精确地重做所有记录在案的操作。这样,母带就恢复到了停电前一瞬间的状态,包括那些拍了一半还没导演喊“卡”的镜头。
  2. 回滚: 恢复专家现在检查导演的日程表(事务表),发现有些镜头导演没喊“卡”(未提交)。于是,他根据“历史记录盒”里的记录,把这些镜头的所有修改全部撤销。于是,最终母带里只留下了导演确认过的完美镜头。

争用是什么?

  • Log Buffer Space等待: 就像场记写得飞快,备忘录本都快写满了,但秘书誊写得太慢,场记不得不停下来等她腾空本子。
  • Log File Sync等待: 就像导演喊了“卡!”,但必须僵在原地,一动不动,直到秘书跑过来当着他的面把刚才那条指令的工作单誊写到总档案册里后,导演才能动并进行下一步。导演等待秘书的时间就是等待时间。

这种“向量化组装”机制是Oracle高性能和高可靠性的基石。它通过只记录最小必要的物理更改,并通过顺序I/O将其持久化,最大限度地减少了磁盘I/O这个数据库最大的瓶颈,同时保证了数据的绝对可恢复性。

欢迎关注我的公众号《IT小Chen

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值