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. 最佳实践
- Buffer Cache 占 SGA 60-70%:最大化缓存命中
- 小表用 KEEP 池:避免被淘汰
- 大表用 RECYCLE 池:避免挤占热数据
- 监控命中率:应 > 95%
- 优化全表扫描:避免缓存抖动
- 合理设置 DBWn 进程数:CPU/8
- 使用 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