In this Document
Purpose |
Requirements |
Configuring |
Instructions |
Script |
Sample Output |
APPLIES TO:
Oracle Database - Enterprise Edition - Version 10.1.0.3 to 11.2.0.4 [Release 10.1 to 11.2]Information in this document applies to any platform.
PURPOSE
The OS command iostat is normally used to monitoring system input/output device load. This script will provide similar information like iostat but specific for the ASM disks.
For details about iostat (ie cumulative or not, and so on) please refer to the man of iostat.
For details about iostat (ie cumulative or not, and so on) please refer to the man of iostat.
REQUIREMENTS
We use v$asm_disk_stat instead of v$asm_disk because the information is exactly the same.
The only difference is v$asm_disk_stat is the information available in memory while v$asm_disk access the disks to re-collect some information. Since the information required doesn't require to "re-collect" it from the disks, v$asm_disk_stat is more appropriate here.
On Solaris, please use :
- the /usr/xpg4/bin/grep utility instead of /usr/bin/grep to avoid the following error:
"grep: illegal option -- q"
- the /usr/xpg4/bin/awk utility instead of /usr/bin/awk to avoid the following error:
"awk: syntax error near line 48"
The only difference is v$asm_disk_stat is the information available in memory while v$asm_disk access the disks to re-collect some information. Since the information required doesn't require to "re-collect" it from the disks, v$asm_disk_stat is more appropriate here.
On Solaris, please use :
- the /usr/xpg4/bin/grep utility instead of /usr/bin/grep to avoid the following error:
"grep: illegal option -- q"
- the /usr/xpg4/bin/awk utility instead of /usr/bin/awk to avoid the following error:
"awk: syntax error near line 48"
On other platforms, it could sometime fail due to Shell compatibility issues.
In such case, retry using another Shell (ie: Bash instead of Ksh)
We do not guarantee that it will work for you, so be sure to test it in your environment before relying on it. This script is provided as an example. If it doesn't work, you must adapt it by yourself for your platform
In 10.1 asmcmd utility is not included:
asmcmd can be used against ASM versions 10gR1 (10.1.0.n) and 10gR2 (10.2.0.n). In ASM version 10.2 asmcmd is provided by default ASM installation.
To use asmcmd in ASM version 10.1 environment we can just copy relevant files from 10.2 installation into the 10.1
asmcmd can be used against ASM versions 10gR1 (10.1.0.n) and 10gR2 (10.2.0.n). In ASM version 10.2 asmcmd is provided by default ASM installation.
To use asmcmd in ASM version 10.1 environment we can just copy relevant files from 10.2 installation into the 10.1
CONFIGURING
Not required.
INSTRUCTIONS
Not required
CAUTION
This sample code is provided for educational purposes only, and is not supported by Oracle Support. It has been tested internally, however, we do not guarantee that it will work for you. Ensure that you run it in your test environment before using.
SCRIPT
NOTE: Check the attachments (asmiostat.zip) for the generic script and for platform specific scripts such as AIX/RHEL/HP UX refer (asmiostat_AIX_RHEL_HPUX.zip) attachment
#!/bin/ksh
#
# NAME
# asmiostat.sh
#
# DESCRIPTION
# iostat-like output for ASM
# $ asmiostat.sh [-s ASM ORACLE_SID] [-h ASM ORACLE_HOME] [-g Diskgroup]
# [-f disk path filter] [<interval>] [<count>]
#
# NOTES
# Creates persistent SQL*Plus connection to the +ASM instance implemented
# as a ksh co-process
#
# AUTHOR
# Doug Utzig
#
# MODIFIED
# dutzig 08/18/05 - original version
#
ORACLE_SID=+ASM
NLS_LANG=AMERICAN_AMERICA.WE8ISO8859P1
NLS_DATE_FORMAT='DD-MON-YYYY HH24:MI:SS'
endOfOutput="_EOP$$"
typeset -u diskgroup
typeset diskgroup_string="Disk Group: All diskgroups"
typeset usage="
$0 [-s ASM ORACLE_SID] [-h ASM ORACLE_HOME] [-g diskgroup] [<interval>] [<count>]
Output:
DiskPath - Path to ASM disk
DiskName - ASM disk name
Gr - ASM disk group number
Dsk - ASM disk number
Reads - Reads
Writes - Writes
AvRdTm - Average read time (in msec)
AvWrTm - Average write time (in msec)
KBRd - Kilobytes read
KBWr - Kilobytes written
AvRdSz - Average read size (in bytes)
AvWrSz - Average write size (in bytes)
RdEr - Read errors
WrEr - Write errors
"
while getopts ":s:h:g:f" option; do
case $option in
s) ORACLE_SID="$OPTARG" ;;
h) ORACLE_HOME="$OPTARG"
LD_LIBRARY_PATH=$ORACLE_HOME/lib:$LD_LIBRARY_PATH ;;
g) diskgroup="$OPTARG"
diskgroup_string="Disk Group: $diskgroup" ;;
f) print '-f option not implemented' ;;
:) print "Option $OPTARG needs a value"
print "$usage"
exit 1 ;;
\?) print "Invalid option $OPTARG"
print "$usage"
exit 1 ;;
esac
done
shift OPTIND-1
typeset -i interval=${1:-10} count=${2:-0} index=0
#
# Verify interval and count arguments are valid
#
(( interval <=0 || count<0 )) && {
print 'Invalid parameter: <interval> must be > 0; <count> must be >= 0'
print "$usage"
exit 1
}
#
# Query to run against v$asm_disk_stat
#
if [[ -z $diskgroup ]]; then
query="select group_number, disk_number, name, path, reads, writes, read_errs, write_errs, read_time, write_time, bytes_read, bytes_written from v\$asm_disk_stat where group_number>0 order by group_number, disk_number;"
else
query="select group_number, disk_number, name, path, reads, writes, read_errs, write_errs, read_time, write_time, bytes_read, bytes_written from v\$asm_disk_stat where group_number=(select group_number from v\$asm_diskgroup_stat where name=regexp_replace('$diskgroup','^\+','')) order by group_number, disk_number;"
fi
#
# Check for version 10.2 or later
#
typeset version minversion=10.2
version=$($ORACLE_HOME/bin/exp </dev/null 2>&1 | grep "Export: " | sed -e 's/^Export: Release \([0-9][0-9]*\.[0-9][0-9]*\).*/\1/')
if ! (print "$version<$minversion" | bc >/dev/null 2>&1); then
print "$0 requires Oracle Database Release $minversion or later"
exit 1
fi
#############################################################################
#
# Fatal error
#----------------------------------------------------------------------------
function fatalError {
print -u2 -- "Error: $1"
exit 1
}
#############################################################################
#
# Drain all of the sqlplus output - stop when we see our well known string
#----------------------------------------------------------------------------
function drainOutput {
typeset dispose=${1:-'dispose'} output
while :; do
read -p output || fatalError 'Read from co-process failed [$0]'
if [[ $QUERYDEBUG == ON ]]; then print $output; fi
if [[ $output == $endOfOutput* ]]; then break; fi
[[ $dispose != 'dispose' ]] && print -- $output
done
}
#############################################################################
#
# Ensure the instance is running and it is of type ASM
#----------------------------------------------------------------------------
function verifyASMinstance {
typeset asmcmdPath=$ORACLE_HOME/bin/asmcmd
[[ ! -x $asmcmdPath ]] && fatalError "Invalid ORACLE_HOME $ORACLE_HOME: $asmcmdPath does not exist"
$asmcmdPath pwd 2>/dev/null | grep -q '^\+$' || fatalError "$ORACLE_SID is not an ASM instance"
}
#############################################################################
#
# Start the sqlplus coprocess
#----------------------------------------------------------------------------
function startSqlplus {
# start sqlplus, setup the env
$ORACLE_HOME/bin/sqlplus -s '/ as sysdba' |&
print -p 'whenever sqlerror exit failure' \
&& print -p "set pagesize 9999 linesize 9999 feedback off heading off" \
&& print -p "prompt $endOfOutput" \
|| fatalError 'Write to co-process failed (startSqlplus)'
drainOutput dispose
}
#############################################################################
#############################################################################
# MAIN
#----------------------------------------------------------------------------
verifyASMinstance
startSqlplus
#
# Loop as many times as requested or forever
#
while :; do
print -p "$query" \
&& print -p "prompt $endOfOutput" \
|| fatalError 'Write to co-process failed (collectData)'
stats=$(drainOutput keep)
print -- "$stats\nEOL"
index=index+1
(( count<index && count>0 )) && break
sleep $interval
done | \
awk '
BEGIN { firstSample=1
}
/^EOL$/ {
firstSample=0; firstLine=1
next
}
{
path=$4
if (path ~ /^ *$/) next
group[path]=$1; disk[path]=$2; name[path]=$3
reads[path]=$5; writes[path]=$6
readErrors[path]=$7; writeErrors[path]=$8
readTime[path]=$9; writeTime[path]=$10
readBytes[path]=$11; writeBytes[path]=$12
# reads and writes
readsDiff[path]=reads[path]-readsPrev[path]
writesDiff[path]=writes[path]-writesPrev[path]
# read errors and write errors
readErrorsDiff[path]=readErrors[path]-readErrorsPrev[path]
writeErrorsDiff[path]=writeErrors[path]-writeErrorsPrev[path]
# read time and write time
readTimeDiff[path]=readTime[path]-readTimePrev[path]
writeTimeDiff[path]=writeTime[path]-writeTimePrev[path]
# average read time and average write time in msec (data provided in csec)
avgReadTime[path]=0; avgWriteTime[path]=0
if ( readsDiff[path] ) avgReadTime[path]=(readTimeDiff[path]/readsDiff[path])*1000
if ( writesDiff[path]) avgWriteTime[path]=(writeTimeDiff[path]/writesDiff[path])*1000
# bytes and KB read and bytes and KB written
readBytesDiff[path]=readBytes[path]-readBytesPrev[path]
writeBytesDiff[path]=writeBytes[path]-writeBytesPrev[path]
readKb[path]=readBytesDiff[path]/1024
writeKb[path]=writeBytesDiff[path]/1024
# average read size and average write size
avgReadSize[path]=0; avgWriteSize[path]=0
if ( readsDiff[path] ) avgReadSize[path]=readBytesDiff[path]/readsDiff[path]
if ( writesDiff[path] ) avgWriteSize[path]=writeBytesDiff[path]/writesDiff[path]
if (!firstSample) {
if (firstLine) {
"date" | getline now; close("date")
printf "\n"
printf "Date: %s Interval: %d secs %s\n\n", now, '"$interval"', "'"$diskgroup_string"'"
printf "%-40s %2s %3s %8s %8s %6s %6s %8s %8s %7s %7s %4s %4s\n", \
"DiskPath - DiskName","Gr","Dsk","Reads","Writes","AvRdTm",\
"AvWrTm","KBRd","KBWr","AvRdSz","AvWrSz", "RdEr", "WrEr"
firstLine=0
}
printf "%-40s %2s %3s %8d %8d %6.1f %6.1f %8d %8d %7d %7d %4d %4d\n", \
path " - " name[path], group[path], disk[path], \
readsDiff[path], writesDiff[path], \
avgReadTime[path], avgWriteTime[path], \
readKb[path], writeKb[path], \
avgReadSize[path], avgWriteSize[path], \
readErrorsDiff[path], writeErrorsDiff[path]
}
readsPrev[path]=reads[path]; writesPrev[path]=writes[path]
readErrorsPrev[path]=readErrors[path]; writeErrorsPrev[path]=writeErrors[path]
readTimePrev[path]=readTime[path]; writeTimePrev[path]=writeTime[path]
readBytesPrev[path]=readBytes[path]; writeBytesPrev[path]=writeBytes[path]
}
END {
}
'
exit 0
#
# NAME
# asmiostat.sh
#
# DESCRIPTION
# iostat-like output for ASM
# $ asmiostat.sh [-s ASM ORACLE_SID] [-h ASM ORACLE_HOME] [-g Diskgroup]
# [-f disk path filter] [<interval>] [<count>]
#
# NOTES
# Creates persistent SQL*Plus connection to the +ASM instance implemented
# as a ksh co-process
#
# AUTHOR
# Doug Utzig
#
# MODIFIED
# dutzig 08/18/05 - original version
#
ORACLE_SID=+ASM
NLS_LANG=AMERICAN_AMERICA.WE8ISO8859P1
NLS_DATE_FORMAT='DD-MON-YYYY HH24:MI:SS'
endOfOutput="_EOP$$"
typeset -u diskgroup
typeset diskgroup_string="Disk Group: All diskgroups"
typeset usage="
$0 [-s ASM ORACLE_SID] [-h ASM ORACLE_HOME] [-g diskgroup] [<interval>] [<count>]
Output:
DiskPath - Path to ASM disk
DiskName - ASM disk name
Gr - ASM disk group number
Dsk - ASM disk number
Reads - Reads
Writes - Writes
AvRdTm - Average read time (in msec)
AvWrTm - Average write time (in msec)
KBRd - Kilobytes read
KBWr - Kilobytes written
AvRdSz - Average read size (in bytes)
AvWrSz - Average write size (in bytes)
RdEr - Read errors
WrEr - Write errors
"
while getopts ":s:h:g:f" option; do
case $option in
s) ORACLE_SID="$OPTARG" ;;
h) ORACLE_HOME="$OPTARG"
LD_LIBRARY_PATH=$ORACLE_HOME/lib:$LD_LIBRARY_PATH ;;
g) diskgroup="$OPTARG"
diskgroup_string="Disk Group: $diskgroup" ;;
f) print '-f option not implemented' ;;
:) print "Option $OPTARG needs a value"
print "$usage"
exit 1 ;;
\?) print "Invalid option $OPTARG"
print "$usage"
exit 1 ;;
esac
done
shift OPTIND-1
typeset -i interval=${1:-10} count=${2:-0} index=0
#
# Verify interval and count arguments are valid
#
(( interval <=0 || count<0 )) && {
print 'Invalid parameter: <interval> must be > 0; <count> must be >= 0'
print "$usage"
exit 1
}
#
# Query to run against v$asm_disk_stat
#
if [[ -z $diskgroup ]]; then
query="select group_number, disk_number, name, path, reads, writes, read_errs, write_errs, read_time, write_time, bytes_read, bytes_written from v\$asm_disk_stat where group_number>0 order by group_number, disk_number;"
else
query="select group_number, disk_number, name, path, reads, writes, read_errs, write_errs, read_time, write_time, bytes_read, bytes_written from v\$asm_disk_stat where group_number=(select group_number from v\$asm_diskgroup_stat where name=regexp_replace('$diskgroup','^\+','')) order by group_number, disk_number;"
fi
#
# Check for version 10.2 or later
#
typeset version minversion=10.2
version=$($ORACLE_HOME/bin/exp </dev/null 2>&1 | grep "Export: " | sed -e 's/^Export: Release \([0-9][0-9]*\.[0-9][0-9]*\).*/\1/')
if ! (print "$version<$minversion" | bc >/dev/null 2>&1); then
print "$0 requires Oracle Database Release $minversion or later"
exit 1
fi
#############################################################################
#
# Fatal error
#----------------------------------------------------------------------------
function fatalError {
print -u2 -- "Error: $1"
exit 1
}
#############################################################################
#
# Drain all of the sqlplus output - stop when we see our well known string
#----------------------------------------------------------------------------
function drainOutput {
typeset dispose=${1:-'dispose'} output
while :; do
read -p output || fatalError 'Read from co-process failed [$0]'
if [[ $QUERYDEBUG == ON ]]; then print $output; fi
if [[ $output == $endOfOutput* ]]; then break; fi
[[ $dispose != 'dispose' ]] && print -- $output
done
}
#############################################################################
#
# Ensure the instance is running and it is of type ASM
#----------------------------------------------------------------------------
function verifyASMinstance {
typeset asmcmdPath=$ORACLE_HOME/bin/asmcmd
[[ ! -x $asmcmdPath ]] && fatalError "Invalid ORACLE_HOME $ORACLE_HOME: $asmcmdPath does not exist"
$asmcmdPath pwd 2>/dev/null | grep -q '^\+$' || fatalError "$ORACLE_SID is not an ASM instance"
}
#############################################################################
#
# Start the sqlplus coprocess
#----------------------------------------------------------------------------
function startSqlplus {
# start sqlplus, setup the env
$ORACLE_HOME/bin/sqlplus -s '/ as sysdba' |&
print -p 'whenever sqlerror exit failure' \
&& print -p "set pagesize 9999 linesize 9999 feedback off heading off" \
&& print -p "prompt $endOfOutput" \
|| fatalError 'Write to co-process failed (startSqlplus)'
drainOutput dispose
}
#############################################################################
#############################################################################
# MAIN
#----------------------------------------------------------------------------
verifyASMinstance
startSqlplus
#
# Loop as many times as requested or forever
#
while :; do
print -p "$query" \
&& print -p "prompt $endOfOutput" \
|| fatalError 'Write to co-process failed (collectData)'
stats=$(drainOutput keep)
print -- "$stats\nEOL"
index=index+1
(( count<index && count>0 )) && break
sleep $interval
done | \
awk '
BEGIN { firstSample=1
}
/^EOL$/ {
firstSample=0; firstLine=1
next
}
{
path=$4
if (path ~ /^ *$/) next
group[path]=$1; disk[path]=$2; name[path]=$3
reads[path]=$5; writes[path]=$6
readErrors[path]=$7; writeErrors[path]=$8
readTime[path]=$9; writeTime[path]=$10
readBytes[path]=$11; writeBytes[path]=$12
# reads and writes
readsDiff[path]=reads[path]-readsPrev[path]
writesDiff[path]=writes[path]-writesPrev[path]
# read errors and write errors
readErrorsDiff[path]=readErrors[path]-readErrorsPrev[path]
writeErrorsDiff[path]=writeErrors[path]-writeErrorsPrev[path]
# read time and write time
readTimeDiff[path]=readTime[path]-readTimePrev[path]
writeTimeDiff[path]=writeTime[path]-writeTimePrev[path]
# average read time and average write time in msec (data provided in csec)
avgReadTime[path]=0; avgWriteTime[path]=0
if ( readsDiff[path] ) avgReadTime[path]=(readTimeDiff[path]/readsDiff[path])*1000
if ( writesDiff[path]) avgWriteTime[path]=(writeTimeDiff[path]/writesDiff[path])*1000
# bytes and KB read and bytes and KB written
readBytesDiff[path]=readBytes[path]-readBytesPrev[path]
writeBytesDiff[path]=writeBytes[path]-writeBytesPrev[path]
readKb[path]=readBytesDiff[path]/1024
writeKb[path]=writeBytesDiff[path]/1024
# average read size and average write size
avgReadSize[path]=0; avgWriteSize[path]=0
if ( readsDiff[path] ) avgReadSize[path]=readBytesDiff[path]/readsDiff[path]
if ( writesDiff[path] ) avgWriteSize[path]=writeBytesDiff[path]/writesDiff[path]
if (!firstSample) {
if (firstLine) {
"date" | getline now; close("date")
printf "\n"
printf "Date: %s Interval: %d secs %s\n\n", now, '"$interval"', "'"$diskgroup_string"'"
printf "%-40s %2s %3s %8s %8s %6s %6s %8s %8s %7s %7s %4s %4s\n", \
"DiskPath - DiskName","Gr","Dsk","Reads","Writes","AvRdTm",\
"AvWrTm","KBRd","KBWr","AvRdSz","AvWrSz", "RdEr", "WrEr"
firstLine=0
}
printf "%-40s %2s %3s %8d %8d %6.1f %6.1f %8d %8d %7d %7d %4d %4d\n", \
path " - " name[path], group[path], disk[path], \
readsDiff[path], writesDiff[path], \
avgReadTime[path], avgWriteTime[path], \
readKb[path], writeKb[path], \
avgReadSize[path], avgWriteSize[path], \
readErrorsDiff[path], writeErrorsDiff[path]
}
readsPrev[path]=reads[path]; writesPrev[path]=writes[path]
readErrorsPrev[path]=readErrors[path]; writeErrorsPrev[path]=writeErrors[path]
readTimePrev[path]=readTime[path]; writeTimePrev[path]=writeTime[path]
readBytesPrev[path]=readBytes[path]; writeBytesPrev[path]=writeBytes[path]
}
END {
}
'
exit 0
SAMPLE OUTPUT
For every ASM disk, this is the output provided:
DiskPath - Path to ASM disk
DiskName - ASM disk name
Gr - ASM disk group number
Dsk - ASM disk number
Reads - Reads
Writes - Writes
AvRdTm - Average read time (in msec)
AvWrTm - Average write time (in msec)
KBRd - Kilobytes read
KBWr - Kilobytes written
AvRdSz - Average read size (in bytes)
AvWrSz - Average write size (in bytes)
RdEr - Read errors
WrEr - Write errors
DiskName - ASM disk name
Gr - ASM disk group number
Dsk - ASM disk number
Reads - Reads
Writes - Writes
AvRdTm - Average read time (in msec)
AvWrTm - Average write time (in msec)
KBRd - Kilobytes read
KBWr - Kilobytes written
AvRdSz - Average read size (in bytes)
AvWrSz - Average write size (in bytes)
RdEr - Read errors
WrEr - Write errors
Script Output
DiskPath - DiskName Gr Dsk Reads Writes AvRdTm AvWrTm KBRd KBWr AvRdSz AvWrSz RdEr WrEr /dev/asmdisk14 - DATA_0000 2 0 0 4 0.0 5.0 0 16 0 4096 0 0
/dev/asmdisk4 - DATA_0001 2 1 10 0 17.0 0.0 0 0 0 0 0 0
/dev/asmdisk17 - DATA_0002 2 2 0 1 0.0 0.0 0 0 0 512 0 0
/dev/asmdisk5 - DATA_0003 2 3 0 0 0.0 0.0 0 0 0 0 0 0
/dev/asmdisk16 - DATA_0004 2 4 3 3 6.7 3.3 0 0 0 0 0 0
/dev/asmdisk23 - DATA_0005 2 5 0 0 0.0 0.0 0 0 0 0 0 0
/dev/asmdisk4 - DATA_0001 2 1 10 0 17.0 0.0 0 0 0 0 0 0
/dev/asmdisk17 - DATA_0002 2 2 0 1 0.0 0.0 0 0 0 512 0 0
/dev/asmdisk5 - DATA_0003 2 3 0 0 0.0 0.0 0 0 0 0 0 0
/dev/asmdisk16 - DATA_0004 2 4 3 3 6.7 3.3 0 0 0 0 0 0
/dev/asmdisk23 - DATA_0005 2 5 0 0 0.0 0.0 0 0 0 0 0 0