主从延迟检测必须在备库查询v$dataguard_stats视图获取apply_lag和transport_lag值,.net core仅负责执行与解析;连接须指向备库、禁用连接池,并用字符串方式读取interval类型后解析为timespan,同时需缓存降频、避免直曝接口。

主从延迟检测必须在数据库层查视图,.NET Core只负责执行和解析
Oracle DG 的主从延迟不是靠 .NET 代码“算出来”的,而是通过查询备库的动态性能视图(如 v$dataguard_stats、v$archive_dest_status)拿到原始延迟值,再由 .NET 解析。你不能在应用层用时间戳差或日志序列号自己推算——因为 Oracle 内部有多种延迟来源(网络传输、RFS 写入、MRP 应用、归档切换等),只有这些视图能反映真实状态。
关键点:查询必须在**备库(standby)实例**上执行,且连接用户需有 SELECT_CATALOG_ROLE 或对相关视图的显式 SELECT 权限。主库上查不到有效延迟数据。
- 最常用语句:
SELECT NAME, VALUE, DATUM_TIME FROM v$dataguard_stats WHERE NAME IN ('apply_lag', 'transport_lag') -
apply_lag表示已接收但尚未应用的日志时长(即真正业务可见延迟) -
transport_lag表示主库生成日志到备库收到日志的时间差(网络+RFS开销) - 若返回空或
VALUE为+开头(如+00 00:00:00),说明无延迟;若为+02 05:30:12,则表示延迟约 2 小时 5 分钟
用 Dapper 执行延迟查询时,连接字符串必须指向备库且禁用连接池
如果你用 Dapper 或原生 OracleConnection 查询延迟,连接串里的 DataSource 必须是备库的 TNS 别名或 Easy Connect 字符串(如 (DESCRIPTION=(ADDRESS=...)(CONNECT_DATA=(SERVICE_NAME=ORCL_STBY))))。写库连接串不能复用——哪怕只是读取延迟,也必须走独立连接路径。
务必关闭连接池,否则可能因连接复用导致后续写操作误连到备库:
- 在连接字符串末尾加
;Pooling=false - 不要把该连接放进全局连接池(比如 EF Core 的
AddDbContextPool) - 避免使用
TransactionScope包裹该查询——备库通常为READ ONLY模式,事务会直接失败
延迟值解析要处理 Oracle 的 INTERVAL DAY TO SECOND 类型
Oracle 返回的 apply_lag 是 INTERVAL DAY(3) TO SECOND(0) 类型,.NET 的 Oracle.ManagedDataAccess.Core 默认映射为 TimeSpan,但存在两个坑:
- 当延迟超过 31 天时,
INTERVAL DAY(3)会溢出,Oracle 返回NULL或报错ORA-01873;应改用STRING显式读取原始字符值(如"+045 03:22:17")再解析 - 负值(如
"-00 00:00:01")表示备库比主库“快”,极少见,但解析逻辑必须兼容 - 推荐解析方式:
var ts = TimeSpan.ParseExact(value.TrimStart('+'), @"d\.h\:mm\:ss", null),注意d对应天数,需支持 1~3 位数字
高频轮询延迟会加重备库负载,必须加缓存与降频
每秒都查 v$dataguard_stats 是危险操作——它触发内部统计刷新,且在高并发下可能引发 latch 竞争。生产环境应严格控制轮询节奏:
- 基础监控:每 30 秒查一次,延迟 > 60 秒时自动缩至 10 秒粒度
- 告警触发后,可临时切到 5 秒轮询,但持续不超过 2 分钟
- 在 .NET 中用
MemoryCache缓存最近一次结果,避免重复查询;设置绝对过期时间为 25 秒,防止缓存击穿 - 禁止在 Web API 接口里直接暴露该查询——应走后台 HostedService + SignalR 推送,避免请求放大
真正难的不是怎么查,而是判断“多大延迟算异常”:有些报表类业务容忍 5 分钟延迟,而金融交易类必须











