文章目录
正文
1. Oracle优化概述
Oracle数据库优化其实就像给汽车做保养一样,不做优化的数据库就像一辆从来不保养的车,刚开始可能还能跑,但时间长了就会各种毛病。优化不是一次性的事情,而是一个持续的过程。
1.1 为什么需要优化
- 提高查询响应时间,用户体验更好
- 减少系统资源消耗,节省成本
- 提升并发处理能力,支持更多用户
- 保证数据库稳定运行,避免宕机
1.2 Oracle优化的层次结构
Oracle优化不是单一维度的,而是一个多层次的体系:
2. SQL语句优化
2.1 执行计划分析
执行计划就像是Oracle给每个SQL语句制定的"作战计划",理解执行计划是优化的第一步。
-- 查看执行计划的几种方法
-- 方法1:使用EXPLAIN PLAN
EXPLAIN PLAN FOR
SELECT e.employee_id, e.first_name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id
WHERE e.salary > 50000;
-- 查看执行计划
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- 方法2:使用AUTOTRACE
SET AUTOTRACE ON
SELECT employee_id, first_name, salary
FROM employees
WHERE department_id = 10;
-- 方法3:获取实际执行统计信息
SELECT /*+ GATHER_PLAN_STATISTICS */
employee_id, first_name, salary
FROM employees
WHERE department_id = 10;
-- 查看实际执行统计
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST'));
2.2 常见SQL优化技巧
2.2.1 避免全表扫描
全表扫描就像在图书馆里为了找一本书把所有书都翻一遍,效率很低:
-- 不好的写法:导致全表扫描
SELECT * FROM employees
WHERE TO_CHAR(hire_date, 'YYYY') = '2023';
-- 优化后的写法:使用索引
SELECT * FROM employees
WHERE hire_date >= DATE '2023-01-01'
AND hire_date < DATE '2024-01-01';
-- 不好的写法:函数导致索引失效
SELECT * FROM employees
WHERE UPPER(last_name) = 'SMITH';
-- 优化后的写法:创建函数索引或使用大小写不敏感查询
CREATE INDEX idx_emp_lastname_upper ON employees (UPPER(last_name));
-- 或者
SELECT * FROM employees
WHERE last_name = 'Smith' OR last_name = 'SMITH' OR last_name = 'smith';
2.2.2 优化JOIN操作
JOIN操作的优化策略:
-- 1. 使用适当的JOIN顺序(小表驱动大表)
-- 不好的写法
SELECT e.employee_id, d.department_name
FROM employees e, departments d
WHERE e.department_id = d.department_id;
-- 优化后的写法
SELECT /*+ USE_NL(e d) */ e.employee_id, d.department_name
FROM departments d
JOIN employees e ON d.department_id = e.department_id;
-- 2. 使用EXISTS代替IN(当子查询返回大量数据时)
-- 不好的写法
SELECT * FROM employees
WHERE department_id IN (
SELECT department_id FROM departments
WHERE location_id = 1700
);
-- 优化后的写法
SELECT * FROM employees e
WHERE EXISTS (
SELECT 1 FROM departments d
WHERE d.department_id = e.department_id
AND d.location_id = 1700
);
2.2.3 合理使用HINT
HINT就像是给Oracle的建议,但要谨慎使用:
-- 常用的HINT示例
-- 1. 强制使用索引
SELECT /*+ INDEX(e emp_department_ix) */
employee_id, first_name
FROM employees e
WHERE department_id = 10;
-- 2. 强制使用特定的JOIN方法
SELECT /*+ USE_HASH(e d) */
e.employee_id, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;
-- 3. 并行查询
SELECT /*+ PARALLEL(employees, 4) */
department_id, COUNT(*)
FROM employees
GROUP BY department_id;
-- 4. 结果集缓存
SELECT /*+ RESULT_CACHE */
department_id, department_name
FROM departments;
2.3 分页查询优化
分页查询是Web应用中最常见的场景之一:
-- 传统的分页方法(性能较差)
SELECT * FROM (
SELECT ROWNUM rn, e.* FROM (
SELECT * FROM employees
ORDER BY employee_id
) e WHERE ROWNUM <= 20
) WHERE rn > 10;
-- Oracle 12c及以上版本的优化方法
SELECT employee_id, first_name, last_name, salary
FROM employees
ORDER BY employee_id
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;
-- 使用游标分页(适合大数据量)
SELECT employee_id, first_name, last_name, salary
FROM employees
WHERE employee_id > :last_employee_id
ORDER BY employee_id
FETCH FIRST 10 ROWS ONLY;
3. 索引优化
3.1 索引类型选择
Oracle提供了多种索引类型,就像工具箱里有不同的工具,要选对工具才能事半功倍:
3.2 B树索引优化
B树索引是最常用的索引类型:
-- 创建单列索引
CREATE INDEX idx_emp_lastname ON employees (last_name);
-- 创建复合索引(列的顺序很重要!)
CREATE INDEX idx_emp_dept_salary ON employees (department_id, salary);
-- 这个索引可以支持以下查询:
SELECT * FROM employees WHERE department_id = 10;
SELECT * FROM employees WHERE department_id = 10 AND salary > 50000;
-- 但不能很好地支持这个查询:
SELECT * FROM employees WHERE salary > 50000; -- 只用到索引的部分
-- 创建唯一索引
CREATE UNIQUE INDEX idx_emp_email ON employees (email);
-- 创建降序索引
CREATE INDEX idx_emp_salary_desc ON employees (salary DESC);
3.3 函数索引
当查询条件中包含函数时,函数索引就派上用场了:
-- 创建基于函数的索引
CREATE INDEX idx_emp_upper_lastname ON employees (UPPER(last_name));
-- 创建基于表达式的索引
CREATE INDEX idx_emp_annual_salary ON employees (salary * 12);
-- 创建基于日期函数的索引
CREATE INDEX idx_emp_hire_year ON employees (EXTRACT(YEAR FROM hire_date));
-- 这些索引可以支持相应的查询
SELECT * FROM employees WHERE UPPER(last_name) = 'SMITH';
SELECT * FROM employees WHERE salary * 12 > 600000;
SELECT * FROM employees WHERE EXTRACT(YEAR FROM hire_date) = 2023;
3.4 位图索引
位图索引适合数据仓库环境中的低基数列:
-- 创建位图索引(适合OLAP环境)
CREATE BITMAP INDEX idx_emp_gender ON employees (gender);
CREATE BITMAP INDEX idx_emp_status ON employees (status);
-- 位图索引在以下查询中表现出色
SELECT COUNT(*) FROM employees WHERE gender = 'M' AND status = 'ACTIVE';
-- 注意:位图索引不适合OLTP环境,因为DML操作会导致锁定问题
3.5 索引监控和维护
-- 监控索引使用情况
ALTER INDEX idx_emp_lastname MONITORING USAGE;
-- 查看索引使用统计
SELECT * FROM V$OBJECT_USAGE WHERE INDEX_NAME = 'IDX_EMP_LASTNAME';
-- 分析索引效率
ANALYZE INDEX idx_emp_lastname VALIDATE STRUCTURE;
-- 查看索引统计信息
SELECT index_name, blevel, leaf_blocks, distinct_keys, clustering_factor
FROM user_indexes
WHERE index_name = 'IDX_EMP_LASTNAME';
-- 重建索引(当索引碎片严重时)
ALTER INDEX idx_emp_lastname REBUILD;
-- 重建索引并压缩
ALTER INDEX idx_emp_lastname REBUILD COMPRESS;
4. 内存优化
4.1 SGA(系统全局区)优化
SGA就像是Oracle的"大脑",包含了共享的内存结构:
-- 查看当前SGA配置
SELECT component, current_size/1024/1024 as size_mb
FROM v$sga_dynamic_components
ORDER BY current_size DESC;
-- 设置SGA相关参数
ALTER SYSTEM SET sga_target = 2G SCOPE=BOTH;
ALTER SYSTEM SET shared_pool_size = 512M SCOPE=BOTH;
ALTER SYSTEM SET db_cache_size = 1G SCOPE=BOTH;
-- 查看缓冲区命中率
SELECT name, physical_reads, db_block_gets, consistent_gets,
1 - (physical_reads / (db_block_gets + consistent_gets)) as hit_ratio
FROM v$buffer_pool_statistics;
-- 查看共享池命中率
SELECT namespace, gets, gethits,
round(gethits/gets*100, 2) as hit_ratio
FROM v$librarycache;
4.2 PGA(程序全局区)优化
PGA是每个服务器进程的私有内存区域:
-- 设置PGA相关参数
ALTER SYSTEM SET pga_aggregate_target = 1G SCOPE=BOTH;
ALTER SYSTEM SET workarea_size_policy = AUTO SCOPE=BOTH;
-- 查看PGA使用情况
SELECT name, value/1024/1024 as value_mb
FROM v$pgastat
WHERE name IN ('total PGA allocated', 'total PGA used for auto workareas');
-- 查看SQL工作区使用情况
SELECT sql_id, operation_type, policy, estimated_optimal_size/1024 as optimal_kb,
estimated_onepass_size/1024 as onepass_kb, actual_mem_used/1024 as used_kb
FROM v$sql_workarea_active;
-- 优化排序操作
ALTER SESSION SET sort_area_size = 1048576; -- 1MB
ALTER SESSION SET hash_area_size = 1048576; -- 1MB
5. I/O优化
5.1 表空间和数据文件优化
I/O优化就像是优化交通流量,减少拥堵,提高通行效率:
-- 创建优化的表空间
CREATE TABLESPACE sales_data
DATAFILE '/u01/oradata/prod/sales_data01.dbf' SIZE 1G AUTOEXTEND ON NEXT 100M,
'/u02/oradata/prod/sales_data02.dbf' SIZE 1G AUTOEXTEND ON NEXT 100M,
'/u03/oradata/prod/sales_data03.dbf' SIZE 1G AUTOEXTEND ON NEXT 100M
EXTENT MANAGEMENT LOCAL
SEGMENT SPACE MANAGEMENT AUTO
BLOCKSIZE 8K;
-- 创建临时表空间
CREATE TEMPORARY TABLESPACE temp_large
TEMPFILE '/u04/oradata/prod/temp_large01.tmp' SIZE 2G AUTOEXTEND ON NEXT 100M
EXTENT MANAGEMENT LOCAL;
-- 查看I/O统计信息
SELECT file_name, phyrds, phywrts, readtim, writetim
FROM v$filestat f, dba_data_files d
WHERE f.file# = d.file_id
ORDER BY phyrds + phywrts DESC;
5.2 ASM(自动存储管理)优化
ASM是Oracle提供的存储管理解决方案:
-- 查看ASM磁盘组信息
SELECT name, total_mb, free_mb,
round((total_mb - free_mb) / total_mb * 100, 2) as used_percent
FROM v$asm_diskgroup;
-- 创建ASM磁盘组
CREATE DISKGROUP DATA_DG NORMAL REDUNDANCY
DISK '/dev/raw/raw1',
'/dev/raw/raw2',
'/dev/raw/raw3',
'/dev/raw/raw4';
-- 添加磁盘到磁盘组
ALTER DISKGROUP DATA_DG ADD DISK '/dev/raw/raw5';
-- 重平衡磁盘组
ALTER DISKGROUP DATA_DG REBALANCE POWER 5;
5.3 分区表优化
分区就像是把大仓库分成若干个小仓库,便于管理:
-- 范围分区
CREATE TABLE sales_data (
sale_id NUMBER,
sale_date DATE,
product_id NUMBER,
amount NUMBER
)
PARTITION BY RANGE (sale_date) (
PARTITION p2023q1 VALUES LESS THAN (DATE '2023-04-01'),
PARTITION p2023q2 VALUES LESS THAN (DATE '2023-07-01'),
PARTITION p2023q3 VALUES LESS THAN (DATE '2023-10-01'),
PARTITION p2023q4 VALUES LESS THAN (DATE '2024-01-01')
);
-- 哈希分区
CREATE TABLE customer_data (
customer_id NUMBER,
customer_name VARCHAR2(100),
region VARCHAR2(50)
)
PARTITION BY HASH (customer_id) PARTITIONS 8;
-- 列表分区
CREATE TABLE regional_sales (
sale_id NUMBER,
region VARCHAR2(20),
amount NUMBER
)
PARTITION BY LIST (region) (
PARTITION north VALUES ('NORTH', 'NORTHEAST'),
PARTITION south VALUES ('SOUTH', 'SOUTHEAST'),
PARTITION west VALUES ('WEST', 'NORTHWEST'),
PARTITION east VALUES ('EAST')
);
-- 复合分区
CREATE TABLE order_details (
order_id NUMBER,
order_date DATE,
region VARCHAR2(20),
amount NUMBER
)
PARTITION BY RANGE (order_date)
SUBPARTITION BY LIST (region) (
PARTITION p2023 VALUES LESS THAN (DATE '2024-01-01') (
SUBPARTITION p2023_north VALUES ('NORTH'),
SUBPARTITION p2023_south VALUES ('SOUTH'),
SUBPARTITION p2023_east VALUES ('EAST'),
SUBPARTITION p2023_west VALUES ('WEST')
),
PARTITION p2024 VALUES LESS THAN (DATE '2025-01-01') (
SUBPARTITION p2024_north VALUES ('NORTH'),
SUBPARTITION p2024_south VALUES ('SOUTH'),
SUBPARTITION p2024_east VALUES ('EAST'),
SUBPARTITION p2024_west VALUES ('WEST')
)
);
6. 统计信息优化
6.1 表和索引统计信息
统计信息就像是Oracle的"GPS",帮助优化器选择最佳路径:
-- 收集表的统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS('HR', 'EMPLOYEES');
-- 收集表和索引的统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS(
ownname => 'HR',
tabname => 'EMPLOYEES',
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
method_opt => 'FOR ALL COLUMNS SIZE AUTO',
cascade => TRUE
);
-- 收集模式级统计信息
EXEC DBMS_STATS.GATHER_SCHEMA_STATS(
ownname => 'HR',
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
method_opt => 'FOR ALL COLUMNS SIZE AUTO',
cascade => TRUE
);
-- 查看表的统计信息
SELECT table_name, num_rows, blocks, avg_row_len, last_analyzed
FROM user_tables
WHERE table_name = 'EMPLOYEES';
-- 查看列的统计信息
SELECT column_name, num_distinct, low_value, high_value, density, histogram
FROM user_tab_columns
WHERE table_name = 'EMPLOYEES';
6.2 直方图统计信息
直方图帮助Oracle更好地理解数据分布:
-- 创建直方图
EXEC DBMS_STATS.GATHER_TABLE_STATS(
ownname => 'HR',
tabname => 'EMPLOYEES',
method_opt => 'FOR COLUMNS SIZE 254 department_id, salary'
);
-- 查看直方图信息
SELECT column_name, histogram, num_buckets
FROM user_tab_columns
WHERE table_name = 'EMPLOYEES'
AND histogram != 'NONE';
-- 查看直方图桶信息
SELECT column_name, bucket_number, endpoint_value, endpoint_actual_value
FROM user_histograms
WHERE table_name = 'EMPLOYEES'
ORDER BY column_name, bucket_number;
7. 并发优化
7.1 锁机制优化
Oracle的锁机制保证数据一致性,但也可能导致性能问题:
-- 查看当前锁等待情况
SELECT s1.username || '@' || s1.machine || ' (SID=' || s1.sid || ')' as blocker,
s2.username || '@' || s2.machine || ' (SID=' || s2.sid || ')' as waiter,
lo.oracle_username, lo.os_user_name, ao.object_name, lo.locked_mode
FROM v$locked_object lo, all_objects ao, v$session s1, v$session s2
WHERE ao.object_id = lo.object_id
AND lo.session_id = s1.sid
AND s2.blocking_session = s1.sid;
-- 查看锁等待事件
SELECT event, total_waits, time_waited, average_wait
FROM v$session_event
WHERE event LIKE '%lock%'
ORDER BY time_waited DESC;
-- 优化ITL(感兴趣事务列表)
CREATE TABLE high_concurrent_table (
id NUMBER,
data VARCHAR2(100)
) INITRANS 10 MAXTRANS 255;
7.2 并行处理优化
并行处理就像是多个人一起干活,可以大大提高效率:
-- 设置表的并行度
ALTER TABLE large_table PARALLEL 4;
-- 并行查询
SELECT /*+ PARALLEL(employees, 4) */
department_id, COUNT(*)
FROM employees
GROUP BY department_id;
-- 并行DML操作
ALTER SESSION ENABLE PARALLEL DML;
INSERT /*+ PARALLEL(target_table, 4) */ INTO target_table
SELECT /*+ PARALLEL(source_table, 4) */ *
FROM source_table;
-- 并行索引创建
CREATE INDEX idx_large_table_id ON large_table (id) PARALLEL 4;
-- 创建完成后改回串行
ALTER INDEX idx_large_table_id NOPARALLEL;
-- 查看并行执行统计
SELECT px_servers_requested, px_servers_allocated,
px_server_set, px_qcsid
FROM v$px_session;
8. 监控和诊断
8.1 AWR(自动工作负载仓库)
AWR就像是数据库的"体检报告",定期收集性能数据:
-- 生成AWR报告
@$ORACLE_HOME/rdbms/admin/awrrpt.sql
-- 查看AWR快照
SELECT snap_id, begin_interval_time, end_interval_time
FROM dba_hist_snapshot
ORDER BY snap_id DESC;
-- 手动创建AWR快照
EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT();
-- 设置AWR收集间隔和保留期
EXEC DBMS_WORKLOAD_REPOSITORY.MODIFY_SNAPSHOT_SETTINGS(
interval => 30, -- 30分钟
retention => 7*24*60 -- 保留7天
);
8.2 SQL调优顾问
SQL调优顾问是Oracle内置的智能优化工具:
-- 创建SQL调优任务
DECLARE
task_name VARCHAR2(30);
BEGIN
task_name := DBMS_SQLTUNE.CREATE_TUNING_TASK(
sql_text => 'SELECT * FROM employees WHERE department_id = 10',
user_name => 'HR',
task_name => 'tune_emp_query'
);
END;
/
-- 执行调优任务
EXEC DBMS_SQLTUNE.EXECUTE_TUNING_TASK('tune_emp_query');
-- 查看调优建议
SELECT DBMS_SQLTUNE.REPORT_TUNING_TASK('tune_emp_query') FROM DUAL;
-- 实施调优建议
EXEC DBMS_SQLTUNE.IMPLEMENT_TUNING_TASK('tune_emp_query');
8.3 实时监控
-- 查看当前活动会话
SELECT sid, serial#, username, program, machine,
sql_id, event, state, wait_time
FROM v$session
WHERE status = 'ACTIVE'
AND username IS NOT NULL;
-- 查看长时间运行的SQL
SELECT sql_id, elapsed_time/1000000 as elapsed_seconds,
cpu_time/1000000 as cpu_seconds,
executions, sql_text
FROM v$sql
WHERE elapsed_time > 60000000 -- 超过60秒
ORDER BY elapsed_time DESC;
-- 查看等待事件
SELECT event, total_waits, total_timeouts, time_waited,
average_wait, time_waited_micro
FROM v$system_event
WHERE event NOT LIKE 'SQL*Net%'
AND event NOT LIKE '%timer%'
ORDER BY time_waited DESC;
9. 实际应用案例
9.1 电商系统优化案例
假设我们有一个电商系统,面临性能问题:
-- 问题:订单查询很慢
-- 原始查询(性能差)
SELECT o.order_id, o.order_date, c.customer_name,
SUM(oi.quantity * oi.unit_price) as total_amount
FROM orders o, customers c, order_items oi
WHERE o.customer_id = c.customer_id
AND o.order_id = oi.order_id
AND o.order_date BETWEEN DATE '2023-01-01' AND DATE '2023-12-31'
GROUP BY o.order_id, o.order_date, c.customer_name;
-- 优化方案1:改写SQL
SELECT /*+ USE_HASH(o c) USE_HASH(o oi) */
o.order_id, o.order_date, c.customer_name,
SUM(oi.quantity * oi.unit_price) as total_amount
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
WHERE o.order_date >= DATE '2023-01-01'
AND o.order_date < DATE '2024-01-01'
GROUP BY o.order_id, o.order_date, c.customer_name;
-- 优化方案2:创建合适的索引
CREATE INDEX idx_orders_date_customer ON orders (order_date, customer_id);
CREATE INDEX idx_order_items_order_id ON order_items (order_id);
-- 优化方案3:分区表
CREATE TABLE orders_partitioned (
order_id NUMBER,
customer_id NUMBER,
order_date DATE,
status VARCHAR2(20)
)
PARTITION BY RANGE (order_date) (
PARTITION p2023q1 VALUES LESS THAN (DATE '2023-04-01'),
PARTITION p2023q2 VALUES LESS THAN (DATE '2023-07-01'),
PARTITION p2023q3 VALUES LESS THAN (DATE '2023-10-01'),
PARTITION p2023q4 VALUES LESS THAN (DATE '2024-01-01'),
PARTITION p2024q1 VALUES LESS THAN (DATE '2024-04-01')
);
-- 优化方案4:物化视图
CREATE MATERIALIZED VIEW mv_order_summary
BUILD IMMEDIATE
REFRESH FAST ON COMMIT
AS
SELECT o.order_date, o.customer_id, c.customer_name,
COUNT(*) as order_count,
SUM(oi.quantity * oi.unit_price) as total_amount
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
GROUP BY o.order_date, o.customer_id, c.customer_name;
9.2 数据仓库ETL优化案例
数据仓库的ETL过程通常涉及大量数据处理:
-- 问题:ETL过程太慢
-- 优化前的插入语句
INSERT INTO fact_sales
SELECT p.product_key, t.time_key, c.customer_key, s.store_key,
s.quantity, s.amount, s.discount
FROM staging_sales s, dim_product p, dim_time t,
dim_customer c, dim_store st
WHERE s.product_id = p.product_id
AND s.sale_date = t.date_value
AND s.customer_id = c.customer_id
AND s.store_id = st.store_id;
-- 优化方案1:使用APPEND和NOLOGGING
INSERT /*+ APPEND */ INTO fact_sales NOLOGGING
SELECT /*+ PARALLEL(s,4) PARALLEL(p,4) */
p.product_key, t.time_key, c.customer_key, st.store_key,
s.quantity, s.amount, s.discount
FROM staging_sales s
JOIN dim_product p ON s.product_id = p.product_id
JOIN dim_time t ON s.sale_date = t.date_value
JOIN dim_customer c ON s.customer_id = c.customer_id
JOIN dim_store st ON s.store_id = st.store_id;
-- 优化方案2:分批处理
DECLARE
CURSOR c_batches IS
SELECT DISTINCT batch_id FROM staging_sales;
BEGIN
FOR batch IN c_batches LOOP
INSERT /*+ APPEND */ INTO fact_sales
SELECT p.product_key, t.time_key, c.customer_key, st.store_key,
s.quantity, s.amount, s.discount
FROM staging_sales s
JOIN dim_product p ON s.product_id = p.product_id
JOIN dim_time t ON s.sale_date = t.date_value
JOIN dim_customer c ON s.customer_id = c.customer_id
JOIN dim_store st ON s.store_id = st.store_id
WHERE s.batch_id = batch.batch_id;
COMMIT;
END LOOP;
END;
/
-- 优化方案3:使用外部表
CREATE OR REPLACE DIRECTORY ext_dir AS '/data/staging';
CREATE TABLE ext_sales (
product_id NUMBER,
customer_id NUMBER,
store_id NUMBER,
sale_date DATE,
quantity NUMBER,
amount NUMBER,
discount NUMBER
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY ext_dir
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
FIELDS TERMINATED BY ','
)
LOCATION ('sales_data.csv')
);
10. 优化最佳实践
10.1 性能优化流程
Oracle性能优化应该遵循系统性的流程:
10.2 优化检查清单
性能优化的检查清单:
优化层面 | 检查项目 | 优化建议 |
---|---|---|
硬件 | CPU利用率 | 确保CPU利用率在70%以下 |
硬件 | 内存使用率 | 避免操作系统层面的内存交换 |
硬件 | I/O性能 | 使用SSD或高速存储设备 |
数据库 | SGA命中率 | 缓冲区命中率应在95%以上 |
数据库 | 等待事件 | 关注前5个等待事件 |
应用 | SQL效率 | 避免全表扫描和笛卡尔积 |
应用 | 索引使用 | 为WHERE条件创建合适索引 |
应用 | 连接管理 | 使用连接池减少连接开销 |
10.3 常见性能陷阱
这些是Oracle性能优化中经常遇到的陷阱:
-- 陷阱1:隐式类型转换
-- 错误写法
SELECT * FROM employees WHERE employee_id = '100'; -- 字符串比较数字
-- 正确写法
SELECT * FROM employees WHERE employee_id = 100;
-- 陷阱2:NOT EXISTS vs NOT IN
-- 当子查询可能返回NULL时,NOT IN的行为可能不符合预期
-- 较危险的写法
SELECT * FROM employees
WHERE department_id NOT IN (SELECT department_id FROM departments WHERE manager_id IS NULL);
-- 更安全的写法
SELECT * FROM employees e
WHERE NOT EXISTS (SELECT 1 FROM departments d
WHERE d.department_id = e.department_id
AND d.manager_id IS NULL);
-- 陷阱3:OR条件导致索引失效
-- 低效的写法
SELECT * FROM employees WHERE department_id = 10 OR salary > 50000;
-- 优化写法(使用UNION)
SELECT * FROM employees WHERE department_id = 10
UNION
SELECT * FROM employees WHERE salary > 50000 AND department_id != 10;
-- 陷阱4:函数导致索引失效
-- 错误写法
SELECT * FROM employees WHERE TRUNC(hire_date) = DATE '2023-01-01';
-- 正确写法
SELECT * FROM employees
WHERE hire_date >= DATE '2023-01-01'
AND hire_date < DATE '2023-01-02';
10.4 监控脚本
建立定期监控脚本,及时发现性能问题:
-- 创建性能监控脚本
CREATE OR REPLACE PROCEDURE monitor_performance AS
BEGIN
-- 检查缓冲区命中率
INSERT INTO perf_monitor_log
SELECT SYSDATE, 'BUFFER_HIT_RATIO',
ROUND((1 - (phy.value / (bg.value + cg.value))) * 100, 2)
FROM v$sysstat phy, v$sysstat bg, v$sysstat cg
WHERE phy.name = 'physical reads'
AND bg.name = 'db block gets'
AND cg.name = 'consistent gets';
-- 检查长时间运行的SQL
INSERT INTO perf_monitor_log
SELECT SYSDATE, 'LONG_RUNNING_SQL', COUNT(*)
FROM v$session s, v$sql sq
WHERE s.sql_id = sq.sql_id
AND s.status = 'ACTIVE'
AND sq.elapsed_time > 300000000; -- 5分钟
-- 检查锁等待
INSERT INTO perf_monitor_log
SELECT SYSDATE, 'LOCK_WAITS', COUNT(*)
FROM v$session
WHERE blocking_session IS NOT NULL;
COMMIT;
END;
/
-- 创建监控任务
BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'PERF_MONITOR_JOB',
job_type => 'STORED_PROCEDURE',
job_action => 'monitor_performance',
start_date => SYSTIMESTAMP,
repeat_interval => 'FREQ=MINUTELY;INTERVAL=5',
enabled => TRUE
);
END;
/
总的来说,Oracle数据库优化是一个系统工程,需要从多个维度综合考虑。关键是要建立完善的监控体系,及时发现问题,然后有针对性地进行优化。记住,优化不是一次性的工作,而是一个持续改进的过程。最重要的是要理解业务需求,在性能和资源消耗之间找到最佳平衡点。
结语
感谢您的阅读!期待您的一键三连!欢迎指正!