3.7 迁移前后如何保证性能
每次硬件变更或软件升级,涉及最多的词就是“性能”,升级迁移前后如何保证性能的稳定性,甚至稳中有升,是每个DBA被问及最多的话题。在测试环境中,开发人员通常已进行了广泛的测试,以验证更改所带来的影响。尽管在测试环境中已经进行了充分的测试,但是新系统投入生产后还是会经常遇到意想不到的问题。这主要还是因为测试不是基于实际的工作负载执行的,所以开发人员在验证系统变更时,无法模拟真实的工作负载。那么如何在测试环境中模拟真实的生产压力,就是我们亟需解决的问题了,同时也是目前DBA所面临的最大挑战之一。DBA需要使用有效的方式分析更改对数据库整体性能的影响,并及时采取措施。
3.7.1 数据库重放
Oracle 10.2.0.4及以上版本提供了数据库重放(Database Replay)工具,可用于帮助我们捕获生产系统上的工作负载,在测试系统上重放,模拟真实的工作环境,并提供分析报告,借此我们能够全面评估环境变更所带来的影响,从而发现潜在问题。数据库重放工具可以在数据库级别捕获外部客户端的工作负载,以前开发人员使用负载模拟工具,需要花费数月的时间才能完成的模拟测试,现在几天内就可以完成,这大大地降低了测试成本。
图3-9所示为数据库重放的主要工作流程,具体如下。
图3-9 数据库重放工作流程图
1)在生产系统上将工作负载捕获到捕获文件中。
2)将捕获文件复制到测试系统并进行预处理。
3)在测试系统上重放生产系统的工作负载。
4)获取重放分析报告。
数据库重放主要适用于以下场景。
1)数据库或操作系统升级。
2)PDB级别整合或用户层面整合。
3)配置更改,例如,从单机转换为RAC环境。
4)存储、网络更改。
5)硬件环境迁移。
数据库重放的具体实现原理不在本节的讨论范围之内,感兴趣的读者可以查看Oracle 19c官方文档《Testing Guide》,本节将以实际案例为载体讲解晦涩的技术原理,为大家提供参考借鉴。
1.负载捕获
(1)还原测试环境
数据库重放的第一步是还原测试环境,使测试环境尽可能与生产保持一致,具体可以使用以下几种方式,建议使用其中的快照备库(Snapshot standby)方式。
·RMAN DUPLICATE。
·Snapshot standby。
·Data Pump Import and Export(数据泵导入和导出)。
如果生产环境使用了Database Vault,则需要拥有DBMS_WORKLOAD_CAPTURE和DBMS_WORKLOAD_REPLAY的授权。
1)测试环境恢复时间点应与生产捕获开始的时间点尽可能地接近,从而最大程度地降低差异。
2)为了不影响当前生产系统,我们需要对测试环境中的如下对象进行处理,包括Database links(数据库链接)、External tables(外部表)、Directory objects(目录对象)、URLs、E-mails,强烈建议测试环境使用隔离专用网络。
(2)创建捕获目录
在生产端单独创建存放目录,确保目录下无任何文件,保证权限正确,合理评估大小(可以模拟几分钟时间的运行测试捕获,按比例推断出完全捕获所需要的空间大小)。
shell> mkdir -p /home/oracle/capdir shell> create or replace directory capdir as '/home/oracle/capdir';
对于RAC环境,建议使用共享文件系统。也可以在每个实例节点上使用单独的目录,但最终需要合并各个实例下生成的文件,并传输至测试环境。
(3)创建过滤器(可选)
默认情况下,捕获文件中会记录所有的用户会话。我们也可以使用过滤器有选择地记录,过滤器包含Inclusion(包含)和Exclusion(排除)两种。
·Inclusion:包含需要记录的内容。
·Exclusion:排除不需要记录的内容。
可以使用ADD_FILTER存储过程添加过滤器,命令如下:
SQL> BEGIN DBMS_WORKLOAD_CAPTURE.ADD_FILTER ( fname => 'filter_user1', fattribute => 'USER', fvalue => 'JASON'); END; /
以上示例代码中,使用ADD_FILTER添加了一个名为filter_user1的过滤器,用于过滤用户名为JASON的所有会话。其参数说明如下。
·fname:添加过滤器名称。
·fattribute:需要过滤的内容,分为PROGRAM、MODULE、ACTION、SERVICE、INSTANCE_NUMBER和USER。
·fvalue:指定需要具体过滤的值。
指定PROGRAM过滤,命令如下:
SQL> BEGIN DBMS_WORKLOAD_CAPTURE.ADD_FILTER ( fname => ' filter_prog', fattribute => 'PROGRAM', fvalue => '%OSM%'); END; /
检查创建的过滤信息,命令如下:
SQL> select * from dba_workload_filters; TYPE ID STATUS NAME ATTRIBUTE VALUE ---------- ---- ------ ------------- --------- -------- CAPTURE NEW FILTER_USER1 USER JASON
代码中,dba_workload_filters视图可用于查询过滤器信息,同样,我们也可以使用DELETE_FILTER存储过程删除过滤器,命令如下:
SQL> BEGIN DBMS_WORKLOAD_CAPTURE.DELETE_FILTER (fname => 'filter_user1'); END; /
(4)运行捕获程序
如果在数据库运行的情况下开启捕获程序,那么之前可能存在部分正在执行的尚未提交的事务,这部分事务就是无法完全捕获的,所以在条件允许的情况下,建议重启数据库进行捕获,即使无法重启,我们也可以以业务周期为单位进行捕获。
此外,为了使测试结果更为准确,在测试环境下建议使用物理恢复的方式,尽可能使捕获负载起点与测试还原点接近。
由于工作负载捕获的限制,部分捕获内容无法在测试环境中进行重放,这种情况就可以使用SQL*Loader从外部文件进行数据加载、闪回查询、非SQL对象访问等操作。
1)开启捕获程序,命令如下:
SQL> BEGIN DBMS_WORKLOAD_CAPTURE.START_CAPTURE ( name => 'pri_capture_1', dir => 'CAPDIR', default_action => 'EXCLUDE', duration => NULL); END; /
在此示例中,捕获程序名为pri_capture_1,duration为null表示未指定时长,需要手动执行停止,并将其转储在CAPDIR的数据库目录中。
default_action为EXCLUDE,表示仅捕获过滤器过滤掉的内容,这里是指捕获Jason用户的所有信息。default_action为INCLUDE,表示捕获过滤器包含的内容,也就是除去Jason用户之外的所有用户信息。
代码中的参数说明如下。
·name:标识符名称。
·dir:目录。
·duration:以秒为单位,指定需要执行捕获的时长,如未指定特定值,则需要手动调用FINISH_CAPTURE停止捕获。
·default_action:过滤器默认设置为INCLUDE,如果需要包含过滤器内容,则需要将该值设置为EXCLUDE。
2)查看捕获状态,命令如下:
SQL> select id capture_id, name, directory, status, user_calls, transactions, AWR_BEGIN_SNAP, AWR_END_SNAP from dba_workload_captures; capture_id NAME DIRECTORY STATUS USER_CALLS TRANSACTIONS AWR_BEGIN_SNAP AWR_END_SNAP ---------- ------------- --------- ---------- ---------- ------------ -------------- ------------ 16 pri_capture_1 CAPDIR INPROGRESS 0 0 3215
模拟客户端连接执行数据库操作,由于生产环境会产生实时业务数据,因此这里不需要创建测试数据。
3)停止捕获,命令如下:
SQL> BEGIN DBMS_WORKLOAD_CAPTURE.finish_capture; END; /
数据库alert后台告警日志会有开启和关闭的提示,具体如下:
DBMS_WORKLOAD_CAPTURE.START_CAPTURE(): Starting database capture at 07/03/2020 Fri Jul 03 15:55:38 2020 DBMS_WORKLOAD_CAPTURE.FINISH_CAPTURE(): Stopped database capture (not all sessions could flush their capture buffers) at 07/03/2020 15:55:37
默认情况下,Oracle 10g R2版本中未启用工作负载捕获功能。可以通过初始化参数PRE_11G_ENABLE_CAPTURE来启用或禁用此功能,而从Oracle 11g R1及以上版本开始,此功能默认开启。在Oracle 10g R2中,工作负载捕获功能的开启和关闭方法分别如下。
开启命令如下:
SQL> @$ORACLE_HOME/rdbms/admin/wrrenbl.sql
关闭命令如下:
SQL> @$ORACLE_HOME/rdbms/admin/wrrdsbl.sql
在capdir目录下生成capfiles和cap这两个捕获文件夹,命令如下:
shell> ls -l drwxr-xr-x 3 oracle asmadmin 4096 Jul 2 08:27 capfiles drwxr-xr-x 2 oracle asmadmin 4096 Jul 2 08:47 cap
(5)获取捕获期间的快照点和其他信息
获取捕获期间的快照点和其他信息,命令如下:
SQL> select id capture_id name, directory, status, user_calls, transactions, AWR_BEGIN_SNAP, AWR_END_SNAP from dba_workload_captures; capture_id NAME DIRECTORY STATUS USER_CALLS TRANSACTIONS AWR_BEGIN_SNAP AWR_END_SNAP ---------- ------------ --------- --------- ---------- ------------ -------------- ------------ 16 pri_capture_1 CAPDIR COMPLETED 759 10 3215 3216
视图记录了捕获期间的会话信息,并自动创建了快照点。
(6)导出捕获期间AWR报告(可选)
获取运行期间的AWR数据,以对比重放前后AWR的整体性能,这些数据具有非常大的参考价值。导出捕获期间AWR报告的命令如下:
SQL> exec dbms_workload_capture.export_awr (capture_id => 16);
数据库后台日志导出的信息提示如下:
Fri Jul 03 15:58:09 2020 DM00 started with pid=52, OS id=7005, job SYS.SYS_EXPORT_TABLE_01 Fri Jul 03 15:58:17 2020 DW00 started with pid=53, OS id=7180, wid=1, job SYS.SYS_EXPORT_TABLE_01
也可以通过@?/rdbms/admin/awrrpt.sql获取执行期间的AWR快照。
(7)导出捕获期间的详细信息(可选)
导出捕获期间的详细信息,命令如下:
SQL> set pagesize 0 long 30000000 longchunksize 2000 line 1000 SQL> spool pri_capture.html SQL> select dbms_workload_capture.report (CAPTURE_ID=>16, FORMAT=>'HTML') from dual; SQL> spool off
大家也可以将导出的详细信息自定义为文本(TEXT)格式,只需要把HTML改成TEXT即可。
详细信息中记录了会话、TOP SQL、等待事件等信息,如图3-10所示。
图3-10 捕获期间详细信息报告
与数据库记录捕获相关的视图有:DBA_WORKLOAD_CAPTURES和DBA_WORKLOAD_FILTERS。
2.初始化重放
完成了生产端的工作负载捕获及相同版本测试环境的搭建之后,接下来就是将捕获文件传输至目标端,对数据进行预处理操作,以创建必要的元数据,为下一步的重放操作做准备。
1)传输捕获文件。在目标端创建目录,以用于存放捕获文件,命令如下:
shell> mkdir -p /home/ora19c/replay SQL> CREATE OR REPLACE DIRECTORY db_replay_capture_dir AS '/home/ora19c/replay';
生产端传输文件至目标端,命令如下:
<!--节点1执行--> shell> scp -r /home/oracle/capfile/* oracle@168.68.19.138:/home/oracle/replay/ <!--节点2执行--> shell> scp -r /home/oracle/capfile/inst* oracle@168.68.19.138: /home/oracle/replay/capfile/
在RAC环境、非共享文件系统的情况下,需要将每个实例节点上生成的文件合并传输至测试环境,且要保持目录结构的一致性,节点2仅需要传输对应的实例文件夹即可。
2)创建过滤器(可选)。默认情况下,初始化所有用户会话。我们也可以使用过滤器选择,用法与负载捕获中的用法相同,故在此不做展开。
3)加载捕获日志。使用Oracle自带的DBMS_WORKLOAD_REPLAY包进行加载,指定加载目录为DB_REPLAY_CAPTURE_DIR,命令如下:
SQL> exec DBMS_WORKLOAD_REPLAY.PROCESS_CAPTURE ('DB_REPLAY_CAPTURE_DIR');
加载完成后,生成一个pp19.3.0.0.0的文件夹,用于记录数据库连接信息和执行数据等,命令如下:
shell> ls -tlr total 4 drwxr-xr-x 2 ora19c oinstall 110 Jul 1 20:54 cap drwxr-xr-x 3 ora19c oinstall 19 Jul 1 20:54 capfiles drwxr-xr-x 3 ora19c oinstall 4096 Jul 1 20:54 pp19.3.0.0.0
3.执行重放
强烈建议重放目标环境使用隔离的专用网络,以免DBLINK等类似操作影响当前生产系统。
(1)重放客户端准备阶段
重放客户端主要用于模拟将客户端连接到测试环境并发起业务请求,客户端至少需要安装Oracle client软件,在条件允许的情况下,建议使用单独的客户端,并将目标端重放目录下的所有文件发送至客户端。
(2)评估重放客户端数量
每个客户端均可模拟出多个会话,使用wrc工具,根据捕捉到的工作负载,评估出需要发起重放的客户端模拟会话的数量。示例代码如下:
shell> wrc MODE=calibrate REPLAYDIR=/home/ora19c/replay Workload Replay Client: Release 12.2.0.1.0 - Production on Thu Jul 2 10:01:46 2020 Copyright (c) 1982, 2017, Oracle and/or its affiliates. All rights reserved. Report for Workload in: /oracle/replay ----------------------- Recommendation: Consider using at least 5 clients divided among 2 CPU(s) You will need at least 285 MB of memory per client process. If your machine(s) cannot match that number, consider using more clients. Workload Characteristics: - max concurrency: 377 sessions - total number of sessions: 1205 Assumptions: - 1 client process per 100 concurrent sessions - 4 client processes per CPU - 256 KB of memory cache per concurrent session - think time scale = 100 - connect time scale = 100 - synchronization = TRUE
以上示例输出中,建议使用5个重放客户端。
(3)初始化重放数据
使用自带的INITIALIZE_REPLAY包将必要的元数据加载到所需的表中,命令如下:
SQL> BEGIN DBMS_WORKLOAD_REPLAY.INITIALIZE_REPLAY (replay_name => 'replay', replay_dir => 'DB_REPLAY_CAPTURE_DIR'); END; / SQL> select dbid, id, name, CAPTURE_ID, STATUS, AWR_BEGIN_SNAP, AWR_END_SNAP from dba_workload_replays; DBID ID NAME CAPTURE_ID STATUS AWR_BEGIN_SNAP AWR_END_SNAP ---------- -- ----- ---------- ------------- --------------------------- 1589686526 1 replay 1 INITIALIZED
INITIALIZE_REPLAY过程用于将预处理负载数据从DB_REPLAY_CAPTURE_DIR目录加载到数据库中。其中所包含的参数说明具体如下。
·replay_name:标识符自定义名称。
·replay_dir:负载文件目录。
(4)重定向连接串和用户
初始化重放数据后,由于捕获文件中记录的客户端连接信息还是指向原生产端,因此需要重定向客户端连接串,以便会话可以连接到目标数据库并执行重放操作。这里使用DBA_WORKLOAD_CONNECTION_MAP视图查询需要重定向的连接信息,命令如下:
SQL> select conn_id, CAPTURE_CONN, REPLAY_CONN from dba_workload_connection_map; SQL> begin dbms_workload_replay.remap_connection(connection_id => 1, replay_connection => '192.168.238.131/ljw'); end; /
connection_id为DBA_WORKLOAD_CONNECTION_MAP视图中查询到的CONN_ID,replay_connection为目标端连接信息。
同理,如果目标端不存在与生产端相同的重放用户,那么我们就需要对用户进行重定向操作了。DBA_WORKLOAD_USER_MAP视图记录了需要重定向的用户,命令如下:
SQL> select CAPTURE_USER,REPLAY_USER from DBA_WORKLOAD_USER_MAP; SQL> BEGIN DBMS_WORKLOAD_REPLAY.SET_USER_MAPPING (capture_user => 'PROD', replay_user => 'JASON'); END; /
重定向完成后,将目标端INITIALIZED模式改为PREPARE REPLAY,命令如下:
SQL> BEGIN DBMS_WORKLOAD_REPLAY.PREPARE_REPLAY (synchronization => true); END; / SQL> select dbid, id, name, CAPTURE_ID, STATUS, AWR_BEGIN_SNAP, AWR_END_SNAP from dba_workload_replays; DBID ID NAME CAPTURE_ID STATUS AWR_BEGIN_SNAP AWR_END_SNAP ---------- -- ------ ---------- ------- -------------- ------------ 1589686526 1 replay 1 PREPARE
(5)启动重放客户端
使用wrc工具启动重放客户端,每个重放客户端将会启动与数据库的一个或多个会话,以驱动工作负载重放。
如果客户端为独立的服务器,则需要将目标端重放目录下的所有文件发送至客户端,并根据评估的数量启动相应数量的客户端。
重放客户端的启动命令如下:
shell> scp -r /home/oracle/replay/* oracle@192.168.239.236:/home/oracle/replay shell> wrc jason/oracle@192.168.238.131/ljw mode=replay replaydir=/home/oracle/replay Workload Replay Client: Release 11.2.0.4.0 - Production on Fri Jul 3 17:08:13 2020 Copyright (c) 1982, 2011, Oracle and/or its affiliates. All rights reserved. Wait for the replay to start (17:08:13) Replay client 1 started (17:08:33) Replay client 1 finished (17:17:48)
执行完成后,等待开启重放客户端。如出现ORA-15552或ORA-15561报错,则基本上是由于重定向步骤未完成而导致的问题。
(6)执行重放
执行重放的时间与生产库捕获时长有关,具体如下。
开始执行重放,命令如下:
SQL> BEGIN DBMS_WORKLOAD_REPLAY.START_REPLAY (); END; / <!--数据库alert后台日志记录了重放开始的时间。--> SQL> DBMS_WORKLOAD_REPLAY.START_REPLAY(): Starting database replay at 07/03/2020 17:05:15 SQL> select dbid, id replay_id, name, CAPTURE_ID, STATUS, AWR_BEGIN_SNAP, AWR_END_SNAP from dba_workload_replays; DBID REPLAY_ID NAME CAPTURE_ID STATUS AWR_BEGIN_SNAP AWR_END_SNAP ---------- --------- ------ ---------- ----------- -------------- ------------ 1589686526 1 replay 1 IN PROGRESS 3610 <!--暂停重放--> SQL> BEGIN DBMS_WORKLOAD_REPLAY.PAUSE_REPLAY (); END; / <!--继续重放--> SQL> BEGIN DBMS_WORKLOAD_REPLAY.RESUME_REPLAY (); END; / <!--停止重放--> SQL> BEGIN DBMS_WORKLOAD_REPLAY.CANCEL_REPLAY (); END; / <!--STATUS三种状态: PREPARE:初始 IN PROGRESS:执行 COMPLETED:完成--> DBMS_WORKLOAD_REPLAY: Database replay ran to completion at 07/03/2020 17:12:28 SQL> select dbid, id replay_id, name, CAPTURE_ID, STATUS, AWR_BEGIN_SNAP, AWR_END_SNAP from dba_workload_replays; DBID REPLAY_ID NAME CAPTURE_ID STATUS AWR_BEGIN_SNAP AWR_END_SNAP ---------- --------- ------ ---------- --------- -------------- ------------ 1589686526 1 replay 1 COMPLETED 3610 3611
4.获取分析报告
1)导出重放期间的详细信息(可选),命令如下:
SQL> set pagesize 0 long 30000000 longchunksize 2000 line 1000 SQL> spool /home/oracle/replay_report.html SQL> select dbms_workload_replay.report(replay_id => 1,format => 'HTML') from dual; SQL> spool off
报告中记录了重放的详细执行情况。
2)导入捕获期间的AWR报告(可选),命令如下:
SQL> select DBMS_WORKLOAD_CAPTURE.GET_CAPTURE_INFO(dir => 'DB_REPLAY_CAPTURE_DIR') capture_id from dual; CAPTURE_ID ---------- 1 SQL> select dbms_workload_capture.import_awr(capture_id => 1, staging_schema => 'SYSTEM') from dual;
3)使用DBMS_WORKLOAD_CAPTURE包生成对比报告,命令如下:
SQL> set serveroutput on SQL> spool /home/oracle/replay/compare_period_report.html SQL> declare v_rlt clob; v_replay_id1 number := 1; v_replay_id2 number := null; v_snum number := 1; v_length number; v_char varchar2(32767); begin dbms_workload_replay.compare_period_report(replay_id1 => v_replay_id1, replay_id2 => v_replay_id2, format => 'HTML', result => v_rlt); v_length := dbms_lob.GETLENGTH(v_rlt); while (v_snum < v_length) loop v_char := dbms_lob.substr(lob_loc => v_rlt, amount => 32767, offset => v_snum); v_snum := v_snum + 32767; dbms_output.put_line(v_char); end loop; end; / SQL> spool off
4)生成AWR性能对比报告。
获取捕获端的dbid、begin_snap和end_snap,命令如下:
SQL> select awr_dbid,status, awr_begin_snap, awr_end_snap from dba_workload_captures; AWR_DBID STATUS AWR_BEGIN_SNAP AWR_END_SNAP ---------- ----------------------- -------------- ------------ 19373648 COMPLETED 3215 3216
获取重放端的信息,命令如下:
SQL> select dbid, STATUS, AWR_BEGIN_SNAP, AWR_END_SNAP from dba_workload_replays; DBID STATUS AWR_BEGIN_SNAP AWR_END_SNAP ---------- ------------- -------------- ------------ 1589686526 COMPLETED 3610 3611
输入以上查询内容生成AWR对比报告,命令如下:
SQL> set pagesize 0 long 30000000 longchunksize 2000 heading off line 1000 SQL> spool /home/oracle/replay/awrdiff_11g_19c.html SQL> SELECT output FROM TABLE(DBMS_WORKLOAD_REPOSITORY.AWR_DIFF_REPORT_HTML(dbid1 => 19373648, inst_num1 => 1, bid1 => 3215, eid1 => 3216, dbid2 => 1589686526, inst_num2 => 1, bid2 => 3610, eid2 => 3611)); SQL> spool off
5.收尾清理
收尾清理的命令如下:
SQL> SELECT 'exec dbms_workload_capture.delete_capture_info('||id||')' FROM dba_ workload_captures; SQL> SELECT 'exec DBMS_WORKLOAD_REPLAY.DELETE_REPLAY_INFO('||id||')' FROM dba_ workload_replays;
3.7.2 SQL性能分析
系统环境的变更可能会导致SQL语句的执行计划发生变化,从而影响SQL的整体性能。如何准确地预测系统更改对SQL性能带来的潜在影响,从而使得我们可以在SQL性能变差之前预先进行调整,或者在SQL性能提升后验证和衡量性能的提升量,这些都需要我们通过工具或脚本来完成。而SQL性能分析(SQL Performance Analyzer,SPA)是我们的最佳选择,如果说数据库重放是迁移前后对整体工作负载可行性的评估,那么SQL性能分析就是对SQL整体业务性能的评估。
SQL性能分析通过自动化识别和评估每个业务SQL语句变更前后的性能差异所产生的总体影响,并提供一份SQL整体性能评估报告,该报告显示了由于语句更改所带来的影响。对于会使性能变差的SQL语句,SPA提供了执行计划详细信息及调整建议,以帮助我们提前纠正任何可能的负面结果,从而使变更对数据库SQL的负面影响降到最低。
我们可以通过SQL性能分析工具分析各种类型的数据库更改对SQL性能产生的影响,这里主要包括以下几大类(如图3-11所示)。
1)数据库升级。
2)PDB级别整合或用户层面整合。
3)操作系统或硬件的配置变更。
4)数据库层参数调整。
5)统计信息更新。
6)SQL优化验证。
SQL性能分析的主要工作流程具体如下。
1)目标环境搭建。
2)根据业务周期捕获生产端需要分析的SQL,并将其存储在SQL调优集(SQL Tunning Set,STS)中。
3)将生产捕获到的SQL调优集打包传输到测试环境并导入。
4)在测试环境上创建SQL性能分析任务。
5)执行SQL调优集中的SQL语句,生成变更前的SQL执行信息。
6)执行系统变更(升级、迁移等)。
7)重新执行SQL调优集中的SQL语句,生成变更后的SQL执行信息。
8)比较和分析变更前后对SQL性能的影响,并生成整体的SQL性能评估报告。
9)调优性能下降的SQL语句。
10)重复执行步骤6到8,直到达到预期的SQL性能目标。
图3-11 SQL性能分析流程图
对于系统范围的更改(例如,数据库升级),不建议在生产系统上使用SQL性能分析,而应该在单独的测试系统上运行,以便能够更好地测试系统更改的效果,同时又不会影响生产。SQL性能分析的具体实现原理不在本节讨论范围之内,感兴趣的读者可以查看官方文档《Testing Guide》。
下面就以真实的升级案例向大家展示SQL性能分析的使用方法。
1.创建测试环境
为了提高测试的准确性,我们通过RMAN物理同步的方式创建了一套与生产几乎相同的测试环境,并将数据库升级到Oracle 19c以进行验证。由于篇幅有限,升级部分不做详细说明。
2.采集SQL信息
在运行SQL性能分析之前,需要在生产系统上捕获一组用于分析的SQL语句,并将捕获的SQL语句放入SQL调优集中,SQL调优集中包含了多个SQL语句及执行统计的信息。除了从生产环境中实时捕获SQL语句之外,我们还可以从现有的AWR、SQL跟踪文件和现有的SQL调优集中导入需要的SQL语句。
在理想情况下,应捕获一个业务周期所有的SQL语句。
1)创建SPA用户,命令如下:
SQL> CREATE USER SPAUSER IDENTIFIED BY ORACLE; SQL> GRANT CONNECT,RESOURCE,DBA TO SPAUSER; SQL> GRANT ADVISOR TO SPAUSER; SQL> GRANT SELECT ANY DICTIONARY TO SPAUSER; SQL> GRANT ADMINISTER SQL TUNING SET TO SPAUSER;
2)创建SQL调优集,命令如下:
SQL> begin DBMS_SQLTUNE.CREATE_SQLSET(SQLSET_NAME => 'spa11g', SQLSET_OWNER => 'SPAUSER'); end;
确认SQL调优集信息,命令如下:
SQL> select owner, name, STATEMENT_COUNT from dba_sqlset; OWNER NAME STATEMENT_COUNT -------- ------------------- --------------- SPAUSER spa11g 0
·SQLSET_NAME:自定义SQL调优集的名称。
·SQLSET_OWNER:指定进行SQL性能分析的用户。
3)获取业务周期AWR快照点,命令如下:
SQL> select min(snap_id) min_id, max(snap_id) max_id from dba_hist_snapshot where end_interval_time between to_date('2020-02-21 00', 'yyyy-mm-dd hh24') and to_date('2020-02-21 14', 'yyyy-mm-dd hh24') order by 1; MIN_ID MAX_ID ---------- ---------- 81474 81488
4)SQL调优集加载数据。通过AWR报告中的SQL语句导入SQL调优集进行整体的SQL性能测试,命令如下:
SQL> declare own VARCHAR2(30) := 'spauser'; bid NUMBER := '&begin_snap'; eid NUMBER := '&end_snap'; stsname VARCHAR2(30) := 'spa11g'; stsowner VARCHAR2(30) := 'SPAUSER'; sts_cur dbms_sqltune.sqlset_cursor; begin open sts_cur for select value(P) from table(dbms_sqltune.select_workload_repository(bid, eid, null, null, null, null, null, 1, null, 'ALL')) P; dbms_sqltune.load_sqlset(sqlset_name => stsname, populate_cursor => sts_cur, load_option => 'MERGE', sqlset_owner => stsowner); end; / 10 11 12 Enter value for begin_snap: 81474 old 3: bid NUMBER := '&begin_snap'; new 3: bid NUMBER := '81474'; Enter value for end_snap: 81488 old 4: eid NUMBER := '&end_snap'; new 4: eid NUMBER := '81488';
3.获取并分析SQL对比信息
1)新建SQL性能分析任务。指定SQLSET_OWNER和SQL调优集的参数值,创建SQL性能分析任务TASK_NAME,为后续对比升级前后的SQL性能做准备,命令如下:
SQL> VARIABLE t_name VARCHAR2(100); SQL> EXEC :t_name := DBMS_SQLPA.CREATE_ANALYSIS_TASK(sqlset_name => 'spa11g', task_name => 'SPA_TASK', SQLSET_OWNER => 'SPAUSER');
·sqlset_name:指定之前创建的SQL调优集的名称。
·task_name:自定义SQL性能分析任务的名称。
·SQLSET_OWNER:指定SPA用户。
2)获取升级前SQL的执行信息。指定分析任务TASK_NAME,获取升级前SQL的执行信息,命令如下:
SQL> begin DBMS_SQLPA.EXECUTE_ANALYSIS_TASK(TASK_NAME => 'SPA_TASK', EXECUTION_NAME => 'EXEC_BEFORE_UPGRADE', EXECUTION_TYPE => 'CONVERT SQLSET'); end; /
执行Oracle 11g到Oracle 19c的升级操作,具体升级步骤请查看本章升级部分的内容。
3)获取升级后SQL的执行信息。升级变更完成后,指定分析任务TASK_NAME,获取升级后SQL的执行信息,命令如下:
SQL> begin DBMS_SQLPA.EXECUTE_ANALYSIS_TASK(TASK_NAME => 'SPA_TASK', EXECUTION_NAME => 'EXEC_AFTER_UPGRADE', EXECUTION_TYPE => 'TEST EXECUTE'); end;
4.执行SQL性能分析对比
1)升级前后SQL性能的对比。得到升级前后SQL执行的信息之后,就可以对比升级它们的执行性能了,下面从不同的维度(SQL执行的时间、SQL执行的CPU时间、SQL执行的逻辑读等)进行对比分析。
对比升级前后SQL执行的时间,命令如下:
SQL> begin DBMS_SQLPA.EXECUTE_ANALYSIS_TASK(task_name => 'SPA_TASK', execution_type => 'COMPARE PERFORMANCE', execution_name => 'Compare_elapsed_time', execution_params => dbms_advisor.arglist ('execution_name1', 'EXEC_BEFORE_UPGRADE', 'execution_name2', 'EXEC_AFTER_UPGRADE', 'comparison_metric', 'elapsed_time')); end; /
对比升级前后SQL执行的CPU时间,命令如下:
SQL> begin DBMS_SQLPA.EXECUTE_ANALYSIS_TASK(task_name => 'SPA_TASK', execution_type => 'COMPARE PERFORMANCE', execution_name => 'Compare_CPU_time', execution_params => dbms_advisor.arglist ('execution_name1', 'EXEC_BEFORE_UPGRADE', 'execution_name2', 'EXEC_AFTER_UPGRADE', 'comparison_metric', 'CPU_TIME')); end; /
对比升级前后SQL执行的逻辑读,命令如下:
SQL> begin DBMS_SQLPA.EXECUTE_ANALYSIS_TASK(task_name => 'SPA_TASK', execution_type => 'COMPARE PERFORMANCE', execution_name => 'Compare_BUFFER_GETS_time', execution_params => dbms_advisor.arglist ('execution_name1', 'EXEC_BEFORE_UPGRADE', 'execution_name2', 'EXEC_AFTER_UPGRADE', 'comparison_metric', 'BUFFER_GETS')); end; /
2)生成SQL性能分析报告,结果如图3-12所示。获取全部结果,命令如下:
SQL> ALTER SESSION SET EVENTS '31156 trace name context forever, level 0x400'; SQL> SET LONG 9999999 longchunksize 100000 linesize 200 head off feedback off echo off SQL> spool 10g_11g_change.html SQL> SELECT DBMS_SQLPA.REPORT_ANALYSIS_TASK('SPA_TASK', 'HTML', 'TYPICAL', 'ALL', NULL, 100, NULL, NULL, NULL) FROM DUAL; SQL> spool off SQL> set trimspool on SQL> set trim on SQL> set pages 0 SQL> set long 999999999 SQL> set linesize 1000
图3-12 SQL性能分析报告
对比SQL的执行时间,生成对比报告,命令如下:
SQL> spool spa_report_elapsed_time.html SQL> SELECT dbms_sqlpa.report_analysis_task('SPA_TASK', 'HTML', 'ALL','ALL', top_sql=>300,execution_name=>'Compare_elapsed_time') FROM dual; SQL> spool off;
对比SQL执行的CPU时间,生成对比报告,命令如下:
SQL> spool spa_report_CPU_time.html SQL> SELECT dbms_sqlpa.report_analysis_task('SPA_TASK', 'HTML', 'ALL','ALL', top_sql=>300,execution_name=>'Compare_CPU_time') FROM dual; SQL> spool off;
对比SQL执行的逻辑读时间,生成对比报告,命令如下:
SQL> spool spa_report_buffer_time.html SQL> SELECT dbms_sqlpa.report_analysis_task('SPA_TASK','HTML','ALL','ALL', top_sql=>300,execution_name=>'Compare_BUFFER_GETS_time') FROM dual; SQL> spool off;
获取错误信息,命令如下:
SQL> spool spa_report_errors.html SQL> SELECT dbms_sqlpa.report_analysis_task('SPA_TASK', 'HTML', 'errors','summary') FROM dual; SQL> spool off;
获取不支持的对象,命令如下:
SQL> spool spa_report_unsupport.html SQL> SELECT dbms_sqlpa.report_analysis_task('SPA_TASK', 'HTML', 'unsupported','all') FROM dual; SQL> spool off; /
从本次迁移前后SQL性能的对比报告来看,一共对比了155条SQL语句,执行计划改变的共计45条,性能改善的共计2条,性能下降的共计5条,性能不变的共计135条,带有报错信息的共计8条,不支持的共计5条。根据性能报告,我们可以有针对性地对性能下降的SQL语句进行优化,具体优化方法请参考第4章。