INSERT、DELETE、UPDATE、CREATE TABLE 最终都是改物理数据页。Redo 记下页怎么变。故障重启或另一份库按产生顺序把记录作用到页上,文件内容就对齐。
临时表的修改不产生 Redo。所以临时表改动过不了另一份库;STANDBY 上仍允许改临时表,手册给的理由就是这一条,外加使用上灵活一点。
一条用户 SQL 在内部会拆成多个相互独立的 物理事务(Physical Transaction,PTX)。PTX 是一组改页动作:原子、有序、不能撤销。它和用户事务不是同一个东西。用户 COMMIT 往往对应多个 PTX。物理事务提交时,把产生的 Redo 写入当前日志包。
PTX 里是一条条 RREC(Redo Record),一条 RREC 对应一次改页动作。分两类。
物理 RREC:操作类型;页地址(表空间号、文件号、页号、页内偏移);页上改了什么。变长记录在记录头后面还有两字节长度。
逻辑 RREC:事务启动、提交、回滚、字典封锁、事务封锁、B 树封锁、字典淘汰等。手册写明这是为数据守护加的:备库重演和用户访问备库并发时要能对上锁;主库 DDL 后两边字典缓存不能各拿各的旧对象。备库解析到逻辑 RREC 时按内容生成事务、封锁对象、从字典缓存里淘汰过期对象。逻辑 RREC 一般在 PRIMARY 模式下才产生。
结构上可以记成:PTX 头 + 若干 RREC(每条有 type、len、数据;物理 RREC 还带 ADDR)。传输和刷盘不按单条 RREC 走,见下一节。
任何数据页从 Buffer 刷到磁盘之前,修改它的 Redo 必须已经写入联机日志文件。这是 WAL。COMMIT 保证的是本事务相关 Redo(含提交标记)落进联机日志;不保证脏页已经进 .dbf。掉电后靠有效 Redo 把页重做成提交后的样子。
检查点线程把 LSN ≤ CKPT_LSN 的那些页从 Buffer 写入数据文件,再推进 CKPT_LSN。推进之后,更早的联机日志占用的空间可以覆盖。检查点越勤,故障后要扫的 Redo 越短;越勤,数据页 I/O 越多。
启动过程里,联机日志重做完成后、回滚活动事务之前,状态是 After Redo。非 STANDBY 在进 OPEN 前也会经过这里。重做的起点不是文件头,是 0 号联机日志文件头里记下的 [CKPT_FILE, CKPT_OFFSET]。
MOUNT 不生成 PWR 日志,数据页仍可刷盘,检查点仍推进。OPEN 切到 MOUNT 会强制回滚活动事务,不强制 Purge 已提交事务,不强制断连接,也不强制把 Buffer 脏页刷完。
SUSPEND 仍可访问对象、甚至改数据,但限制 Redo 刷盘。COMMIT 一类会触发刷盘的语句会挂住。切 SUSPEND 比重切 MOUNT 轻:不回滚活动事务。实时归档失败时 PRIMARY 会试图进 SUSPEND,避免再往联机日志里写一段对端没收到的包。
PTX 提交时 Redo 写入当前日志包。包是批量保存 PTX 的单元,大小不固定,固定包头加可变包头,里面连续放一个或多个 PTX。包头有类型、长度、包序号、LSN、产生日志的节点号、加密压缩、并行路数等。包能自描述。
刷盘和发到另一实例的最小单位是包,不是单条 RREC。触发刷盘:用户事务提交,或包写满。刷盘线程把包写入联机日志;配了归档时,刷盘线程还负责触发归档。发送方是刷盘线程,不是 dmwatcher。
包序号生成时连续递增。相邻包的 LSN 总体递增;DSC 多节点下不一定连续。未开并行日志时,包内 LSN 递增。开了并行日志,一个包里有多路,每一路内部递增,路与路之间整体无序,所以是局部有序、整体无序。
每个包有 LSEQ 和 GSEQ。
LSEQ:节点内唯一、连续。校联机日志连续性。
GSEQ:守护组内主备共同维护,全局唯一、连续、递增。校归档连续性。判断归档是否连续、做备库归档恢复时,GSEQ 和 LSN 成对用,不要只盯一个。
写入联机日志前给包分配一个 GSEQ。每次把包写入联机日志后改 FILE_SEQ。
| 名字 | 含义 |
|---|---|
| CUR_SEQ / CSEQ | 已分配的最大 GSEQ。主库:最新产生的包。备库:CSEQ 与自身 FSEQ 相同,不必等于主库 CSEQ |
| FILE_SEQ / FSEQ | 已写入联机日志的最大 GSEQ。主库:本节点已落文件的最新包。备库:非控制节点此字段保持不变;控制节点跟与本控制节点号相同的那个主库节点的 FSEQ |
| APPLY_SEQ / ASEQ | 备库已把主库原包写入自己联机日志时的原始 GSEQ。主库是 DSC 时,备库按主库每个节点各维护一套 |
| RPKG_SEQ / RSEQ | 备库已重演完的最大 GSEQ。主库是 DSC 时同样按节点各维护 |
| SSEQ | 备库明确可重演的最大 GSEQ |
| KSEQ | 已收到、尚未确定能否重演的最大 GSEQ |
SELECT CUR_LSN, FILE_LSN, FLUSH_LSN, CKPT_LSN,
CUR_SEQ, FILE_SEQ
FROM V$RLOG;
列名以现场 V$RLOG 为准。有的版本没有 FLUSH_LSN,有的还有 TOTAL_SPACE / FREE_SPACE。没有的列去掉再查。
LSN(Log Sequence Number)是系统维护的递增 Bigint,全局唯一地标识一次物理事务。物理事务提交时分配 CUR_LSN+1,再把 CUR_LSN 改成这个值。
动态视图主要是 V$RLOG。备库重演侧还有 V$RAPPLY_SYS、V$RAPPLY_PARALLEL_INFO、V$KEEP_RLOG_PKG。
| 名字 | 含义 |
|---|---|
| CUR_LSN / CLSN | 已分配的最大 LSN。主库:最新产生。备库:准备重演刷盘的那个包上的最大 LSN,不是「这份库自己最新产生的用户修改」 |
| FILE_LSN / FLSN | 已写入联机日志文件。每写完一个包就更新 |
| FLUSH_LSN | 已发起刷盘、文件尚未写完 |
| CKPT_LSN | 检查点。LSN ≤ 此值的页已从 Buffer 进数据文件。检查点线程维护 |
| APPLY_LSN / ALSN | 备库已把主库原包写入自己联机日志。取自主库原包中的最大 LSN。主库是 DSC 时按节点各维护 |
| RPKG_LSN / RLSN | 备库已经重演完成的最大 LSN。主库是 DSC 时按节点各维护 |
| SLSN | 备库明确可重演的最大 LSN |
| KLSN | 已收到、尚未确定能否重演。KEEP 包用这个 |
收到包不等于重演完。看同步看 ALSN、RLSN、SLSN,不能只看归档 Valid。
单机上能用这几个差判断本机日志路径:
CUR_LSN 往前跑、FILE_LSN 落后:包已分配,刷盘慢。COMMIT 会等刷盘。FILE_LSN 往前跑、CKPT_LSN 落后:联机日志里有效段在变长,检查点慢或刷脏跟不上。可用空间变少时检查点会被逼着跑。FILE_LSN 往前跑、RPKG_LSN 落后:对端 APPLY 慢。监视器、守护日志里的 CLSN/FLSN/ALSN/RLSN/SLSN 要和 V$RLOG、V$RAPPLY_SYS 对同一实例。数字对不上,先确认查的是哪一端:同一套缩写在主备上含义不同。
默认两个文件(如 DAMENG01.log、DAMENG02.log),内部称 0 号、1 号。包顺序写入,写满切下一个。0 号是主文件,文件头有 CKPT_LSN、CKPT_FILE、CKPT_OFFSET、FILE_LSN 等。故障重启从 [CKPT_FILE, CKPT_OFFSET] 起读包、解析 RREC、改对应页。
检查点推进后,已刷进数据文件的那些 Redo 占用的空间可以覆盖。文件长度不随业务无限涨。这和本地归档相反:归档文件不能覆盖,直到删除或碰到空间上限。见 [[29_实时归档即时归档与KEEP]]。
三段:
(CKPT_LSN, FILE_LSN],掉电必须重做(FILE_LSN, FLUSH_LSN],已请求刷盘尚未落文件再往后到 CUR_LSN 的是已分配、还在包里未发起刷盘或未刷完的部分。
备库重演也会写自己的联机日志,但备库本地归档里是主库原包,所以备库联机日志和备库归档文件内容不完全一样。组内各节点归档文件要对齐的是「源库产生的那份包」。
查文件清单(路径、大小列名以版本为准):
SELECT * FROM V$RLOGFILE;
常见还能看到路径、大小。两个文件都在、大小接近配置值,是循环覆盖,不是「日志盘在涨」。归档目录在涨才是归档文件在堆积。
实时路径:目标先把最新收到的包放在内存里,不立刻重演,这份叫 KEEP_RLOG_PKG。KLSN / KSEQ 标的就是「已收到、尚未确定能否重演」。SLSN / SSEQ 标的是「明确可重演」。即时路径先写源端联机日志再发,没有 KEEP。
手册用 LSN 把窗口钉死:源端在写入联机日志之前就发出去,对端已经拿到包。源端此时宕机,对端若立刻重演,源端重启后联机日志里没有这段,两边对不齐。KEEP 先压住;源端继续当主则丢 KEEP;对端接管则先重演 KEEP。接管时 APPLY_LSN ≥ 源端 FILE_LSN。
KEEP 启动重演的三个时机:又来了一个新包(旧 KEEP 进 APPLY,新包当 KEEP);源端定时把 FILE_LSN 过来且等于本端 SLSN(说明 KEEP 对应的那段已经在源端落盘);切成新主之前。细节、ini、状态机见 [[29_实时归档即时归档与KEEP]]。
单机 NORMAL、只开本地归档时,不会出现 KEEP。V$KEEP_RLOG_PKG 空是正常的。
单机查的值:
SELECT PARA_NAME, PARA_VALUE
FROM V$DM_INI
WHERE PARA_NAME IN (
'CKPT_INTERVAL',
'CKPT_RLOG_SIZE',
'CKPT_DIRTY_PAGES',
'CKPT_FLUSH_RATE',
'CKPT_FLUSH_PAGES',
'RLOG_BUF_SIZE',
'RLOG_POOL_SIZE',
'RLOG_SAFE_SPACE',
'BUFFER'
)
ORDER BY PARA_NAME;
定时、日志占用、脏页数这几路检查点可以同时生效,某路为 0 表示关掉这一路。有效 Redo 把环写太满时,还会为了腾可用空间被逼着检查点。
手工检查点(权限足够时)。有的版本支持过程/函数,有的控制台命令和 DISQL 关键字不一样:
SELECT CHECKPOINT(100) FROM DUAL;
100 表示按该版本约定去刷一批脏页(常见是比例)。若报错,再试不带参数的 CHECKPOINT;,或查本版本管理员手册里检查点语句。不要假设 DISQL 一定认无参 CHECKPOINT;。
不需要第二套实例,为了看见 PTX 提交后尺子怎么动、刷盘和检查点不是一回事、SUSPEND 卡的是刷盘、联机日志循环覆盖。
9.1 先看尺子和文件
SELECT STATUS$, MODE$ FROM V$INSTANCE;
SELECT NAME, ARCH_MODE FROM V$DATABASE;
SELECT CUR_LSN, FILE_LSN, CKPT_LSN FROM V$RLOG;
若本版本有这些列,一并看空间:
SELECT TOTAL_SPACE, FREE_SPACE FROM V$RLOG;
SELECT * FROM V$RLOGFILE;
记下 CUR_LSN、FILE_LSN、CKPT_LSN。CUR_LSN ≥ FILE_LSN ≥ CKPT_LSN 是常态。三者相等说明刚检查点过、没有未刷的包;不相等是正常负载,不是故障。
9.2 造 Redo,看 CUR / FILE 前进
CREATE TABLE LSN_SOLO(C1 INT, C2 VARCHAR(80));
INSERT INTO LSN_SOLO VALUES (1, 'one-row');
COMMIT;
SELECT CUR_LSN, FILE_LSN, CKPT_LSN FROM V$RLOG;
一行提交后 CUR_LSN、FILE_LSN 应比 9.1 大。CKPT_LSN 不必立刻跟上。
再批量造,让差距更容易看见:
BEGIN
FOR I IN 1..20000 LOOP
INSERT INTO LSN_SOLO VALUES (I, 'redo-gen');
IF MOD(I, 1000) = 0 THEN
COMMIT;
END IF;
END LOOP;
COMMIT;
END;
/
SELECT CUR_LSN, FILE_LSN, CKPT_LSN FROM V$RLOG;
批量过程中反复查。CUR_LSN 先动、FILE_LSN 随后跟上。若 FILE_LSN 长期不追,查磁盘和日志相关等待,不要先去改归档。
DDL 也会进 Redo:
CREATE TABLE LSN_DDL(C1 INT);
ALTER TABLE LSN_DDL ADD C2 INT;
DROP TABLE LSN_DDL;
COMMIT;
SELECT CUR_LSN, FILE_LSN FROM V$RLOG;
9.3 检查点推进 CKPT_LSN
SELECT CUR_LSN AS C, FILE_LSN AS F, CKPT_LSN AS K FROM V$RLOG;
SELECT CHECKPOINT(100) FROM DUAL;
SELECT CUR_LSN AS C, FILE_LSN AS F, CKPT_LSN AS K FROM V$RLOG;
第二次 CKPT_LSN 应靠近当时的 FILE_LSN。有效区间 (CKPT_LSN, FILE_LSN] 变短。联机日志文件在操作系统里的字节数通常不变。
若函数报错,不要循环重试同一句。换本版本的检查点语法,或等 CKPT_INTERVAL 到点后再查一次 CKPT_LSN。
9.4 SUSPEND 卡住的是刷盘
单机 NORMAL 可做。看清效果立刻 OPEN,不要挂在业务会话上。
先记下 FILE_LSN。
ALTER DATABASE SUSPEND;
SELECT STATUS$ FROM V$INSTANCE;
SELECT CUR_LSN, FILE_LSN, CKPT_LSN FROM V$RLOG;
再在同一会话:
INSERT INTO LSN_SOLO VALUES (-1, 'suspend-test');
COMMIT;
COMMIT 会挂住。另开会话查 FILE_LSN,应停在 SUSPEND 之前。这就是手册说的「限制 Redo 刷盘」。
恢复:
ALTER DATABASE OPEN;
SELECT STATUS$ FROM V$INSTANCE;
SELECT CUR_LSN, FILE_LSN, CKPT_LSN FROM V$RLOG;
SELECT COUNT(*) FROM LSN_SOLO WHERE C1 = -1;
OPEN 之后挂住的 COMMIT 才能结束,FILE_LSN 前进,那一行可见。PRIMARY/STANDBY 下 OPEN 要加 FORCE。单机 NORMAL 一般不用。没有第二实例时不要 ALTER DATABASE STANDBY。
MOUNT 和 SUSPEND 不能直接互切。要经过 OPEN。
9.5 联机日志在盘上的样子
数据目录按自己的装法改。下面假设 /dmdata/DAMENG。
ls -l /dmdata/DAMENG/*.log
应能看到 DAMENG01.log、DAMENG02.log(库名不同则前缀不同)。大小是初始化或后来扩出来的固定长度量级,造完 9.2 的 Redo 再 ls,这两个文件通常还是那么大。归档目录 /dmarch/DAMENG 若已打开,那里的 ARCHIVE_LOCAL1_*.log 才会随日志量增加。
9.6 有第二实例以后对哪几列(先记着,现在不必连)
备库:
SELECT * FROM V$RAPPLY_SYS;
SELECT * FROM V$RAPPLY_PARALLEL_INFO;
SELECT * FROM V$KEEP_RLOG_PKG;
APPLY / RPKG 应跟上源端已落盘的部分。KEEP 非空时 KLSN 可能大于已重演的 RLSN。即时目标上 KEEP 应一直空。现在没有 STANDBY 不要为了跑这三条去改角色。
同一套名字,主库 CLSN 是最新产生的物理事务;备库 CLSN 是准备重演刷盘的包。拿备库 CLSN 当「业务已经写到哪」会看错。
FILE_LSN 是本实例联机日志。备库的 FILE_LSN 含重演时自己新写的 Redo,和归档文件里的主库原包不是同一份内容。对齐归档用 GSEQ + 原包 LSN(ALSN),不要拿两端 FILE_LSN 直接减。
并行日志开着时,不要假设一个包内 LSN 全程递增。
CKPT_LSN 落后不是归档失败。先看检查点和脏页。归档 Valid 也不是 APPLY 完成。
文章
阅读量
获赞
