开发转兼职DBA(五):从救火到防火------参数、内存、监控、备份
前面两篇都是数据库起不来了硬恢复。这篇讲怎么从"出事再救"转向"提前预防"------参数配置、内存结构、性能监控、备份策略。
文章目录
转变
两次数据库起不来的事故之后,我意识到一个事实:每次都是出了事才学,每次都是在生产环境上赌命。
开发者面对数据库的态度,通常有三个阶段:
- 能用就行------SQL写对了就行,不管性能
- 出事再救------查询慢了看执行计划,数据库挂了硬恢复
- 提前预防------配好参数、建好监控、做好备份
这篇讲的是从阶段二到阶段三的转变。
一、Oracle的内存结构
调参数之前,得先知道数据库的内存是怎么组织的。
Oracle的内存分两大块:SGA (System Global Area)和PGA(Program Global Area)。
SGA------所有进程共享的内存
SGA
├── Database Buffer Cache(数据缓冲区)
│ └── 存数据块的副本,减少磁盘读取
├── Redo Log Buffer(日志缓冲区)
│ └── 存redo记录,提交时刷到redo log文件
├── Shared Pool(共享池)
│ ├── Library Cache(库缓存)------存SQL的解析结果和执行计划
│ └── Data Dictionary Cache(数据字典缓存)------存表结构、索引信息
├── Large Pool(大池,可选)
│ └── RMAN备份、并行查询等大块操作
└── Java Pool(Java池,可选)
└── JVM相关
PGA------每个进程独占的内存
PGA
├── SQL工作区(排序、哈希连接用的内存)
├── 会话信息
└── 游标状态
哪些参数控制这些内存
Oracle 10g以后,可以用一个参数自动管理大部分内存:
sql
ALTER SYSTEM SET memory_target=4G SCOPE=SPFILE;
Oracle自动在SGA和PGA之间分配。简单,但不够精细。
手动管理模式------更可控:
sql
-- SGA大小
ALTER SYSTEM SET sga_target=3G SCOPE=SPFILE;
-- PGA大小
ALTER SYSTEM SET pga_aggregate_target=1G SCOPE=SPFILE;
再细一点:
sql
-- 共享池(SQL解析、执行计划缓存)
ALTER SYSTEM SET shared_pool_size=512M SCOPE=SPFILE;
-- 数据缓冲区(数据块缓存)
ALTER SYSTEM SET db_cache_size=1G SCOPE=SPFILE;
-- 日志缓冲区
ALTER SYSTEM SET log_buffer=16M SCOPE=SPFILE;
常见的内存问题
1. 共享池太小
症状:SQL执行慢,但不是查询本身慢------是每次都要重新解析SQL。
sql
SELECT sql_text, executions, parse_calls
FROM v$sqlarea
WHERE parse_calls > executions;
如果parse_calls接近executions,说明SQL几乎每次都在重新解析。可能的原因:
- 共享池太小,缓存的执行计划被挤掉了
- SQL没有用绑定变量,每次都是硬解析
2. 数据缓冲区太小
症状:磁盘读取频繁。
sql
SELECT name, value
FROM v$sysstat
WHERE name IN ('db block gets from cache', 'consistent gets from cache', 'physical reads');
计算命中率:
命中率 = 1 - (physical reads / (db block gets from cache + consistent gets from cache))
命中率低于90%,考虑增大数据缓冲区。
3. 排序溢出到磁盘
症状:排序操作慢。
sql
SELECT name, value
FROM v$sysstat
WHERE name IN ('sorts (memory)', 'sorts (disk)');
如果sorts (disk)不为0,说明排序在内存里放不下,溢出到临时表空间了。增大PGA或pga_aggregate_target。
二、关键参数配置
除了内存参数,还有几个影响数据库行为的参数。
游标相关的参数
sql
-- 每个会话能打开的游标数
ALTER SYSTEM SET open_cursors=300 SCOPE=SPFILE;
-- 会话缓存游标的数量(软解析用)
ALTER SYSTEM SET session_cached_cursors=100 SCOPE=SPFILE;
游标数太小,会报ORA-01000: maximum open cursors exceeded。但不要设太大------每个游标都占共享池内存。
进程和会话数
sql
-- 最大进程数
ALTER SYSTEM SET processes=500 SCOPE=SPFILE;
-- 最大会话数(通常比processes大10%~20%)
ALTER SYSTEM SET sessions=555 SCOPE=SPFILE;
政务系统并发用户多、连接池大,默认值150经常不够。
undo相关
sql
-- undo保留时间(秒)
ALTER SYSTEM SET undo_retention=900 SCOPE=SPFILE;
undo_retention=900意味着Oracle尝试保留undo数据至少15分钟。用于闪回查询(Flashback Query)和一致性读。设置太短,长查询可能遇到ORA-01555: snapshot too old。
日志相关
sql
-- 日志切换的间隔(秒)
-- 如果日志切换太频繁(几分钟一次),考虑增大日志文件大小
ALTER SYSTEM SET archive_lag_target=1800 SCOPE=SPFILE;
日志文件大小在建库时设定,不能动态改。一般建议日志切换间隔15~30分钟。
三、性能监控
等待事件------判断瓶颈在哪
sql
SELECT event, total_waits, time_waited
FROM v$system_event
WHERE wait_class != 'Idle'
ORDER BY time_waited DESC;
常见的等待事件:
| 等待事件 | 含义 | 可能的原因 |
|---|---|---|
| db file sequential read | 单块读等待 | 索引访问大量数据,可能索引选择不当 |
| db file scattered read | 多块读等待 | 全表扫描 |
| log file sync | 日志同步等待 | 提交太频繁 |
| buffer busy waits | 缓冲区忙等待 | 热块竞争 |
| enq: TX - row lock contention | 行锁等待 | 事务冲突 |
| latch: shared pool | 共享池锁存器竞争 | 硬解析太多 |
db file sequential read多 → 检查索引是否合理,是不是走了不该走的索引。
db file scattered read多 → 检查是否有不该全表扫描的查询。
log file sync多 → 检查是否有频繁提交的循环逻辑,考虑批量提交。
enq: TX - row lock contention多 → 检查是否有多个事务同时改同一行。
AWR报告------全面体检
Oracle的AWR(Automatic Workload Repository)每隔一段时间自动采集数据库状态快照。生成报告:
sql
-- 查看快照列表
SELECT snap_id, begin_interval_time, end_interval_time
FROM dba_hist_snapshot
ORDER BY snap_id DESC;
-- 生成报告(在SQL*Plus中执行)
@?/rdbms/admin/awrrpt.sql
AWR报告很长,重点看几个部分:
- Top 10 Foreground Events------数据库时间花在哪了
- SQL ordered by Elapsed Time------最慢的SQL
- SQL ordered by Gets------消耗最多逻辑读的SQL
- Segment by Physical Reads------读取最多的表/索引
- Tablespace I/O------表空间的I/O情况
找到慢SQL
sql
SELECT sql_id, sql_text, elapsed_time / 1000000 AS elapsed_sec,
executions, elapsed_time / executions / 1000000 AS avg_sec
FROM v$sql
WHERE executions > 0
ORDER BY elapsed_time DESC
FETCH FIRST 20 ROWS ONLY;
找到慢SQL后,拿sql_id查执行计划:
sql
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('sql_id_here'));
锁阻塞
用户说"操作卡住了",先查锁:
sql
SELECT sid, serial#, blocking_session, wait_class, event, seconds_in_wait
FROM v$session
WHERE blocking_session IS NOT NULL;
blocking_session字段告诉你谁在阻塞谁。找到阻塞源头:
sql
SELECT sid, serial#, sql_text
FROM v$session s
JOIN v$sql q ON s.sql_id = q.sql_id
WHERE s.sid = <blocking_session>;
必要时杀掉阻塞的会话:
sql
ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;
四、备份策略
前面两次事故都是因为没有备份才那么狼狈。恢复成功是运气,不是能力。
RMAN备份
Oracle的标准备份工具是RMAN(Recovery Manager)。
全库备份:
bash
rman target /
RMAN> BACKUP DATABASE PLUS ARCHIVELOG;
增量备份(只备份变化的数据块):
bash
RMAN> BACKUP INCREMENTAL LEVEL 0 DATABASE; -- 基础备份
RMAN> BACKUP INCREMENTAL LEVEL 1 DATABASE; -- 增量备份
建议的备份策略
每天凌晨:增量备份(LEVEL 1)
每周日凌晨:全量备份(LEVEL 0)
每小时:归档日志备份
保留策略:
bash
RMAN> CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 7 DAYS;
保留7天的恢复窗口。
验证备份能恢复
备份不做恢复测试等于没备份。
bash
RMAN> RESTORE DATABASE VALIDATE;
RMAN> RESTORE ARCHIVELOG ALL VALIDATE;
VALIDATE只验证备份文件是否完整可读,不做实际恢复。
定期在测试环境做真实的恢复演练------恢复到指定时间点:
bash
RMAN> RUN {
SET UNTIL TIME "TO_DATE('2024-01-15 14:00:00', 'YYYY-MM-DD HH24:MI:SS')";
RESTORE DATABASE;
RECOVER DATABASE;
ALTER DATABASE OPEN RESETLOGS;
}
expdp/impdp------逻辑备份
除了RMAN的物理备份,还有逻辑备份(导出数据):
bash
expdp scott/tiger DIRECTORY=dp_dir DUMPFILE=kc22_%U.dmp TABLES=kc22 PARALLEL=4
逻辑备份不能做媒体恢复(磁盘坏了不能用它恢复),但适合:
- 迁移数据
- 只恢复个别表
- 跨版本导出
五、日常运维清单
把以上内容整合成日常要做的事:
| 频率 | 做什么 | 怎么做 |
|---|---|---|
| 每天 | 检查告警日志 | adrci或直接读alert_<SID>.log |
| 每天 | 检查表空间使用率 | SELECT * FROM dba_tablespace_usage_metrics; |
| 每天 | 检查备份是否成功 | SELECT * FROM v$rman_status; |
| 每周 | 查看AWR报告 | @?/rdbms/admin/awrrpt.sql |
| 每周 | 查看慢SQL | v$sql按elapsed_time排序 |
| 每月 | 收集统计信息 | DBMS_STATS.GATHER_DATABASE_STATS |
| 每月 | 检查无效对象 | SELECT * FROM dba_objects WHERE status='INVALID'; |
| 每季度 | 恢复演练 | 测试环境做RMAN恢复 |
从救火到防火
前面四篇,前两篇是性能问题(执行计划、索引),后两篇是可靠性问题(redo损坏、undo损坏)。都是出了事才处理。
这篇讲的------参数配置、内存结构、性能监控、备份策略------是让事尽量不出。
但现实中,防火做得再好,也要准备救火的工具。因为:
- 硬盘会坏
- 内存会出错
- 人会写错SQL
- 电力会断
下一篇,讲这些原理在不同数据库里的对应------从Oracle到MySQL到PostgreSQL,底层逻辑是不是同一套。
标签:#DBA #Oracle #参数配置 #内存结构 #SGA #PGA #性能监控 #AWR #RMAN #备份策略