1.1、数据块结构
Oracle数据块是I/O的最小单位,其内部结构如下:
|---------------------|------------------------|
| 组成部分 | 说明 |
| Common Header | 块类型、格式、SCN等基本信息 |
| Table Directory | 该块中存储了哪些表的数据 |
| Row Directory | 每行数据在块内的位置指针 |
| Free Space | 未使用的空间,用于INSERT和UPDATE |
| Row Data | 实际的行数据存储区域 |
| Block Overhead | 块管理元数据,约占84~107字节 |
1.2、块大小选择
Oracle支持2KB、4KB、8KB(默认)、16KB、32KB五种块大小。选择原则:
- **推荐:**OLTP场景(短事务、高并发):8KB(默认),平衡行锁和缓存效率
- **大块优势:**DSS/数据仓库(大范围扫描):16KB或32KB,减少I/O次数
- **通用建议:**混合负载:8KB通常是最佳折中
sql
-- 查看当前块大小
SHOW PARAMETER db_block_size;
-- 非标准块大小表空间(需要配置对应的缓冲池)
CREATE TABLESPACE ts_16k
DATAFILE '+DATA' SIZE 10G
BLOCKSIZE 16K;
-- 必须先配置16K缓冲池
ALTER SYSTEM SET db_16k_cache_size = 2G SCOPE=BOTH;
实例:块大小优化
**S --- Situation(场景):**某OLTP系统数据库(块大小8KB)在处理大量小事务时出现Buffer Busy Waits等待事件,热块争用严重,AWR报告显示buffer busy waits占总等待时间的15%。
T --- Task **(任务):**分析热块争用原因,评估是否需要调整块大小或采用其他优化手段。
A --- Action(行动):
1、分析AWR报告,定位热块争用的具体段:
SELECT owner, object_name, statistic_name, value FROM v$segment_statistics WHERE statistic_name = 'buffer busy waits' ORDER BY value DESC FETCH FIRST 10 ROWS ONLY;
2、发现IDX_ORDER_STATUS索引是主要争用源;
3、评估方案:
方案A:调整块大小(需要重建数据库,不可行)
方案B:使用反向键索引(适合等值查询但不适合范围查询)
方案C:分区索引(将索引分散到多个段,最优方案)
4、选择方案C,创建LIST分区索引。
**R --- Result(结果):**使用分区索引后,buffer busy waits等待时间下降70%。索引段从1个分散到16个分区段,热块争用基本消除。TPS从12000提升至18000,系统吞吐量提升50%。(结论:大多数情况下,调整块大小不如优化索引结构有效。)
1.3、 PCTFREE 和 PCTUSED
PCTFREE和PCTUSED是控制块空间使用的关键参数:
- PCTFREE **:**块中保留的空闲空间百分比,用于UPDATE操作。默认10%。
- **PCTUSED:**块使用率低于此值后才重新加入Freelist(仅MSSM)。默认40%。
sql
-- 创建表时设置PCTFREE/PCTUSED
CREATE TABLE orders (
order_id NUMBER,
customer_id NUMBER,
order_date DATE,
amount NUMBER(12,2)
) PCTFREE 20 PCTUSED 40;
-- ASSM模式下查看实际块使用情况
ANALYZE TABLE orders COMPUTE STATISTICS;
SELECT table_name, num_rows, blocks, empty_blocks,
avg_space, chain_cnt
FROM dba_tables
WHERE owner = 'APP_USER' AND table_name = 'ORDERS';
-- DBMS_SPACE包分析块使用详情
DECLARE
unformatted_blocks NUMBER;
unformatted_bytes NUMBER;
fs1_blocks NUMBER; fs1_bytes NUMBER;
fs2_blocks NUMBER; fs2_bytes NUMBER;
fs3_blocks NUMBER; fs3_bytes NUMBER;
fs4_blocks NUMBER; fs4_bytes NUMBER;
full_blocks NUMBER; full_bytes NUMBER;
BEGIN
DBMS_SPACE.SPACE_USAGE(
segment_owner => 'APP_USER',
segment_name => 'ORDERS',
segment_type => 'TABLE',
unformatted_blocks => unformatted_blocks,
unformatted_bytes => unformatted_bytes,
fs1_blocks => fs1_blocks, fs1_bytes => fs1_bytes,
fs2_blocks => fs2_blocks, fs2_bytes => fs2_bytes,
fs3_blocks => fs3_blocks, fs3_bytes => fs3_bytes,
fs4_blocks => fs4_blocks, fs4_bytes => fs4_bytes,
full_blocks => full_blocks, full_bytes => full_bytes
);
DBMS_OUTPUT.PUT_LINE('0-25% free: ' || fs1_blocks);
DBMS_OUTPUT.PUT_LINE('25-50% free: ' || fs2_blocks);
DBMS_OUTPUT.PUT_LINE('50-75% free: ' || fs3_blocks);
DBMS_OUTPUT.PUT_LINE('75-100% free: ' || fs4_blocks);
DBMS_OUTPUT.PUT_LINE('Full blocks: ' || full_blocks);
END;