#!/usr/bin/perl -w # Lastanalyse aufgrund der SQL Area # Author: Uwe Schneider # # Diese Software ist UNSUPPORTET. # use strict; use FileHandle; use DBI; use Getopt::Std; # $opt_d: Differentielle Analyse, wartet $opt_d Sekunden # $opt_n: Gib eine fortlaufende Nr. aus # $opt_s: Gib die Liste der Session-Ids aus use vars qw($opt_d $opt_n $opt_s); getopts('d:ns') || usage_exit(); # Sekunden, die das Ding wartet. my ($wait_time); $wait_time = $opt_d if ($opt_d); # Feldseparator bei der Ausgabe my ($SEP) = "\t"; ############################################################### sub do_select { my ($dbh) = @_; my ($query) = q{ SELECT u.username , a.hash_value , a.address , sum(a.executions) , sum(a.disk_reads) , min(replace(a.SQL_TEXT,chr(13),' ')) , avg(86400 * (sysdate - i.startup_time)) FROM V$SQL a , dba_users u , v$instance i WHERE a.PARSING_SCHEMA_ID = u.user_id AND u.username NOT LIKE 'SYS%' GROUP BY u.username , a.hash_value , a.address } || die; # Alle Session-IDs, die zu einem SQL-Text gehören my ($query_sess) = $dbh->prepare(q{ SELECT se.sid FROM v$open_cursor oc , v$session se WHERE se.saddr = oc.saddr AND se.sid = oc.sid AND oc.hash_value = ? AND oc.address = ? GROUP BY se.sid ORDER BY se.sid }) || die; my($sth) = $dbh->prepare($query) || die; my ($n_secs,$puser,$hash,$addr,$n_exec, $n_exec_sec,$n_disk, $n_disk_exec,$sql); my ($lifetime, $lifetime1,$lifetime2); my ($sid, $sidlist); my ($st); if ($wait_time) { $sth->execute() || die; while (($puser,$hash,$addr,$n_exec,$n_disk,$sql,$n_secs) = $sth->fetchrow_array()) { $sql =~ tr/\c@//d; $sql =~ tr/\t\r\n\f/ /; $st->[0]{$puser}{$hash} = [$n_exec, $n_disk, $sql]; $lifetime1 = $n_secs; } $sth->finish(); sleep($wait_time); } $sth->execute() || die; while (($puser,$hash,$addr,$n_exec,$n_disk,$sql,$n_secs) = $sth->fetchrow_array()) { $sql =~ tr/\c@//d; $sql =~ tr/\t\r\n\f/ /; $lifetime2 = $n_secs; # Hole auch die Session-IDs if ($opt_s) { $sidlist = ""; $query_sess->execute($hash,$addr) || die; while (($sid) = $query_sess->fetchrow_array()) { $sidlist .= "$sid,"; } $query_sess->finish(); chop($sidlist); # Letztes Komma wegwerfen } $st->[1]{$puser}{$hash} = [$n_exec, $n_disk, $sql, $sidlist]; } $sth->finish(); if ($wait_time) { $lifetime = $lifetime2 - $lifetime1; } else { $lifetime = $lifetime2 - 0; } my $st_delta; # Anzahl Statements / Sekunde my $n_sec_sum = 0; # bestimme Delta und gib aus! # Reigenfolge: Absteigend nach Anzahl der Executions print "User${SEP}"; print "N${SEP}" if $opt_n; print "N Exec${SEP}N Exec/sec${SEP}Diskreads${SEP}Diskreads/Exec.${SEP}"; print "SIDs${SEP}" if $opt_s; print "SQL Text\n"; foreach $puser (sort keys %{$st->[1]}) { my $st_delta; # Loop über alle Statements foreach $hash (keys %{$st->[1]{$puser}}) { $st_delta->{$hash}{'n_exec'} = $st->[1]{$puser}{$hash}[0] - ($st->[0]{$puser}{$hash}[0] || 0); $st_delta->{$hash}{'n_exec_secs'} = $st_delta->{$hash}{'n_exec'}/$lifetime; $st_delta->{$hash}{'n_diskreads'} = $st->[1]{$puser}{$hash}[1] - ($st->[0]{$puser}{$hash}[1] || 0); $st_delta->{$hash}{'n_disk_exec'} = $st_delta->{$hash}{'n_diskreads'} / ($st_delta->{$hash}{'n_exec'} || 1); $st_delta->{$hash}{'sql'} = $st->[1]{$puser}{$hash}[2]; $st_delta->{$hash}{'sidlist'} = $st->[1]{$puser}{$hash}[3] if $opt_s; $n_sec_sum += $st_delta->{$hash}{'n_exec_secs'}; } my ($count) = 0; # Ausgabeschleife: Absteigend sortiert über Executions foreach $hash (sort { $st_delta->{$b}{'n_exec'} <=>$st_delta->{$a}{'n_exec'} } keys %{$st_delta}) { # ganz seltene Staements wegwerfen? #next if ($st_delta->{$hash}{'n_exec_secs'} < 0.001); # Alles wegwerfen, was mit dem EXPLAIN PLAN zu tun hat next if $st_delta->{$hash}{'sql'} =~ /plan_table/i; $count++; printf "%s${SEP}",$puser; printf "$count${SEP}" if ($opt_n); printf "%s${SEP}%.3f${SEP}%s${SEP}%.3f${SEP}", $st_delta->{$hash}{'n_exec'}, $st_delta->{$hash}{'n_exec_secs'}, $st_delta->{$hash}{'n_diskreads'}, $st_delta->{$hash}{'n_disk_exec'} ; printf "%s${SEP}",$st_delta->{$hash}{'sidlist'} if $opt_s; printf "%s\n",$st_delta->{$hash}{'sql'}; } print "\n\n"; } printf "Anzahl der Statements/sec: %.2f\n",$n_sec_sum; } ############################################################### sub usage_exit { print STDERR < Optionen: -d : Bilde differentielle Last während Sekunden -n : Gib eine fortlaufende Statement-Nummer aus -s : Gib die Sessions aus, die das SQL abgesetzt haben EOUSAGE die; } ############################################################### my ($login) = shift || usage_exit(); my ($dbh) = DBI->connect("DBI:Oracle:",$login); $dbh->{'AutoCommit'} = 0; $dbh->do(qq(ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '.,')); unless ($dbh) { die "Kein Connect zur DB.\n";} do_select($dbh); $dbh->disconnect();