Subject: Help: Reducing Extents From: John Clutterbuck Newsgroups: comp.databases.informix Date: Mon, 23 Jun 1997 17:45:37 +0100 > I am running a electronic document management system (EDMS) that uses > Informix 7.22 as a back end to manage the documents. The EDMS system > warns me when there are database problems that slow performance. In > this case it warned me that there were too many extends for the tables > listed below: > > DBMS Tables With More Than 5 Extents > # of Extents Type Name > 36 Table arrowdev:dm_sysobject_s > 22 Table arrowdev:syscolumns > 21 Table arrowdev:dmr_content_s > 19 Table arrowdev:sysviews > 17 Table arrowdev:dm_sysobject_r > 15 Table arrowdev:dmr_content_r > 14 Table arrowdev:dmi_object > 12 Table arrowdev:dm_type_r > 8 Table arrowdev:sysobjstate > 7 Table arrowdev:dmi_queue_item_s > 7 Table arrowdev:sysconstraints > 6 Table sysmaster:syscolumns > > How do I reduce the number of extents to improve performance?? Here is a perl script I use to defrag tables and the script it uses to run the SQL under a transaction and only commit if no errors. These are supplied without warrenty of any kind. If they are useful I am glad. If they trash you're database, I or my company are not responsible. But please let me know of any bugs or improvements. #!/usr/local/bin/perl5 #defrag_table.pl - de-fragment an informix table #This script is supplied without warrenty of any kind. If it is useful I am glad. #If it trashes your database, I or my company are not responsible. $usage="Usage: defrag_table.pl [-u|-d] [-s] [-n numrecs] database tablename tempdir\n\t-u = do not unload the data (assumes tempdir/tablename.unl exists)\n\t-d = delete the unload file at end\n\t-s = produce script file:tempdir/tablename.sql - do NOT run it\n\t-n x = use x as the number of records\n"; # runs the SQL file in a transaction and only commits of no errors #------------------------------------------------------------------------------- # check args #------------------------------------------------------------------------------- require ('getopts.pl'); &Getopts('udsn:'); if ( $#ARGV != 2 ) { die $usage; } #preset do unload and do NOT delete $do_delete=0; $do_unload=1; $do_script=0; #check for -s option if ( $opt_s ) { $do_script=1; $do_delete=0; } else { #check for -u option if ( $opt_u ) { $do_unload=0; } elsif ( $opt_d ) { $do_delete=1; } } #check for -u option if ( $opt_n ) { $new_count=$opt_n; } else { $new_count=0; } #get other args $database=$ARGV[0]; $table=$ARGV[1]; $tempdir=$ARGV[2]; #check for existing unload file or writeable temp dir if (! $do_unload) { if ( ! -r "$tempdir/$table.unl" ) { die "Unload file: $tempdir/$table.unl not found\n"; } } elsif ( ! -d $tempdir or ! -w $tempdir ) { die "Temp dir: $tempdir must be a writeable directory with enough space\n"; } #------------------------------------------------------------------------------- # setup permanent or temporary script file #------------------------------------------------------------------------------- if ( $do_script ) { $tmpsql="$tempdir/$table.sql"; } else { $tmpsql="/tmp/dfg_$$.sql"; } #------------------------------------------------------------------------------- #setup exit handling #------------------------------------------------------------------------------- sub exithandler { # 1st argument is signal name local($sig) = @_; &cleanNdie("Caught a SIG$sig--shutting down"); exit(0); } #exit cleanup sub cleanNdie { local ($message) = @_; # 1st argument is message chomp $message; die $message."\n"; } $SIG{'INT'} = 'exithandler'; $SIG{'QUIT'} = 'exithandler'; #------------------------------------------------------------------------------- # take optional 3rd arg get the number of records for the table #------------------------------------------------------------------------------- if ( $new_count > 0 ) { $count=$new_count; } #get count from database else { $count=`dbaccess $database < 10000) { $extstuff='EXTENT SIZE 5120 NEXT SIZE 5120'; } elsif ( $ext > 5000) { $extstuff='EXTENT SIZE 5120 NEXT SIZE 2048'; } elsif ( $ext > 2000) { $extstuff='EXTENT SIZE 2048 NEXT SIZE 1024'; } elsif ( $ext > 1000) { $extstuff='EXTENT SIZE 1024 NEXT SIZE 256'; } elsif ( $ext > 500) { $extstuff='EXTENT SIZE 512 NEXT SIZE 128'; } elsif ( $ext > 100) { $extstuff='EXTENT SIZE 256 NEXT SIZE 64'; } elsif ( $ext > 50) { $extstuff='EXTENT SIZE 64 NEXT SIZE 16'; } else { $extstuff=''; } # add the unload info after the TABLE stuff $unlinfo="{ unload file name = $tempdir/$table.unl number of rows = $count }"; $schema =~ s/(\{ TABLE.*row size .*index size =.*\})/$1\n$unlinfo/; } else { die "Cannot establish column details\n"; } #------------------------------------------------------------------------------- # get the permissions and schema for the table #------------------------------------------------------------------------------- $permissions=''; $drop_indeces=''; open(PERM,"dbschema -t $table -p all -d $database|") || die "Cannot establish permissions\n"; while () { #look for current table only if ( /^(.*\s+(?:on|from)\s+.*\b$table\s+to\s+.*;)/ ) { $permissions .= $1."\n"; } elsif ( /^create.*index\s+(.*) on .*/ ) { #look for any indeces to drop $drop_indeces .= "DROP INDEX $1;\n"; } } close(PERM); #------------------------------------------------------------------------------- # create the new sql #------------------------------------------------------------------------------- $rn_table=$table.'X'; $new_sql = "BEGIN WORK;\n"; $new_sql .= "SET LOCK MODE TO WAIT 300;\n"; $new_sql .= "LOCK TABLE $table IN EXCLUSIVE MODE;\n"; $new_sql .= "\n{Move the table to one side}\nRENAME TABLE $table TO $rn_table;\n"; $new_sql .= "\n{Unload the data - quicker than table to table}\nUNLOAD TO $tempdir/$table.unl SELECT * FROM $rn_table;\n" if $do_unload; $new_sql .= "\n{Drop the indexes now before creating copy table}\n".$drop_indeces."\n"; #add extent info and the load from file $loadstuff="\n\n{Reload the data} LOAD FROM $tempdir\/$table.unl INSERT INTO $table; SELECT 1 id,count(*) numrecords FROM $rn_table INTO TEMP tmp_countorig; SELECT 1 id,count(*) numrecords FROM $table INTO TEMP tmp_countnew; SELECT 'FORCED ERROR' info from tmp_countorig o,tmp_countnew n WHERE o.id=n.id AND o.numrecords <> n.numrecords; \n"; $schema =~ s/^\s*\);\s*$/ ) $extstuff LOCK MODE ROW;$loadstuff/; $new_sql .= $schema; $new_sql .= "{Now drop the original table}\nDROP TABLE $rn_table;\n"; #------------------------------------------------------------------------------- # open script file #------------------------------------------------------------------------------- open (SQL_S,">$tmpsql") || &cleanNdie("Cannot open $tmpsql for writing\n"); print SQL_S $new_sql; print SQL_S $permissions; close(SQL_S); #------------------------------------------------------------------------------- # either print or run sql script #------------------------------------------------------------------------------- if ( $do_script ) { print "script file:$tmpsql created\n"; } else { print STDOUT "Dropping and recreating table using the following sql:\n----------------------------------------------------\n$new_sql\n\napply table permissions\n...\n "; if ( system("/usr/local/bin/commit_ifok.pl $database $tmpsql 3000") != 0 ) { &cleanNdie("*ERROR* SQL error on $database while running: $tmpsql\n"); } unlink $tmpsql; } #cleanup unlink $tempdir.'/'.$table.'.unl' if $do_delete; #eof #!/usr/local/bin/perl5 # commit_ifok.pl - script to run an SQL file and only commit if no errors #This script is supplied without warrenty of any kind. If it is useful I am glad. #If it trashes your database, I or my company are not responsible. $usage="Usage: commit_ifok.pl [-W] database sqlfile [wait_in_secs] -W = wait until backups are finished\n"; use strict 'subs'; use strict 'refs'; #------------------------------------------------------------------------------- #check for -W argument (wait for backups) #------------------------------------------------------------------------------- if ( $ARGV[0] eq '-W' ) { $check_backups=1; shift; } else { $check_backups=0; } #------------------------------------------------------------------------------- #check args #------------------------------------------------------------------------------- if ( $#ARGV != 1 && $#ARGV != 2 ) { die $usage; } $dbname=$ARGV[0]; $sqlfile=$ARGV[1]; #------------------------------------------------------------------------------- #get optional wait time (3rd arg) #------------------------------------------------------------------------------- if ( $ARGV[2] ) { $sleepsupplied=1; $sleeptime=$ARGV[2]; } else { $sleepsupplied=0; $sleeptime=600; } #------------------------------------------------------------------------------- #setup temporary files #------------------------------------------------------------------------------- $tmp="/tmp/cok$$"; $tmpfin="$tmp.fin"; $tmpout="$tmp.out"; umask(000); unlink $tmpfin; unlink $tmpout; #------------------------------------------------------------------------------- #exit cleanup subroutine #------------------------------------------------------------------------------- sub cleanNdie { # 1st argument is message local($message) = @_; #close the pipe close(PIPE); close(SQLFILE); close(OUTFILE); unlink $tmpfin if $tmpfin; unlink $tmpout if $tmpout; print STDERR "\n$message\n"; exit(1); } #------------------------------------------------------------------------------- #setup exit handling for ^C etc #------------------------------------------------------------------------------- sub exithandler { # 1st argument is signal name local($sig) = @_; &cleanNdie("Caught a SIG$sig--shutting down"); exit(0); } $SIG{'INT'} = \&exithandler; $SIG{'QUIT'} = \&exithandler; #------------------------------------------------------------------------------- #open sql file for reading #------------------------------------------------------------------------------- if ( ! -s $sqlfile || ! open(SQLFILE,"<$sqlfile") ) { die "Cannot open $sqlfile or it is empty\n"; } #------------------------------------------------------------------------------- # ensure stderr has forces flushing #------------------------------------------------------------------------------- select(STDERR); $|=1; #force flushing #------------------------------------------------------------------------------- #check if backups are running and suspend #------------------------------------------------------------------------------- if ( $check_backups ) { $trytime=180; #try for three hours $wait_time=60; #wait for XX secs per iteration $message_times=5; #print message every XX waits #check the dbpath variable for remote server $DBPATH=$ENV{'DBPATH'}; if ( $ENV{'DBPATH'} gt " " ) { $server=$DBPATH; $server=~ s/^\/+//; #remove leading slashes $rsh='rsh '.$server; } else { $rsh=''; } $tryit=0; $message=$message_times; $running=1; while ($running && ++$tryit < $trytime ) { $ps_info=`$rsh ps -ef`; if ( $ps_info !~ /tbtape -s/m ) { $running=0; } else { if ( ++$message > $message_times ) { print STDERR " Waiting for backup to finish\n"; $message=1; } sleep($wait_time); #wait for one minute } } #die if backups still running if ( $running ) { print STDERR "*ERROR* Backups still running after $trytime minutes - aborting\n"; exit 1; } } #------------------------------------------------------------------------------- #run dbaccess via a pipe #------------------------------------------------------------------------------- open(PIPE,"|dbaccess $dbname > $tmpout 2>&1 ") || &cleanNdie("Cannot run dbaccess"); #------------------------------------------------------------------------------- #write the intiial transaction #------------------------------------------------------------------------------- select(PIPE); $|=1; #force flushing print PIPE "BEGIN WORK;\n" || &cleanNdie("Cannot output to dbaccess"); #------------------------------------------------------------------------------- #read each line in file and write to pipe unless begin/commit work #------------------------------------------------------------------------------- while () { $line=$_; #delete any begin/commit works $line =~ s/(BEGIN|COMMIT)\s+WORK(\s*;)*//i; select(PIPE); $|=1; #force flushing print PIPE $line || &cleanNdie("Cannot output to dbaccess"); #clockup sleep counter - allow 40 lines per sec if( ! $sleepsupplied && ++$numlines > 40 ) { $sleeptime += 1; $numlines=0; #reset counter } } close(SQLFILE); #------------------------------------------------------------------------------- #command to ensure buffers flushed by dbaccess #------------------------------------------------------------------------------- select(PIPE); $|=1; #force flushing print PIPE "CREATE TEMP TABLE TMP_cok$$(foo date,bar date);\n"; select(PIPE); $|=1; #force flushing print PIPE "INSERT INTO TMP_cok$$ values(TODAY,TODAY);\n"; select(PIPE); $|=1; #force flushing print PIPE "UNLOAD TO $tmpfin SELECT 'END_OF_COMP_CHECKING',* FROM TMP_cok$$;\n"; sleep(5); #let everything settle down #------------------------------------------------------------------------------- #wait for dbaccess to finish processing data #------------------------------------------------------------------------------- select(STDERR); $|=1; #force flushing $numtrys=0; print STDERR " All SQL sent to dbaccess - waiting... "; while ( ! -f $tmpfin && $numtrys < $sleeptime ) { sleep(1); ++$numtrys; } if ( -f $tmpfin && $numtrys == 0 ) { print STDERR "Finished "; } elsif ( -f $tmpfin ) { print STDERR "Finished after $numtrys secs "; } else { &cleanNdie("*ERROR* not finished within time limit ($sleeptime secs) - aborting"); } #------------------------------------------------------------------------------- #now look for errors in the output file #------------------------------------------------------------------------------- open (OUTFILE,"<$tmpout") || &cleanNdie("Cannot open dbaccess output"); $found_err=0; $numfound=0; $last_line=''; nextout: while () { ++$numfound; #debug display the errors found; chop; if ( /^ 256: Transaction not available/) { $found_err=-1; #not much we can do about this last nextout; } #check for errors - NOTE 'FORCED ERROR' can be used within select to force an error elsif ( /^Error in line/ || /FORCED ERROR/ || /ISAM error/ || /^\s*[0-9]+:\s+.*/ ) { if ( ! $found_err ) { $error_line=$last_line."\n".$_; $found_err=1; } last nextout; } elsif ( /END_OF_COMP_CHECKING/ ) { last nextout; } $last_line=$_; } if ( $numfound < 1 ) { print STDERR "No output found\n"; } elsif ( $found_err == 1 ) { select(PIPE); $|=1; #force flushing print PIPE "ROLLBACK WORK;\n" || &cleanNdie("Cannot output to dbaccess"); &cleanNdie("*SQL ERROR* - ROLLED BACK\n$error_line"); } elsif ( ! $found_err ) { select(PIPE); $|=1; #force flushing print PIPE "COMMIT WORK;\n" || &cleanNdie("Cannot output to dbaccess"); #print STDOUT "Committing Transaction\n"; } #------------------------------------------------------------------------------- #exit cleanly and remove temporary files #------------------------------------------------------------------------------- close(PIPE); close(OUTFILE); close(SQLFILE); unlink $tmpfin if $tmpfin; unlink $tmpout if $tmpout; print STDOUT "RAN OK\n"; exit(0); #eof -- # John Clutterbuck Tel: +44 1703 765021 # Siemens Business Services email: jclutterbuck@sbs.siemens.co.uk # Phi House, Enterprise Road # Chilworth, Hampshire, SO16 7NS