Database Buffer Cache 工作原理

Database Buffer Cache 工作原理

适用版本:Oracle Database 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07


1. 概述

Database Buffer Cache 是 SGA 中最重要的组件,缓存数据块以减少磁盘 I/O[1]。

核心机制

  • LRU(最近最少使用)算法管理
  • 写延迟(Dirty Buffer 延迟写盘)
  • CR 块构造(读一致性)

2. 块的四种状态

状态含义
Pinned正在被进程访问(持有 pin)
Clean干净块(与磁盘一致)
Dirty脏块(已修改,未写盘)
Free空闲块(未被使用)

状态转换

Free ──读──> Pinned ──release pin──> Clean ──修改──> Pinned ──release pin──> Dirty


                                                                       DBWn 写盘


                                                                          Clean

3. LRU 链表

3.1 主 LRU 链表

     MRU 端(最近使用)                            LRU 端(最少使用)
     ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
     │B1│B2│B3│B4│B5│B6│B7│B8│B9│..│Bn│
     └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
       ↑                                    ↑
       新块入此                              优先淘汰此

新读入的块 → MRU 端
DBWn 写盘后 → MRU 端
查找空闲块 → 从 LRU 端开始

3.2 辅助 LRU 链表

     辅助 LRU(仅含 free 块)
     ┌──┬──┬──┬──┐
     │F1│F2│F3│F4│
     └──┴──┴──┴──┘

       DBWn 写盘后移入此
       新块优先从这里取

3.3 LRU-W(写链表 / Dirty List)

脏块链表,DBWn 从此链表取脏块写盘:

LRU-W(Dirty List)
┌──┬──┬──┬──┐
│D1│D2│D3│D4│
└──┴──┴──┴──┘

DBWn 从此开始写

4. 块访问流程

4.1 读操作

1. 进程请求访问 block X
2. 在 Buffer Cache 中查找:
   a. 找到:直接使用(Cache Hit)
   b. 未找到:从磁盘读入(Cache Miss)
3. Cache Miss 处理:
   a. 从 LRU 链表尾部找 free 块
   b. 若找到 dirty 块:移到 LRU-W,继续找
   c. 若 LRU-W 太长:触发 DBWn 写脏块
   d. 找到 free 块:从磁盘读入数据
   e. 块加 pin,移到 MRU 端
4. 进程访问块
5. 释放 pin

4.2 写操作

1. 进程请求修改 block X
2. 读入 Buffer Cache(如未在)
3. 加 pin(独占)
4. 生成 Redo 记录 → Redo Log Buffer
5. 修改块数据
6. 块状态从 Clean → Dirty
7. 移到 LRU-W
8. 释放 pin
9. DBWn 异步写盘

5. 全表扫描与 LRU 尾部插入

全表扫描的块默认插入 LRU 尾部,避免大表扫描挤出热数据[1]:

-- 查看是否使用尾部插入
SELECT name, value 
FROM v$sysstat 
WHERE name = 'physical reads cache';

-- 控制参数(默认 0 = 尾部插入)
SHOW PARAMETER _serial_direct_read;
SHOW PARAMETER _small_table_threshold;

小表优化:小于 _small_table_threshold 的表全表扫描会插入 MRU 端。


6. 多缓冲池

6.1 三种缓冲池

作用配置
DEFAULT默认池db_cache_size
KEEP常驻缓存db_keep_cache_size
RECYCLE一次性使用db_recycle_cache_size

6.2 使用场景

-- KEEP 池:访问频繁的小表(如字典表)
ALTER TABLE lookup_codes STORAGE (BUFFER_POOL KEEP);

-- RECYCLE 池:偶尔访问的大表(如历史表)
ALTER TABLE huge_logs STORAGE (BUFFER_POOL RECYCLE);

-- 查询对象所在缓冲池
SELECT table_name, buffer_pool 
FROM user_tables 
WHERE buffer_pool <> 'DEFAULT';

7. 非标准块大小缓冲池

支持 2K/4K/16K/32K 等非标准块大小:

-- 配置非标准块缓存
ALTER SYSTEM SET db_16k_cache_size=512M SCOPE=BOTH;

-- 创建非标准块表空间
CREATE TABLESPACE ts_16k 
  DATAFILE '/u01/ts16k.dbf' SIZE 1G 
  BLOCKSIZE 16K;

-- 查看配置
SELECT name, value/1024/1024 AS mb 
FROM v$parameter 
WHERE name LIKE 'db_%_cache_size';

8. 常见视图

-- 块状态统计
SELECT 
  status,
  COUNT(*) AS blocks
FROM v$bh
GROUP BY status;

-- Buffer Pool 命中率
SELECT 
  name, 
  1 - (physical_reads / NULLIF(db_block_gets + consistent_gets, 0)) AS hit_ratio
FROM v$buffer_pool_statistics;

-- 等待事件
SELECT event, total_waits, time_waited
FROM v$system_event
WHERE event IN (
  'free buffer waits',
  'buffer busy waits',
  'write complete waits',
  'read by other session'
);

9. 常见坑与排错

9.1 free buffer waits 等待严重

现象:DBWn 写脏块速度跟不上,无法找到 free 块。

修复

-- 1. 增加 DBWn 进程
ALTER SYSTEM SET db_writer_processes=4 SCOPE=SPFILE;

-- 2. 增大 Buffer Cache
ALTER SYSTEM SET db_cache_size=4G SCOPE=BOTH;

-- 3. 优化产生大量脏块的 SQL

9.2 buffer busy waits

现象:多个会话争用同一数据块。

排查

SELECT 
  event,
  p1 AS file#,
  p2 AS block#,
  p3 AS class#,
  COUNT(*) AS waits
FROM v$session_wait
WHERE event = 'buffer busy waits'
GROUP BY event, p1, p2, p3;

修复

  • 增大表的 INITRANS
  • 减少热点块争用(如使用哈希分区)
  • 使用 Sequence CACHE

9.3 read by other session

现象:多个会话同时读同一块,等待第一个会话完成物理读。

修复:优化全表扫描 SQL,避免大表并发扫描。

9.4 write complete waits

现象:等待 DBWn 完成写盘。

修复

  • 增加 DBWn 进程
  • 优化 I/O 子系统
  • 检查 ASM disk 配置

10. 最佳实践

  1. Buffer Cache 占 SGA 60-70%:最大化缓存命中
  2. 小表用 KEEP 池:避免被淘汰
  3. 大表用 RECYCLE 池:避免挤占热数据
  4. 监控命中率:应 > 95%
  5. 优化全表扫描:避免缓存抖动
  6. 合理设置 DBWn 进程数:CPU/8
  7. 使用 ASSM 表空间:减少段头争用

11. 参考资料

[1] Oracle Database Concepts 19c, “Database Buffer Cache” https://docs.oracle.com/en/database/oracle/oracle-database/19/cncpt/memory-architecture.html

[2] Oracle Database Administrator’s Guide 19c, “Managing the Buffer Cache” https://docs.oracle.com/en/database/oracle/oracle-database/19/admin/managing-memory.html

[3] AskTOM, “Buffer Cache Internals” https://asktom.oracle.com/pls/apex/f?p=100:1:0