Focus On Oracle

Installing, Backup & Recovery, Performance Tuning,
Troubleshooting, Upgrading, Patching

Oracle Engineered System


当前位置: 首页 » 技术文章 » ASM

Find block in ASM

find_block.pl是Oracle ASM项目开发组成员(http://asmsupportguy.blogspot.com)用perl写的脚本,脚本很实用,可以帮我们找到数据块在磁盘中对应的位置,她适应于ASMLIB,AFD(ASM Filter Driver) Disk,当然也适用于Exadata。这个脚本需要在GI环境下运行,要设置LD_LIBRARY_PATH,否则可能出现以下的错误。

[oracle@ohs1 ~]$ $ORACLE_HOME/perl/bin/perl find_block.pl  +DATA_PGOLD/racdb/system01.dbf 128

Can't load '/oracle/product/112/perl/lib/site_perl/5.10.0/i686-linux-thread-multi/auto/DBD/Oracle/Oracle.so' for module DBD::Oracle: libclntsh.so.11.1: cannot open shared object file: No such file or directory at /oracle/product/112/perl/lib/5.10.0/i686-linux-thread-multi/DynaLoader.pm line 203.
 at find_block.pl line 35
Compilation failed in require at find_block.pl line 35.
BEGIN failed--compilation aborted at find_block.pl line 35.
[oracle@ohs1 ~]$


使用find_block.pl

$ echo $LD_LIBRARY_PATH
[oracle@ohs1 ~]$ export LD_LIBRARY_PATH=$ORACLE_HOME/lib
[oracle@ohs1 ~]$ $ORACLE_HOME/perl/bin/perl find_block.pl  +DATA_PGOLD/racdb/system01.dbf 1
dd if=/dev/oracleasm/disks/ASMDISK7 bs=8192 count=1 skip=2689 of=block_1.dd
[oracle@ohs1 ~]$ $ORACLE_HOME/perl/bin/perl find_block.pl  +DATA_PGOLD/racdb/system01.dbf 128
dd if=/dev/oracleasm/disks/ASMDISK8 bs=8192 count=1 skip=2944 of=block_128.dd
[oracle@ohs1 ~]$ $ORACLE_HOME/perl/bin/perl find_block.pl  +DATA_PGOLD/racdb/system01.dbf 256
dd if=/dev/oracleasm/disks/ASMDISK6 bs=8192 count=1 skip=2944 of=block_256.dd
[oracle@ohs1 ~]$ 


find_block.pl脚本

#!$ORACLE_HOME/perl/bin/perl -w
#
# The find_block.pl constructs the command(s) to extract a block from ASM.
# For a complete info about this script see ASM Support Guy blog post:
# http://asmsupportguy.blogspot.com/2014/10/find-block-in-asm.html
#
# Copyright (C) 2014 Bane Radulovic
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
# at http://www.gnu.org/licenses/.
#
# Version 1.00, Oct 2014
# The initial release.
#
# Version 1.01, Oct 2014
# Minor improvements.
#
# Version 1.02, Oct 2014
# Added support for AFD disks.
#
# Version 1.03, Nov 2014
# Added sanity checks, e.g. if the requested block is reasonable,
# if the specified filename is valid, etc.
#
# Version 1.04, Nov 2014
# Improved the check for Exadata storage cell based disk.
#
use strict;
use DBI;
use DBD::Oracle qw(:ora_session_modes);
use POSIX;
# Handle the version query
die "find_block.pl version 1.04\n"
 if ( $ARGV[0] =~ /^-v/i );
# Check the number of input arguments
die "Usage: \$ORACLE_HOME/perl/bin/perl find_block.pl filename block\n"
 unless ( @ARGV == 2 );
# Get the filename from the first input argument
my $filename = shift @ARGV;
# Check if the filename makes sense.
# The 'minimum' filename is +DGNAME/filename,
# i.e. it has to begin with the '+' followed by a disk group name,
# followed by at least one '/', followed by directory or file name...
die "Error: The $filename is not a valid file name.\n"
 unless ( $filename =~ /^\+\w/ && $filename =~ /\/\w/ );
# Get the disk group name out of the user specified filename
my $diskgroup_name = substr($filename, 1, index($filename, "/") -1 );
# Get the ASM file name out of the user specified filename
my $asmfile = substr($filename, rindex($filename, "/") +1 );
# Get the block number from the second input argument
my $block_number = shift @ARGV;
# Check if the block number is an integer
die "Usage: \$ORACLE_HOME/perl/bin/perl find_block.pl filename block\n"
 unless ( $block_number =~ /^\d+$/ );
# Check if the ASM SID is set
die "Error: ASM SID not set.\n"
 unless ( $ENV{ORACLE_SID} =~ /\+ASM/ );
# Connect to the (local) ASM instance
my $dbh = DBI->connect('dbi:Oracle:', "", "", { ora_session_mode => ORA_SYSDBA })
 or die "$DBI::errstr\n";
# Check if the disk group exists and if it is mounted
my $group_number = &asm_diskgroup("group_number", $diskgroup_name);
die "Error: Disk group $diskgroup_name not mounted or does not exist.\n"
 unless ( $group_number );
# Check if the user specified file exists in the disk group
my $file_number = &asm_alias("file_number", $asmfile, $group_number);
die "Error: File $asmfile does not exist in disk group $diskgroup_name.\n"
 unless ( $file_number );
# Get the block size for the file
my $block_size = &asm_file("block_size", $group_number, $file_number);
# Get the number of blocks in the file
my $file_blocks = &asm_file("blocks", $group_number, $file_number);
# Check if the user specified block number makes sense
die "Error: Block range for file $asmfile is: 0 - $file_blocks.\n"
 unless ( $block_number >= 0 && $block_number <= $file_blocks );
# Get the disk group AU size
my $au_size = &asm_diskgroup("allocation_unit_size", $diskgroup_name);
# Work out the blocks per AU and the virtual extent number
my $blocks_per_au = $au_size/$block_size;
my $xnum_kffxp = floor($block_number/$blocks_per_au);
# Get the disk and AU numbers into the @disk_au array
my @disk_au = &asm_kffxp($file_number, $group_number, $xnum_kffxp);
die "Could not get any disk and AU numbers for file $asmfile.\n"
 unless ( @disk_au );
# Get the disk path(s) and generate the block extract command(s)
while ( @disk_au ) {
 # Do not assume anything
 my $storage_cell = "FALSE";
 # Get the disk number from @disk_au
 my $disk_number = shift @disk_au;
 # Get the AU number from @disk_au
 my $au_number = shift @disk_au;
 # Get the path for that disk number
 my $path = &asm_disk("path", $group_number, $disk_number);
 # If there is no path move to the next disk
 if ( ! $path ) {
  next;
  }
 # If ASMLIB is in use, the path will return ORCL:DISKNAME.
 # Set the path to /dev/oracleasm/disks/DISKNAME
 elsif ( $path =~ /ORCL:(.*)/ ) {
  $path = "/dev/oracleasm/disks/".$1;
  }
 # If ASM Filter Driver (AFD) is in use, the path will return AFD:DISKNAME.
 # Get the actual path from /dev/oracleafd/disks/DISKNAME
 elsif ( $path =~ /AFD:(.*)/ ) {
  if ( ! open AFDDISK, "/dev/oracleafd/disks/".$1 ) { next }
  else { chomp($path = <AFDDISK>) }
  }
 # For Exadata storage cell based disk, the path will start with o/IP address
 elsif ( $path =~ /^o\/\d{1,3}\./ ) {
  $storage_cell = "TRUE";
  }
 if ( $storage_cell eq "TRUE" ) {
  # Construct the kfed command for Exadata storage cell based disk
  # dev=$path ausz=$au_size aunum=$au_number blksz=$block_size blknum=$block_number
  # The grep filters out the kfed stuff
  print "kfed read dev=$path ausz=$au_size aunum=$au_number blksz=$block_size blknum=$block_number | grep -iv ^kf > block_$block_number.txt\n";
  }
 else {
  # Construct the dd command
  # if=$path bs=$block_size count=1 skip=$skip of=block_$block_number.dd
  my $skip=$au_number*$blocks_per_au + $block_number%$blocks_per_au;
  print "dd if=$path bs=$block_size count=1 skip=$skip of=block_$block_number.dd\n";
  }
 }
# We are done. Disconnect from the (local) ASM instance
$dbh->disconnect;
# Subs
# Get a column from v$asm_file for a given group number and file number
sub asm_file {
 my $col = shift @_;
 my $group_number = shift @_;
 my $file_number = shift @_;
 my $sql = $dbh->prepare("select $col from v\$asm_file where group_number=$group_number and file_number=$file_number");
 $sql->execute;
 my $col_value = $sql->fetchrow_array;
 $sql->finish;
 return $col_value;
 }
# Get a column from v$asm_alias for a given (file) name and group number
sub asm_alias {
 my $col = shift @_;
 my $name = shift @_;
 my $group_number = shift @_;
 my $sql = $dbh->prepare("select $col from v\$asm_alias where lower(name)=lower('$name') and group_number=$group_number");
 $sql->execute;
 my $col_value = $sql->fetchrow_array;
 $sql->finish;
 return $col_value;
 }
# Get a column from v$asm_diskgroup for a given disk group name
sub asm_diskgroup {
 my $col = shift @_;
 my $name = shift @_;
 my $sql = $dbh->prepare("select $col from v\$asm_diskgroup where name=upper('$name')");
 $sql->execute;
 my $col_value = $sql->fetchrow_array;
 $sql->finish;
 return $col_value;
 }
# Get a column from v$asm_disk for a given group number and disk number
sub asm_disk {
 my $col = shift @_;
 my $group_number = shift @_;
 my $disk_number = shift @_;
 my $sql = $dbh->prepare("select $col from v\$asm_disk where group_number=$group_number and disk_number=$disk_number");
 $sql->execute;
 my $col_value = $sql->fetchrow_array;
 $sql->finish;
 return $col_value;
 }
# Get the disk and AU numbers from x$kffxp for a given virtual extent number.
# This will return one row for an external redundancy file,
# two rows for a normal redundancy and three rows for a high redundancy.
# Well, it will return an array with disk and AU pairs, not rows.
sub asm_kffxp {
 my $file_number = shift @_;
 my $group_number = shift @_;
 my $xnum = shift @_;
 # The @disk_au array to hold the disk number, AU number rows
 my @disk_au;
 my $sql = $dbh->prepare("select disk_kffxp, au_kffxp from x\$kffxp where number_kffxp=$file_number and group_kffxp=$group_number and xnum_kffxp=$xnum");
 $sql->execute;
 # Expecting one disk number and one AU number per row
 while ( my @row = $sql->fetchrow_array) {
  # Add each (element of the) row to @disk_au array
  foreach ( @row ) { push @disk_au, $_ }
  }
 $sql->finish;
 return @disk_au;
 }



关键词:asm 

相关文章

Oracle ASM from 10g to 18c
在18c中通过ASM Flex DiskGroup克隆PDB
Exadata and ASM
Oracle ASM Storage Limits
12c 如何将Standard ASM转化为Flex ASM
How to use amdu(ASM Metadata Dump Utility)
How to use kfed(Kernel Files metadata Editor)
How to use kfod(Kernel Files OSM Disk)
如何计算ASM磁盘头自动备份的位置
What is disk_repair_time?
Find block in ASM
12c新特性ASMFD
Top