Oracle开发者性能课第9课(如何查找慢 SQL)实验

Posted dingdingfish

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle开发者性能课第9课(如何查找慢 SQL)实验相关的知识,希望对你有一定的参考价值。

概述

本实验参考DevGym中的实验指南

创建环境

创建表和过程,其中我加了注释:

create table schools (
  school_id integer not null primary key,
  school_name varchar2(30) not null
);

create table students (
  student_id integer not null primary key,
  parent_id  integer not null, 
  school_id  integer not null
);

create table school_attendance (
  school_id  
    references schools ( school_id ) not null,
  student_id 
    references students ( student_id ) not null,
  primary key (
    school_id, student_id
  )
);

create or replace procedure init as
  num_students constant pls_integer := 5000;
begin
  delete school_attendance;
  delete schools;
  delete students;
  
  insert into students 
    with rws as (
      select level student_id,
             mod ( level, ( num_students / 2 ) ) + 1 parent_id,
             mod ( level, 5 ) + 1 school_id 
      from   dual
      connect by level <= num_students
    )
      select * from rws;
      
  insert into schools values ( 1, 'Java Junior' );
  insert into schools values ( 2, 'Haskell High' );
  insert into schools values ( 3, 'SQL School' );
  insert into schools values ( 4, 'C College' );
  insert into schools values ( 5, 'Ada Academy' );
      
  commit;
   
end init;
/

exec init();

查看数据:

-- 有5个学校
SQL> select * from schools;
   SCHOOL_ID     SCHOOL_NAME
____________ _______________
           1 Java Junior
           2 Haskell High
           3 SQL School
           4 C College
           5 Ada Academy

-- 5000个学生
SQL> select * from students where rownum < 10;
   STUDENT_ID    PARENT_ID    SCHOOL_ID
_____________ ____________ ____________
          921          922            2
          922          923            3
          923          924            4
          924          925            5
          925          926            1
          926          927            2
          927          928            3
          928          929            4
          929          930            5

9 rows selected.


SQL> select count(*) from students;
   COUNT(*)
___________
       5000

SQL> select count(*) from school_attendance;

   COUNT(*)
___________
          0

简介

到目前为止,本系列中的教程都侧重于调优单个 SQL 语句。但通常你需要分析才能找出最慢的 SQL !

为此,您需要分析在“数据库运行缓慢”时运行的代码。这将捕获运行客户抱怨的SQL语句的会话时的详细执行信息。

本教程介绍了 Oracle 数据库中的工具,以帮助您分析 SQL 和 PL/SQL 执行情况。

创建要跟踪的程序

创建此过程以添加学校的学生出勤条目:

create or replace procedure drop_off_students (
  school integer
) as
  att_count pls_integer;
begin
  for studs in ( 
    select student_id
    from   students
    where  school_id = school
  ) loop
   
    select count (*) into att_count
    from   school_attendance
    where  student_id = studs.student_id
    and    school_id = school;
  
    if att_count = 0 then 
      insert into school_attendance ( student_id, school_id )
        values ( studs.student_id, school );
    end if;
      
  end loop;
end drop_off_students;
/

create or replace procedure start_school as
begin
  for s in (
    select school_id from schools
  ) loop
    drop_off_students ( s.school_id );
  end loop;
end start_school;
/

跟踪 SQL 执行

要捕获会话中所有语句的 SQL 执行详细信息,您可以运行 SQL Trace。使用 DBMS_monitor.session_trace_enable 执行此操作:

exec sys.dbms_monitor.session_trace_enable ( waits => true, binds => true );

exec start_school ();
  
exec sys.dbms_monitor.session_trace_disable ();

以上将产生Trace文件。

访问SQL Trace文件

SQL Trace文件位于数据库服务器。
或者也可以用SQL访问系统视图V$DIAG_TRACE_FILE_CONTENTS:

set lines 200
set serveroutput off
set pagesize 0
set echo off 
set feedback off 
set trimspool on 
set heading off
set tab off

spool /tmp/trace-file.trc
select payload 
from   v$diag_trace_file_contents
where  trace_filename = (
    select substr (
           value,
           instr ( value, '/', -1 ) + 1
         ) filename
  from   v$diag_info
  where  name = 'Default Trace File'
)
order  by line_number;
spool off

需要删除输出文件头部和尾部记录的SQL语句

格式化 SQL Trace文件

SQL Trace文件一般都比较大,注意文件头部有trace文件的具体位置:

$ wc -l /tmp/trace-file.trc
85185 /tmp/trace-file.trc

$ more trace-file.trc
Trace file /opt/oracle/diag/rdbms/orclcdb/ORCLCDB/trace/ORCLCDB_ora_24406.trc
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.11.0.0.0
Build label:    RDBMS_19.11.0.0.0DBRU_LINUX.X64_210412
ORACLE_HOME:    /opt/oracle/product/19c/dbhome_1
System name:    Linux
Node name:      oracle-19c-vagrant
Release:        5.4.17-2102.202.5.el7uek.x86_64
Version:        #2 SMP Sat May 22 16:17:06 PDT 2021
Machine:        x86_64
Instance name: ORCLCDB
Redo thread mounted by this instance: 1
Oracle process number: 29
Unix process pid: 24406, image: oracle@oracle-19c-vagrant


*** 2021-10-30T21:39:19.697766+08:00 (ORCLPDB1(3))

*** SESSION ID:(264.23699) 2021-10-30T21:39:19.697789+08:00
*** CLIENT ID:() 2021-10-30T21:39:19.697793+08:00
*** SERVICE NAME:(orclpdb1) 2021-10-30T21:39:19.697795+08:00
*** MODULE NAME:(SQL*Plus) 2021-10-30T21:39:19.697798+08:00
*** ACTION NAME:() 2021-10-30T21:39:19.697800+08:00
*** CLIENT DRIVER:(SQL*PLUS) 2021-10-30T21:39:19.697802+08:00
*** CONTAINER ID:(3) 2021-10-30T21:39:19.697804+08:00

=====================
PARSING IN CURSOR #140298864054008 len=83 dep=0 uid=113 oct=47 lid=113 tim=42004037595 hv=666663546 ad='b8aa1b90' sqlid='575acgwmvsymu'
BEGIN sys.dbms_monitor.session_trace_enable ( waits => true, binds => true ); END;
...

需要格式化使其易读:

cd /tmp
tkprof trace-file.trc trace-file.txt

格式化后居然只有200多行了:

$ wc -l trace-file.txt
215 trace-file.txt

$ cat trace-file.txt

TKPROF: Release 19.0.0.0.0 - Development on Sat Oct 30 21:41:11 2021

Copyright (c) 1982, 2019, Oracle and/or its affiliates.  All rights reserved.

Trace file: trace-file.trc
Sort options: default

********************************************************************************
count    = number of times OCI procedure was executed
cpu      = cpu time in seconds executing
elapsed  = elapsed time in seconds executing
disk     = number of physical reads of buffers from disk
query    = number of buffers gotten for consistent read
current  = number of buffers gotten in current mode (usually for update)
rows     = number of rows processed by the fetch or execute call
********************************************************************************

SQL ID: 575acgwmvsymu Plan Hash: 0

BEGIN sys.dbms_monitor.session_trace_enable ( waits => true, binds => true );
  END;


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        0      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           1
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        1      0.00       0.00          0          0          0           1

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 113

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       1        0.00          0.00
  SQL*Net message from client                     1        0.17          0.17
********************************************************************************

SQL ID: bu5kprjcz46js Plan Hash: 0

BEGIN start_school (); END;


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.09       0.10          0          0          0           1
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      0.09       0.10          0          0          0           1

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 113

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       1        0.00          0.00
  SQL*Net message from client                     1        0.61          0.61
********************************************************************************

SQL ID: 87rppdhxpw693 Plan Hash: 1367354807

SELECT SCHOOL_ID
FROM
 SCHOOLS


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        1      0.00       0.00          0          4          0           5
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        3      0.00       0.00          0          4          0           5

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 113     (recursive depth: 1)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         5          5          5  INDEX FAST FULL SCAN SYS_C008677 (cr=4 pr=0 pw=0 time=60 us starts=1 cost=2 size=13 card=1)(object id 75943)

********************************************************************************

SQL ID: 35sscdb8ca6uh Plan Hash: 4078133427

SELECT STUDENT_ID
FROM
 STUDENTS WHERE SCHOOL_ID = :B1


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      5      0.00       0.00          0          0          0           0
Fetch       55      0.00       0.00          0        129          0        5000
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       61      0.00       0.00          0        129          0        5000

Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 113     (recursive depth: 1)
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
      1000       1000       1000  TABLE ACCESS FULL STUDENTS (cr=25 pr=0 pw=0 time=164 us starts=1 cost=5 size=26000 card=1000)

********以上是关于Oracle开发者性能课第9课(如何查找慢 SQL)实验的主要内容,如果未能解决你的问题,请参考以下文章

Oracle开发者性能课第6课(如何创建物化视图)实验

Oracle开发者性能课第4课(如何创建索引)实验

Oracle开发者性能课第1课(如何阅读执行计划)实验

Oracle开发者性能课第7课(Join如何工作)实验

Oracle开发者性能课第3课(我的查询做了多少工作)实验

Oracle开发者性能课第5课(为什么我的查询不使用索引)实验