前言
不管现阶段美国和中国对峙到何种程度,不管桀傲不驯拉里.埃里森如何不看好中国,Oracle 仍是数据库中的一枝独秀。然而,他山之石可以攻玉,多个国产数据库在关键技术攻关方面的整体水平也已达到国际先进。
国内越来越多的 Oracle 数据库开始下线,迁移到开源或者国产数据上,o2k 支持实时增量的将 Oracle 数据库增量变化抽取出来,助力国产化数据库无缝接管 Oracle。
笔者作为数据库内核的负责人参与实现了 o2k 来解析 Oracle 日志,并于 2022 年 2 月 15 号把它开放出来给社区免费使用,期间经历了各种迷茫,获得了众多大佬的指导,最终总算是交出了一份还算不错的试卷。
作为一个 DBA 兼开发人员,在整个 o2k 的实现过程中,对于 Oracle 数据库的数据库理论和工程能力学习了很多,也进一步对数据库原理有了进一步了解。
借助 Oracle 的设计理念和实现,我们也看看是否能对新一代的数据库从业者有一定的帮助和借鉴作用。
国内软件由于美国的打压,数据库作为基础软件赢来了一个难得的蓬勃发展期。
曾几何时,数据库从业人员只是公司 IT 团队->运维团队里的一个小小部门,而今一个能指导开发正确使用数据库,选择数据库来适配业务来适应业务的数据架构师供不应求。
如果能掌握着数据库原理,甚至能主动改造数据库适配相关应用场景的人才年薪至少百万。
作为计算机皇冠上的其中一粒明珠,数据库上承业务在线离线之责,下接硬件内核之妙,看到越来越多的人才加入数据库及数据库内核队伍,不胜欣慰!
本系列文章中我们将由浅入深以 Oracle 日志解析遇到的重重阻塞为例,来介绍在数据库中常见,而又关键的概念,了解数据库设计思路及工程实现中需要注意的事项。
本文以浅为主,我们先简单介绍一下数据库的背景和 Oracle 日志解析的基础知识
前置知识了解
数据库日志
- 类似于银行账户系统一样,张三存入 100 块钱会'先'被记录为'张三增加 100'的流水账,然后再把张三的账户从 1000 块修改为 1100 块。
- 数据库为了保证原子性和持久化,也会'先'在 redo 日志中记录一笔或者多笔 redo record,然后再修改数据库实际的行记录
- 注意,这里的“修改账户/修改行记录”都是在“写流水账/写日志”之后完成的。也就是说 redo 先于“数据写入”,这也就是著名的数据库 write-ahead logging (WAL) 。
- Oracle 写数据库日志采用的是物理日志方式,记录的是内部数据块的变化;MySQL 在 Server 层的 binlog 是逻辑日志,记录的是逻辑行数据的变化。所以对 Oracle 的日志解析不仅需要理解日志本身 filespace、redo record,change vector 的变化,还需要理解 Oracle 内部数据存储的格式。

怎么查看 Oracle 日志中记录的内容
Oracle 中有专门的命令,支持将指定的二进制 redo 日志解析为逻辑的文本文件,类似于 MySQL 提供的 mysqlbinlog 工具,方便用户查看和诊断数据库问题。
ALTER SYSTEM DUMP LOGFILE '/opt/oracle/oradata/master1/redo01.log';
当然,这个解析出来的文本文件也并不是那么容易理解,下文中我们会对关键信息进行解读。
另外,这个逻辑的文本文件也并不是把所有的二进制 redo 日志中的信息都解析并输出出来,例如 supplement log 这种,就没有输出
supplemental log
Oracle 默认只记录变化的信息,类似于 MySQL 中 binlog_row_image=minimal 的情况。
也就是说,你执行了 update t1 set c2=3 where id=2,它只会记录 c2=3 的后镜像数据和 c2=2 修改之前的前镜像数据。
对于主备物理块同步,这些信息已经足够了,但是对于数据仓库或者大数据平台,不知道到底更新的是哪一行(id=2)的数据,是无法保持跟 Oracle 的数据一致的。
通过 alter database add supplemental log data (primary key) columns;可以让 Oracle 在记录日志的时候顺便把 primary key 主键也记录下来,方便同步工具将变化对应到具体哪一行,这些额外增加的日志称为 supplemental log。
OGG,O2K 等同步工具都会要求源端 Oracle 开启 supplemental log。
日志组织形式
- WAL 日志是逻辑的日志表现形式,一个 update 的事务更新了 5 行数据,会产生多个 redo record(begin,5 行记录的前镜像和后镜像,commit),而这些日志记录到日志文件中是以一个 block 一个 block(默认为 512 字节)写到文件中的
- 逻辑上,数据库日志是由一个一个的 redo record 组成的;
- 物理上,数据库日志文件中每个 block 都有 Header 和 Tail,逻辑的 redo record 记录到物理文件中对应关系如下:

redo 和 undo
日志记录内容
说了这么多理论的、务虚的东西,我们来点干货。
首先,我们看看我们做一个 update 一行数据到底会生成哪些日志信息,为了排除其他的干扰,我们在做 update 之前和之后都 switch logfile,保证我们查询的 redo log 中只有 update 一个变更
在 Oracle 上执行的语句如下
## 这里新建一个表用于测试
create table test1 (id number primary key, name varchar2(15) not null, hiredate date default(sysdate) );
insert into test1 (id, name) values (1, 'o2k1');
insert into test1 (id, name, hiredate) values (2, 'o2k2', sysdate);
commit
## 为了排除其他的干扰,我们在做update之前和之后都switch logfile,保证我们查询的redo log中只有update一个变更
ALTER SYSTEM SWITCH LOGFILE;
update test1 set name='o2k3' where id=2;
commit;
ALTER SYSTEM SWITCH LOGFILE;
## 查询到底应该从那个归档日志中获取update的变更
select * from v$archived_log;
## 将二进制日志反解析出来
ALTER SYSTEM DUMP LOGFILE '+SSDDG1/chenmm/archivelog/2022_05_12/thread_2_seq_114.1017.1104513037';
## 查看日志被导入到哪个trace文件了
select value from v$diag_info where name='Default Trace File'; # 返回ora_6130.trc
这里我们得到两个文件:
- 二进制的 redo 日志文件"thread_2_seq_114.1017.1104513037"
- 根据这个二进制日志解析出来的文本文件 ora_6130.trc
相关的内容我都放到了文末的附录中了,大家可以自行查看。
redo 逻辑结构
我们先从文本文件 ora_6130.trc 入手查看一下 redo 文件的逻辑形式。参考 redo 逻辑格式(见附录),可以看到
- FILE HEADER:日志文件有日志文件的头,记录了 Db Id,Db Name 的数据库信息,也记录了文件大小、文件类型以及 Blksiz=512(也就是说,redo 物理块大小为 512 字节),对比 redo 物理格式(见附录)FILE HEADER 是放到第一个 512 字节的 BLOCK 中。这里的文件号对应的就是日志序号 Seq#
FILE HEADER:
File Number=3, Blksiz=512, File Type=2 LOG
- REDO HEADER:另外这里还记录了这是 RAC 的哪个节点,是那个序号的 redo 日志,scn 的范围,"Thread 0002, Seq# 0000000114, SCN 0x0000004f