diff --git a/storage/ndb/tools/Makefile.am b/storage/ndb/tools/Makefile.am index 7480b9a2ae9..de0f36963bf 100644 --- a/storage/ndb/tools/Makefile.am +++ b/storage/ndb/tools/Makefile.am @@ -14,7 +14,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA dist_bin_SCRIPTS = ndb_size.pl ndb_error_reporter -dist_pkgdata_DATA = ndb_size.tmpl +dist_pkgdata_DATA = ndbtools_PROGRAMS = \ ndb_test_platform \ diff --git a/storage/ndb/tools/ndb_size.pl b/storage/ndb/tools/ndb_size.pl index 3d1ea3f4231..4ecdc297cd3 100644 --- a/storage/ndb/tools/ndb_size.pl +++ b/storage/ndb/tools/ndb_size.pl @@ -1,17 +1,29 @@ #!/usr/bin/perl -w +# Copyright (C) 2005-2007 MySQL AB +# +# 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; version 2 of the License. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + use strict; use DBI; use POSIX; -use HTML::Template; +use Getopt::Long; # MySQL Cluster size estimator # ---------------------------- # -# (C)2005 MySQL AB -# -# # The purpose of this tool is to work out storage requirements # from an existing MySQL database. # @@ -25,157 +37,578 @@ use HTML::Template; # # BUGS # ---- -# - enum/set is 0 byte storage! Woah - efficient! # - DECIMAL is 0 byte storage. A bit too efficient. # - some float stores come out weird (when there's a comma e.g. 'float(4,1)') # - no disk data values # - computes the storage requirements of views (and probably MERGE) -# - ignores character sets. +# - ignores character sets? -my $template = HTML::Template->new(filename => 'ndb_size.tmpl', - die_on_bad_params => 0) - or die "Could not open ndb_size.tmpl."; +package MySQL::NDB::Size::Parameter; -my $dbh; +use Class::MethodMaker [ + scalar => 'name', + scalar => 'default', + scalar => 'mem_per_item', + scalar => [{ -default => '' },'unit'], + hash => [ qw ( value + ver_mem_per_item + mem_total ) ], + new => [ -hash => 'new' ], + ]; -if(@ARGV < 3 || $ARGV[0] eq '--usage' || $ARGV[0] eq '--help') +1; + +package MySQL::NDB::Size::Report; + +use Class::MethodMaker [ + scalar => [ qw( database + dsn ) ], + array => 'versions', + hash => [qw( tables + parameters + supporting_tables) ], + new => [ -hash => 'new' ], + ]; +1; + +package MySQL::NDB::Size::Column; + +use Class::MethodMaker [ + new => [ -hash => 'new' ], + scalar => [ qw( name + type + is_varsize + size + Key) ], + hash => 'dm_overhead', + scalar => [{ -default => 4 },'align'], + scalar => [{ -default => 0 },'null_bits'], + ]; + +# null_bits: +# 0 if not nullable, 1 if nullable +# + additional if bitfield as these are stored in the null bits +# if is_varsize, null_bits are in varsize part. + +# dm is default DataMemory value. Automatically 4byte aligned +# ver_dm is DataMemory value for specific versions. +# an entry in ver_dm OVERRIDES the dm value. +# e.g. if way column stored changed in new version. +# +# if is_varsize, dm/ver_dm is in varsized part. + +sub ver_dm_exists +{ + my ($self,$ver)= @_; + return exists($self->{ver_dm}{$ver}); +} + +use Data::Dumper; +sub ver_dm +{ + my ($self,$ver,$val)= @_; + if(@_ > 2) + { + $self->{ver_dm}{$ver}= + $self->align * POSIX::floor(($val+$self->align-1)/$self->align); + } + return $self->{ver_dm}{$ver}; +} + +sub dm +{ + my ($self,$val)= @_; + if(@_ > 1) + { + $self->{dm}= + $self->align * POSIX::floor(($val+$self->align-1)/$self->align) + } + return $self->{dm}; +} + +package MySQL::NDB::Size::Index; + +use Class::MethodMaker [ + new => [ -hash => 'new' ], + hash => [ qw( ver_dm + ver_im ) ], + scalar => [ qw( name + type + comment + columns + unique + dm + im) ], + scalar => [ { -default=> 4 },'align'], + scalar => [ { -default=> 0 },'is_supporting_table' ], + ]; + +package MySQL::NDB::Size::Table; + +# The following are computed by compute_row_size: +# row_dm_size DataMemory Size per row +# row_vdm_size Varsized DataMemory Size per row +# row_ddm_size Disk Data size per row (on disk size) +# +# These are hashes of versions. If an entry in (dm|vdm|ddm)_versions exists, +# then this parameter is calculated. +# +# Specific per-row overhead is in row_(dm|vdm|ddm)_overhead. +# e.g. if there is a varsized column, we have a vdm overhead for the +# varsized part of the row, otherwise vdm_size==0 + +# Any supporting tables - e.g. BLOBs have their name in supporting_tables +# These tables should then be looked up in the main report object. +# The main report object also has a supporting_tables hash used for +# excluding these from the main list of tables. +use POSIX; +use Class::MethodMaker [ + new => [ -hash => 'new' ], + array => [ qw( supporting_tables + dm_versions + vdm_versions + ddm_versions ) ], + scalar => [ qw( name + rows ) ], + hash => [ qw( columns + indexes + indexed_columns + row_im_size + row_dm_size + row_vdm_size + row_dm_overhead + row_vdm_overhead + row_ddm_overhead) ], + scalar => [ { -default=> 8192 },'im_pagesize'], + scalar => [ { -default=> 0 },'im_page_overhead'], + scalar => [ { -default=> 32768 },'dm_pagesize' ], + scalar => [ { -default=> 128 },'dm_page_overhead' ], + scalar => [ { -default=> 32768 },'vdm_pagesize' ], + scalar => [ { -default=> 128 },'vdm_page_overhead' ], + hash => [ # these are computed + qw( + dm_null_bytes + vdm_null_bytes + dm_needed + vdm_needed + im_needed + im_rows_per_page + dm_rows_per_page + vdm_rows_per_page) ], + scalar => [ { -default=> 4 },'align'], + ]; + +sub compute_row_size +{ + my ($self, $releases) = @_; + + my %row_dm_size; + my %row_vdm_size; + my %row_im_size; + my %dm_null_bits; + my %vdm_null_bits; + my $no_varsize= 0; + + foreach my $c (keys %{$self->columns}) + { + if($self->columns->{$c}->is_varsize) + { + $no_varsize++; + foreach my $ver ($self->vdm_versions) + { + if($self->columns->{$c}->ver_dm_exists($ver)) + { + $row_vdm_size{$ver}+= $self->columns->{$c}->ver_dm($ver); + $vdm_null_bits{$ver}+= $self->columns->{$c}->null_bits(); + } + else + { + $row_vdm_size{$ver}+= $self->columns->{$c}->dm; + $vdm_null_bits{$ver}+= $self->columns->{$c}->null_bits(); + } + } + } + foreach my $ver ($self->dm_versions) + { + if($self->columns->{$c}->ver_dm_exists($ver)) + { + next if $self->columns->{$c}->is_varsize; + $row_dm_size{$ver}+= $self->columns->{$c}->ver_dm($ver); + $dm_null_bits{$ver}+= $self->columns->{$c}->null_bits(); + } + else + { + $row_dm_size{$ver}+= $self->columns->{$c}->dm||0; + $dm_null_bits{$ver}+= $self->columns->{$c}->null_bits()||0; + } + } + } + + foreach ($self->row_dm_overhead_keys()) + { + $row_dm_size{$_}+= $self->row_dm_overhead->{$_} + if exists($row_dm_size{$_}); + } + + + foreach ($self->row_vdm_overhead_keys()) + { + $row_vdm_size{$_}+= $self->row_vdm_overhead->{$_} + if exists($row_vdm_size{$_}); + } + + + # now we compute size of indexes for dm + foreach my $i (keys %{$self->indexes}) + { + foreach my $ver ($self->dm_versions) + { + $row_dm_size{$ver}+= $self->indexes->{$i}->dm() || 0; + } + } + + # now we compute size of indexes for im + while(my ($iname, $i) = $self->indexes_each()) + { + foreach my $ver ($self->dm_versions) + { + if($i->ver_im_exists($ver)) + { + $row_im_size{$ver}+= $i->ver_im->{$ver}; + } + else + { + $row_im_size{$ver}+= $i->im() || 0; + } + } + } + + # 32-bit align the null part + foreach my $k (keys %dm_null_bits) + { + $dm_null_bits{$k}= + $self->align * POSIX::floor((ceil($dm_null_bits{$k}/8)+$self->align-1) + /$self->align); + } + + foreach my $k (keys %vdm_null_bits) + { + $vdm_null_bits{$k}= + $self->align * POSIX::floor((ceil($vdm_null_bits{$k}/8)+$self->align-1) + /$self->align); + } + + # Finally set things + $self->dm_null_bytes(%dm_null_bits); + $self->vdm_null_bytes(%vdm_null_bits); + + # add null bytes to dm/vdm size + foreach my $k (keys %row_dm_size) + { + $row_dm_size{$k}+=$dm_null_bits{$k}||0; + } + + foreach my $k (keys %row_vdm_size) + { + $row_vdm_size{$k}+=$vdm_null_bits{$k}||0; + } + + $self->row_dm_size(%row_dm_size); + $self->row_vdm_size(%row_vdm_size); + $self->row_im_size(%row_im_size); +} + +sub compute_estimate +{ + my ($self) = @_; + + foreach my $ver (@{$self->dm_versions}) + { + $self->dm_rows_per_page_set($ver => + floor( + ($self->dm_pagesize - $self->dm_page_overhead) + / + $self->row_dm_size->{$ver} + ) + ); + } + + foreach my $ver (@{$self->vdm_versions}) + { + next if ! $self->row_vdm_size_exists($ver); + $self->vdm_rows_per_page_set($ver => + floor( + ($self->vdm_pagesize - $self->vdm_page_overhead) + / + $self->row_vdm_size->{$ver} + ) + ); + } + + $self->im_page_overhead(0) if !$self->im_page_overhead(); + foreach my $ver (@{$self->dm_versions}) + { + $self->im_rows_per_page_set($ver => + floor( + ($self->im_pagesize - $self->im_page_overhead) + / + $self->row_im_size->{$ver} + ) + ); + } + + $self->dm_needed_set($_ => $self->dm_pagesize() + * + POSIX::ceil( + $self->rows + / + ($self->dm_rows_per_page->{$_}) + ) + ) + foreach $self->dm_versions; + + $self->vdm_needed_set($_ => (!$self->vdm_rows_per_page->{$_})? 0 : + $self->vdm_pagesize() + * + POSIX::ceil( + $self->rows + / + ($self->vdm_rows_per_page->{$_}) + ) + ) + foreach $self->vdm_versions; + + $self->im_needed_set($_ => $self->im_pagesize() + * + POSIX::ceil( + $self->rows + / + ($self->im_rows_per_page->{$_}) + ) + ) + foreach $self->dm_versions; +} + +package main; + +my ($dbh,$database,$hostname,$user,$password,$help,$savequeries,$loadqueries,$debug,$format); + +GetOptions('database|d=s'=>\$database, + 'hostname=s'=>\$hostname, + 'user|u=s'=>\$user, + 'password|p=s'=>\$password, + 'savequeries|s=s'=>\$savequeries, + 'loadqueries|l=s'=>\$loadqueries, + 'help|usage|h!'=>\$help, + 'debug'=>\$debug, + 'format|f=s'=>\$format, + ); + +my $report= new MySQL::NDB::Size::Report; + +if($help || !$database) { print STDERR "Usage:\n"; - print STDERR "\tndb_size.pl database hostname user password\n\n"; - print STDERR "If you need to specify a port number, use host:port\n\n"; + print STDERR "\tndb_size.pl --database= [--hostname=]" + ."[--user=] [--password=] [--help|-h] [--format=(html|text)] [--loadqueries=] [--savequeries=]\n\n"; + print STDERR "\t--hostname=: can be used to designate a " + ."specific port\n"; + print STDERR "\t--hostname defaults to localhost\n"; + print STDERR "\t--user and --password default to empty string\n"; + print STDERR "\t--format=(html|text) Output format\n"; + print STDERR "\t--savequeries= saves all queries to the DB into \n"; + print STDERR "\t--loadqueries= loads query results from . Doesn't connect to DB.\n"; exit(1); } +$hostname= 'localhost' unless $hostname; + +my %queries; # used for loadqueries/savequeries + +if(!$loadqueries) { - my $database= $ARGV[0]; - my $hostname= $ARGV[1]; - my $user= $ARGV[2]; - my $password= $ARGV[3]; my $dsn = "DBI:mysql:database=$database;host=$hostname"; $dbh= DBI->connect($dsn, $user, $password) or exit(1); - $template->param(db => $database); - $template->param(dsn => $dsn); + $report->database($database); + $report->dsn($dsn); +} +else +{ + open Q,"< $loadqueries"; + my @q= ; + my $VAR1; + my $e= eval join("",@q) or die $@; + %queries= %$e; + close Q; + $report->database("file:$loadqueries"); + $report->dsn("file:$loadqueries"); } -my @releases = ({rel=>'4.1'},{rel=>'5.0'},{rel=>'5.1'}); #,{rel=>'5.1-dd'}); -$template->param(releases => \@releases); +$report->versions('4.1','5.0','5.1'); -my $tables = $dbh->selectall_arrayref("show tables"); +my $tables; -my @table_size; - -my @dbDataMemory; -my @dbIndexMemory; -my @NoOfAttributes; -my @NoOfIndexes; -my @NoOfTables; -$NoOfTables[$_]{val} = @{$tables} foreach 0..$#releases; - - -sub align { - my($to,@unaligned) = @_; - my @aligned; - foreach my $x (@unaligned) { - push @aligned, $to * POSIX::floor(($x+$to-1)/$to); - } - return @aligned; +if($loadqueries) +{ + $tables= $queries{"show tables"}; +} +else +{ + $tables= $dbh->selectall_arrayref("show tables"); + $queries{"show tables"}= $tables; } sub do_table { - my $table= shift; + my $t= shift; my $info= shift; my %indexes= %{$_[0]}; my @count= @{$_[1]}; - my @columns; - my %columnsize; # used for index calculations - # We now work out the DataMemory usage - - # sizes for 4.1, 5.0, 5.1 and 5.1-dd - my @totalsize= (0,0,0,0); - @totalsize= @totalsize[0..$#releases]; # limit to releases we're outputting - my $nrvarsize= 0; + $t->dm_versions($report->versions); + $t->vdm_versions('5.1'); + $t->ddm_versions('5.1'); - foreach(keys %$info) + foreach my $colname (keys %$info) { - my @realsize = (0,0,0,0); - my @varsize = (0,0,0,0); - my $type; - my $size; - my $name= $_; - my $is_varsize= 0; + my $col= new MySQL::NDB::Size::Column(name => $colname); + my ($type, $size); - if($$info{$_}{Type} =~ /^(.*?)\((\d+)\)/) + $col->Key($$info{$colname}{Key}) + if(defined($$info{$colname}{Key}) &&$$info{$colname}{Key} ne ''); + + $col->null_bits(defined($$info{$colname}{Null}) + && $$info{$colname}{Null} eq 'YES'); + + if(defined($$info{$colname}{Type}) + && $$info{$colname}{Type} =~ /^(.*?)\((.+)\)/) { $type= $1; $size= $2; } + elsif(exists($$info{$colname}{type})) + { + # we have a Column object.. + $type= $$info{$colname}{type}; + $size= $$info{$colname}{size}; + } else { - $type= $$info{$_}{Type}; + $type= $$info{$colname}{Type}; } + $col->type($type); + $col->size($size); if($type =~ /tinyint/) - {@realsize=(1,1,1,1)} + {$col->dm(1)} elsif($type =~ /smallint/) - {@realsize=(2,2,2,2)} + {$col->dm(2)} elsif($type =~ /mediumint/) - {@realsize=(3,3,3,3)} + {$col->dm(3)} elsif($type =~ /bigint/) - {@realsize=(8,8,8,8)} + {$col->dm(8)} elsif($type =~ /int/) - {@realsize=(4,4,4,4)} + {$col->dm(4)} elsif($type =~ /float/) { - if($size<=24) - {@realsize=(4,4,4,4)} + if(!defined($size) || $size<=24) + {$col->dm(4)} else - {@realsize=(8,8,8,8)} + {$col->dm(8)} } elsif($type =~ /double/ || $type =~ /real/) - {@realsize=(8,8,8,8)} + {$col->dm(8)} elsif($type =~ /bit/) { - my $a=($size+7)/8; - @realsize = ($a,$a,$a,$a); + # bitfields stored in null bits + $col->null_bits($size+($col->null_bits()||0)); } elsif($type =~ /datetime/) - {@realsize=(8,8,8,8)} + {$col->dm(8)} elsif($type =~ /timestamp/) - {@realsize=(4,4,4,4)} + {$col->dm(4)} elsif($type =~ /date/ || $type =~ /time/) - {@realsize=(3,3,3,3)} + {$col->dm(3)} elsif($type =~ /year/) - {@realsize=(1,1,1,1)} + {$col->dm(1)} + elsif($type =~ /enum/ || $type =~ /set/) + { + # I *think* this is correct.. + my @items= split ',',$size; + $col->dm(ceil((scalar @items)/256)); + } elsif($type =~ /varchar/ || $type =~ /varbinary/) { my $fixed=$size+ceil($size/256); - my @dynamic=$dbh->selectrow_array("select avg(length(`" - .$name - ."`)) from `".$table.'`'); - $dynamic[0]=0 if !$dynamic[0]; - $dynamic[0]+=ceil($dynamic[0]/256); # size bit - $nrvarsize++; - $is_varsize= 1; - $varsize[3]= ceil($dynamic[0]); - @realsize= ($fixed,$fixed,ceil($dynamic[0]),$fixed); + $col->dm_overhead_set('length' => ceil($size/256)); + $col->dm($fixed); + if(!$col->Key()) # currently keys must be non varsized + { + my $sql= "select avg(length(`" + .$colname + ."`)) from `".$t->name().'`'; + my @dynamic; + if($loadqueries) + { + @dynamic= @{$queries{$sql}}; + } + else + { + @dynamic= $dbh->selectrow_array($sql); + $queries{$sql}= \@dynamic; + } + $dynamic[0]=0 if ! defined($dynamic[0]) || !@dynamic; + $dynamic[0]+=ceil($size/256); # size bit + $col->is_varsize(1); + $col->ver_dm('5.1',ceil($dynamic[0])); + } } elsif($type =~ /binary/ || $type =~ /char/) - {@realsize=($size,$size,$size,$size)} + {$col->dm($size)} elsif($type =~ /text/ || $type =~ /blob/) { - @realsize=(8+256,8+256,8+256,8+256); + $col->dm_overhead_set('length' => 8); + $col->dm(8+256); my $blobhunk= 2000; $blobhunk= 8000 if $type=~ /longblob/; $blobhunk= 4000 if $type=~ /mediumblob/; - my @blobsize=$dbh->selectrow_array("select SUM(CEILING(". - "length(`$name`)/$blobhunk))". - "from `".$table."`"); + my $sql= "select SUM(CEILING(". + "length(`$colname`)/$blobhunk))" + ."from `".$t->name."`"; + my @blobsize; + if($loadqueries) + { + @blobsize= @{$queries{$sql}}; + } + else + { + @blobsize= $dbh->selectrow_array($sql); + $queries{$sql}= \@blobsize; + } $blobsize[0]=0 if !defined($blobsize[0]); - #$NoOfTables[$_]{val} += 1 foreach 0..$#releases; # blob uses table - do_table($table."\$BLOB_$name", + + # Is a supporting table, add it to the lists: + $report->supporting_tables_set($t->name()."\$BLOB_$colname" => 1); + $t->supporting_tables_push($t->name()."\$BLOB_$colname"); + + my $st= new MySQL::NDB::Size::Table(name => + $t->name()."\$BLOB_$colname", + rows => $blobsize[0], + row_dm_overhead => + { '4.1' => 12, + '5.0' => 12, + '5.1' => 16, + }, + row_vdm_overhead => + { '5.1' => 8 }, + row_ddm_overhead => + { '5.1' => 8 }, + ); + + + + do_table($st, {'PK'=>{Type=>'int'}, 'DIST'=>{Type=>'int'}, 'PART'=>{Type=>'int'}, @@ -195,26 +628,12 @@ sub do_table { \@blobsize); } - @realsize= @realsize[0..$#releases]; - @realsize= align(4,@realsize); - - $totalsize[$_]+=$realsize[$_] foreach 0..$#totalsize; - - my @realout; - push @realout,{val=>$_} foreach @realsize; - - push @columns, { - name=>$name, - type=>$type, - is_varsize=>$is_varsize, - size=>$size, - key=>$$info{$_}{Key}, - datamemory=>\@realout, - }; - - $columnsize{$name}= \@realsize; # used for index calculations + $col->type($type); + $col->size($size); + $t->columns_set( $colname => $col ); } - + $report->tables_set( $t->name => $t ); + # And now... the IndexMemory usage. # # Firstly, we assemble some information about the indexes. @@ -222,170 +641,1058 @@ sub do_table { # we can still connect to pre-5.0 mysqlds. if(!defined($indexes{PRIMARY})) { - my @usage= ({val=>8},{val=>8},{val=>8},{val=>8}); - @usage= @usage[0..$#releases]; - $indexes{PRIMARY}= { - type=>'BTREE', - unique=>1, - comment=>'Hidden pkey created by NDB', - columns=>['HIDDEN_NDB_PKEY'], - }; - push @columns, { - name=>'HIDDEN_NDB_PKEY', - type=>'bigint', - size=>8, - key=>'PRI', - datamemory=>\@usage, - }; - $columnsize{'HIDDEN_NDB_PKEY'}= [8,8,8]; - } + my $i= new MySQL::NDB::Size::Index( + name => 'PRIMARY', + unique => 1, + comment =>'Hidden pkey created by NDB', + type =>'BTREE', + columns => ['HIDDEN_NDB_PKEY'], + ); - my @IndexDataMemory= ({val=>0},{val=>0},{val=>0},{val=>0}); - my @RowIndexMemory= ({val=>0},{val=>0},{val=>0},{val=>0}); - @IndexDataMemory= @IndexDataMemory[0..$#releases]; - @RowIndexMemory= @RowIndexMemory[0..$#releases]; + $i->im(16); + $i->dm(16); + $i->ver_im('4.1',25+8); + + $t->indexes_set('PRIMARY' => $i); + $t->indexed_columns_set('HIDDEN_NDB_PKEY' => 1); + + $t->columns_set('HIDDEN_NDB_PKEY' => + new MySQL::NDB::Size::Column( + name => 'HIDDEN_NDB_PKEY', + type => 'bigint', + dm => 8, + Key => 'PRI')); + } my @indexes; - foreach my $index (keys %indexes) { - my $im41= 25; - $im41+=$columnsize{$_}[0] foreach @{$indexes{$index}{columns}}; - my @im = ({val=>$im41},{val=>25},{val=>25}); #,{val=>25}); - my @dm = ({val=>10},{val=>10},{val=>10}); #,{val=>10}); - push @indexes, { - name=>$index, - type=>$indexes{$index}{type}, - columns=>join(',',@{$indexes{$index}{columns}}), - indexmemory=>\@im, - datamemory=>\@dm, - }; - $IndexDataMemory[$_]{val}+=$dm[$_]{val} foreach 0..$#releases; - $RowIndexMemory[$_]{val}+=$im[$_]{val} foreach 0..$#releases; - } - # total size + 16 bytes overhead - my @TotalDataMemory; - my @RowOverhead = ({val=>16},{val=>16},{val=>16}); #,{val=>24}); - # 5.1 has ptr to varsize page, and per-varsize overhead - my @nrvarsize_mem= ({val=>0},{val=>0}, - {val=>8}); #,{val=>0}); + # We model the PRIMARY first as needed for secondary uniq indexes + if(defined($indexes{'PRIMARY'})) { - my @a= align(4,$nrvarsize*2); - $nrvarsize_mem[2]{val}+=$a[0]+$nrvarsize*4; + my $index= 'PRIMARY'; + my $i= new MySQL::NDB::Size::Index( + name => $index, + unique => $indexes{$index}{unique}, + comment => $indexes{$index}{comment}, + type => $indexes{$index}{type}, + columns => [@{$indexes{$index}{columns}}], + ); + my $im41= 25; # keep old estimate for 4.1 + $im41+= $t->columns->{$_}->dm foreach @{$indexes{$index}{columns}}; + $i->im(16); # estimate from Johan + $i->dm(16) if $indexes{$index}{unique}; # estimate from Johan + $i->ver_im('4.1',$im41); + + $t->indexes_set($index => $i); + $t->indexed_columns_set($_ => 1) + foreach @{$indexes{$index}{columns}}; } - $TotalDataMemory[$_]{val}=$IndexDataMemory[$_]{val}+$totalsize[$_]+$RowOverhead[$_]{val}+$nrvarsize_mem[$_]{val} foreach 0..$#releases; + foreach my $index (keys %indexes) { + next if $index eq 'PRIMARY'; - my @RowDataMemory; - push @RowDataMemory,{val=>$_} foreach @totalsize; - - my @RowPerPage; - push @RowPerPage,{val=>(floor((32768-128)/$TotalDataMemory[$_]{val}))} foreach 0..$#TotalDataMemory; + if(!$indexes{$index}{unique}) + { + my $i= new MySQL::NDB::Size::Index( + name => $index, + unique => $indexes{$index}{unique}, + comment => $indexes{$index}{comment}, + type => $indexes{$index}{type}, + columns => [@{$indexes{$index}{columns}}], + ); + $i->dm(16); + $t->indexes_set($index => $i); + $t->indexed_columns_set($_ => 1) + foreach @{$indexes{$index}{columns}}; + } + else + { + my $i= new MySQL::NDB::Size::Index( + name => $index, + unique => $indexes{$index}{unique}, + comment => $indexes{$index}{comment}, + type => $indexes{$index}{type}, + columns => [@{$indexes{$index}{columns}}, + @{$t->indexes->{'PRIMARY'}->columns()}], + ); - my @RowPerIndexPage; - push @RowPerIndexPage,{val=>(floor(8192/$RowIndexMemory[$_]{val}))} foreach 0..$#TotalDataMemory; + $i->is_supporting_table(1); + $t->indexes_set($index => $i); - my @DataMemory; - push @DataMemory,{val=>ceil(($count[0]/($RowPerPage[$_]{val})))*32} foreach 0..$#RowPerPage; + my %idxcols; + foreach(@{$i->columns()}) + { + $idxcols{$_} = $t->columns->{$_} + } + # Is a supporting table, add it to the lists: + my $idxname= $t->name().'_'.join('_',@{$indexes{$index}{columns}}). + "\$unique"; + $report->supporting_tables_set($idxname => 1); + $t->supporting_tables_push($idxname); - my @IndexMemory; - push @IndexMemory,{val=>ceil(($count[0]/($RowPerIndexPage[$_]{val})))*8} foreach 0..$#RowPerPage; + $t->indexed_columns_set($_ => 1) + foreach @{$indexes{$index}{columns}}; - my $count= $count[0]; - my @counts; - $counts[$_]{val}= $count foreach 0..$#releases; + my $st= new MySQL::NDB::Size::Table(name => $idxname, + rows => $count[0], + row_dm_overhead => + { '4.1' => 12, + '5.0' => 12, + '5.1' => 16+4, + }, + row_vdm_overhead => + { '5.1' => 8 }, + row_ddm_overhead => + { '5.1' => 8 }, + ); - my @nrvarsize_rel= ({val=>0},{val=>0}, - {val=>$nrvarsize}); #,{val=>0}); + do_table($st, + \%idxcols, + { + 'PRIMARY' => { + 'unique' => 0,#$indexes{$index}{unique}, + 'columns' => [@{$indexes{$index}{columns}}], + 'type' => 'BTREE', + } + }, + \@count); + } + } - push @table_size, { - table=>$table, - indexes=>\@indexes, - columns=>\@columns, - count=>\@counts, - RowOverhead=>\@RowOverhead, - RowDataMemory=>\@RowDataMemory, - nrvarsize=>\@nrvarsize_rel, - nrvarsize_mem=>\@nrvarsize_mem, - releases=>\@releases, - IndexDataMemory=>\@IndexDataMemory, - TotalDataMemory=>\@TotalDataMemory, - RowPerPage=>\@RowPerPage, - DataMemory=>\@DataMemory, - RowIndexMemory=>\@RowIndexMemory, - RowPerIndexPage=>\@RowPerIndexPage, - IndexMemory=>\@IndexMemory, - - }; + $t->compute_row_size($report->versions); - $dbDataMemory[$_]{val} += $DataMemory[$_]{val} foreach 0..$#releases; - $dbIndexMemory[$_]{val} += $IndexMemory[$_]{val} foreach 0..$#releases; - $NoOfAttributes[$_]{val} += @columns foreach 0..$#releases; - $NoOfIndexes[$_]{val} += @indexes foreach 0..$#releases; -} +} # do_table foreach(@{$tables}) { my $table= @{$_}[0]; - my $info= $dbh->selectall_hashref('describe `'.$table.'`',"Field"); - my @count = $dbh->selectrow_array('select count(*) from `'.$table.'`'); + my $info; + { + my $sql= 'describe `'.$table.'`'; + if($loadqueries) + { + $info= $queries{$sql}; + } + else + { + $info= $dbh->selectall_hashref($sql,"Field"); + $queries{$sql}= $info; + } + } + my @count; + { + my $sql= 'select count(*) from `'.$table.'`'; + if($loadqueries) + { + @count= @{$queries{$sql}}; + } + else + { + @count= $dbh->selectrow_array($sql); + $queries{$sql}= \@count; + } + } my %indexes; { - my $sth= $dbh->prepare("show index from `".$table.'`'); - $sth->execute; - while(my $i = $sth->fetchrow_hashref) - { + my @show_indexes; + { + my $sql= "show index from `".$table.'`'; + if($loadqueries) + { + @show_indexes= @{$queries{$sql}}; + } + else + { + my $sth= $dbh->prepare($sql); + $sth->execute; + while(my $i = $sth->fetchrow_hashref) + { + push @show_indexes, $i; + } + $queries{$sql}= \@show_indexes; + } + } + foreach my $i(@show_indexes) + { $indexes{${%$i}{Key_name}}= { type=>${%$i}{Index_type}, unique=>!${%$i}{Non_unique}, comment=>${%$i}{Comment}, } if !defined($indexes{${%$i}{Key_name}}); - $indexes{${%$i}{Key_name}}{columns}[${%$i}{Seq_in_index}-1]= + $indexes{${%$i}{Key_name}}{columns}[${%$i}{Seq_in_index}-1]= ${%$i}{Column_name}; } } - do_table($table, $info, \%indexes, \@count); + my $t= new MySQL::NDB::Size::Table(name => $table, + rows => $count[0], + row_dm_overhead => + { '4.1' => 12, + '5.0' => 12, + '5.1' => 16, + }, + row_vdm_overhead => { '5.1' => 8 }, + row_ddm_overhead => { '5.1' => 8 }, + ); + + + do_table($t, $info, \%indexes, \@count); } -my @NoOfTriggers; -# for unique hash indexes -$NoOfTriggers[$_]{val} += $NoOfIndexes[$_]{val}*3 foreach 0..$#releases; -# for ordered index -$NoOfTriggers[$_]{val} += $NoOfIndexes[$_]{val} foreach 0..$#releases; - -my @ParamMemory; -foreach (0..$#releases) { - $ParamMemory[0]{releases}[$_]{val}= POSIX::ceil(200*$NoOfAttributes[$_]{val}/1024); - $ParamMemory[0]{name}= 'Attributes'; - - $ParamMemory[1]{releases}[$_]{val}= 20*$NoOfTables[$_]{val}; - $ParamMemory[1]{name}= 'Tables'; - - $ParamMemory[2]{releases}[$_]{val}= 10*$NoOfIndexes[$_]{val}; - $ParamMemory[2]{name}= 'OrderedIndexes'; - - $ParamMemory[3]{releases}[$_]{val}= 15*$NoOfIndexes[$_]{val}; - $ParamMemory[3]{name}= 'UniqueHashIndexes'; +# compute table estimates +while(my ($tname,$t)= $report->tables_each()) +{ + $t->compute_estimate(); } -$template->param(tables => \@table_size); -$template->param(Parameters => [{name=>'DataMemory (kb)', - releases=>\@dbDataMemory}, - {name=>'IndexMemory (kb)', - releases=>\@dbIndexMemory}, - {name=>'MaxNoOfTables', - releases=>\@NoOfTables}, - {name=>'MaxNoOfAttributes', - releases=>\@NoOfAttributes}, - {name=>'MaxNoOfOrderedIndexes', - releases=>\@NoOfIndexes}, - {name=>'MaxNoOfUniqueHashIndexes', - releases=>\@NoOfIndexes}, - {name=>'MaxNoOfTriggers', - releases=>\@NoOfTriggers} - ] - ); -$template->param(ParamMemory => \@ParamMemory); +# Now parameters.... -print $template->output; +$report->parameters_set('NoOfTables' => + new MySQL::NDB::Size::Parameter(name=>'NoOfTables', + mem_per_item=>20, + default=>128) + ); + +$report->parameters->{'NoOfTables'}->value_set($_ => scalar @{$report->tables_keys()}) + foreach $report->versions; + +$report->parameters_set('NoOfAttributes' => + new MySQL::NDB::Size::Parameter(name=>'NoOfAttributes', + mem_per_item=>0.2, + default=>1000) + ); + +{ + my $attr= 0; + while(my ($tname,$t)= $report->tables_each()) + { + $attr+= scalar @{$t->columns_keys()}; + } + $report->parameters->{'NoOfAttributes'}->value_set($_ => $attr) + foreach $report->versions; +} + + +$report->parameters_set('NoOfOrderedIndexes' => + new MySQL::NDB::Size::Parameter(name=>'NoOfOrderedIndexes', + mem_per_item=>10, + default=>128) + ); +{ + my $attr= 0; + while(my ($tname,$t)= $report->tables_each()) + { + next if $report->supporting_tables_exists($tname); + $attr+= scalar @{$t->indexes_keys()}; + } + $report->parameters->{'NoOfOrderedIndexes'}->value_set($_ => $attr) + foreach $report->versions; +} + +$report->parameters_set('NoOfUniqueHashIndexes' => + new MySQL::NDB::Size::Parameter(name=>'NoOfUniqueHashIndexes', + mem_per_item=>15, + default=>64) + ); +{ + my $attr= 0; + while(my ($tname,$t)= $report->tables_each()) + { + next if not $tname =~ /\$unique$/; + $attr++; + } + $report->parameters->{'NoOfUniqueHashIndexes'}->value_set($_ => $attr) + foreach $report->versions; +} + +# Size of trigger is not documented +$report->parameters_set('NoOfTriggers' => + new MySQL::NDB::Size::Parameter(name=>'NoOfTriggers', + mem_per_item=>0, + default=>768) + ); + +{ + $report->parameters->{'NoOfTriggers'}->value_set( + $_ => ( + (3* + $report->parameters->{'NoOfUniqueHashIndexes'}->value->{$_}) + + + $report->parameters->{'NoOfOrderedIndexes'}->value->{$_} + + + (4* # for backups (3) and replication (1??) + $report->parameters->{'NoOfTables'}->value->{$_}) + + ) + ) + foreach $report->versions; +} + +# DataMemory is in bytes... +$report->parameters_set('DataMemory' => + new MySQL::NDB::Size::Parameter(name=>'DataMemory', + mem_per_item=>1024, + unit=>'KB', + default=>80*1024) + ); +$report->parameters_set('IndexMemory' => + new MySQL::NDB::Size::Parameter(name=>'IndexMemory', + mem_per_item=>1024, + unit=>'KB', + default=>18*1024) + ); + +{ + foreach my $ver ($report->versions) + { + my $dm=0; + my $im=0; + while(my ($tname,$t)= $report->tables_each()) + { + $dm+=$t->dm_needed->{$ver}; + $dm+=$t->vdm_needed->{$ver} || 0; + $im+=$t->im_needed->{$ver}; + } + $report->parameters->{'DataMemory'}->value_set($ver => $dm/1024); + $report->parameters->{'IndexMemory'}->value_set($ver => $im/1024); + } +} + + +if($savequeries) +{ + open Q, "> $savequeries"; + print Q Dumper(\%queries); + close Q; +} + +use Data::Dumper; + +if($debug) +{ + eval 'print STDERR Dumper($report)'; +} + +if($format eq 'text') +{ + my $text_out= new MySQL::NDB::Size::Output::Text($report); + $text_out->output(); +} +elsif($format eq 'html') +{ + my $html_out= new MySQL::NDB::Size::Output::HTML($report); + $html_out->output(); +} +else +{ + # default to text output + my $text_out= new MySQL::NDB::Size::Output::Text($report); + $text_out->output(); +} + +package MySQL::NDB::Size::Output::Text; +use Data::Dumper; + +sub new { bless { report=> $_[1] }, $_[0]} + +sub ul +{ + my $s= $_[1]."\n"; + $s.='-' foreach (1..length($_[1])); + return $s.="\n"; +} + +sub output +{ + my $self= shift; + my $r= $self->{report}; + + print $self->ul("ndb_size.pl report for database ". $r->database(). + " (".(($r->tables_count()||0)-($r->supporting_tables_count()||0)). + " tables)"); + + print "Connected to: ".$r->dsn()."\n\n"; + + print "Including information for versions: ". + join(', ',@{$r->versions})."\n\n"; + + foreach my $tname (@{$r->tables_keys()}) + { + my $t= $r->tables->{$tname}; +# next if $r->supporting_tables_exists($tname); + + print $self->ul($tname)."\n"; + + # format strings + my $f= "%25s "; + my $v= "%10s "; + + # Columns + print "DataMemory for Columns (* means varsized DataMemory):\n"; + printf $f.'%20s %9s %5s','Column Name','Type','Varsized', 'Key'; + printf $v, $_ foreach @{$r->versions}; + print "\n"; + my %dm_totals; + my %vdm_totals; + while(my ($cname, $c)= $t->columns_each()) + { + $c->type =~ /^([^\(]*)/g; + printf $f.'%20s %9s %5s', + $cname, + $1.( + ( $c->size and not $c->type() =~ /(enum|set)/) + ? '('.$c->size.')' + :'' ), + ($c->is_varsize)? 'Y':' ', + (defined($c->Key))?$c->Key:' '; + foreach(@{$r->versions}) + { + if($c->ver_dm_exists($_)) + { + printf $v, $c->ver_dm($_).(($c->is_varsize)?'*':''); + if($c->is_varsize()) + { + $vdm_totals{$_}+= $c->ver_dm($_); + } + else + { + $dm_totals{$_}+= $c->ver_dm($_); + } + } + else + { + printf $v, $c->dm||'N/A'; + $dm_totals{$_}+=$c->dm||0; + } + } + print "\n"; + } + printf $f.'%20s %9s %5s','','','', ''; + printf $v, '--' foreach @{$t->dm_versions}; + print "\n"; + printf $f.'%20s %9s %5s','Fixed Size Columns DM/Row','','',''; + printf $v, $dm_totals{$_} foreach @{$r->versions}; + print "\n"; + printf $f.'%20s %9s %5s','Varsize Columns DM/Row','','',''; + printf $v, $vdm_totals{$_} || 0 foreach @{$r->versions}; + print "\n"; + + + # DM for Indexes + print "\n\nDataMemory for Indexes:\n"; + printf $f.'%20s ','Index Name','Type'; + printf $v, $_ foreach @{$r->versions}; + print "\n"; + my %idx_dm_totals; + while(my ($iname, $i)= $t->indexes_each()) + { + printf $f.'%20s ',$iname,$i->type(); + foreach(@{$r->versions}) + { + if($i->ver_dm_exists($_)) + { + printf $v, $i->ver_dm($_).(($i->is_varsize)?'*':''); + $idx_dm_totals{$_}+= $i->ver_dm($_); + } + else + { + printf $v, ((defined($i->dm))?$i->dm:'N/A'); + $idx_dm_totals{$_}+= $i->dm if defined($i->dm); + } + } + print "\n"; + } + printf $f.'%20s ','',''; + printf $v, '--' foreach @{$r->versions}; + print "\n"; + printf $f.'%20s ','Total Index DM/Row',''; + printf $v, (defined($idx_dm_totals{$_}))?$idx_dm_totals{$_}:0 + foreach @{$r->versions}; + print "\n\n"; + + if(@{$t->supporting_tables()}) + { + print "\n\nSupporting Tables DataMemory/Row"; + my %supp_total; + foreach(@{$t->supporting_tables()}) + { + print "\n"; + printf $f, $_; + my $st= $r->tables->{$_}; + printf $v, $st->row_dm_size->{$_} foreach @{$st->dm_versions}; + $supp_total{$_}+=$st->row_dm_size->{$_} + foreach @{$st->dm_versions}; + } + print "\n"; + printf $f, ''; + printf $v, '--' foreach @{$t->dm_versions}; + print "\n"; + printf $f, 'This DataMemory/Row'; + printf $v, $t->row_dm_size->{$_} foreach @{$t->dm_versions}; + $supp_total{$_}+=$t->row_dm_size->{$_} + foreach @{$t->dm_versions}; + print "\n"; + printf $f, 'Total DM/Row'; + printf $v, $supp_total{$_} foreach @{$t->dm_versions}; + print " Includes DM in other tables\n"; + } + + # IM for Columns + print "\n\nIndexMemory for Indexes:\n"; + printf $f,'Index Name'; + printf $v, $_ foreach @{$r->versions}; + print "\n"; + my %im_totals; + foreach my $iname (@{$t->indexes_keys()}) + { + my $i= $t->indexes->{$iname}; + next if $i->is_supporting_table(); + + printf $f, $iname; + + foreach(@{$r->versions}) + { + if(!defined($i->im)) + { + printf $v,'N/A'; + next; + } + if($i->ver_im_exists($_)) + { + printf $v, $i->ver_im->{$_}; + $im_totals{$_}+= $i->ver_im->{$_}; + } + else + { + printf $v, $i->im; + $im_totals{$_}+=$i->im; + } + } + print "\n"; + } + printf $f,''; + printf $v, '--' foreach @{$t->dm_versions}; + print "\n"; + printf $f,'Indexes IM/Row'; + printf $v, $im_totals{$_} foreach @{$r->versions}; + print "\n"; + + if(@{$t->supporting_tables()}) + { + print "\n\nSupporting Tables IndexMemory/Row"; + my %supp_total; + foreach(@{$t->supporting_tables()}) + { + print "\n"; + my $st= $r->tables->{$_}; + foreach(@{$st->indexes_keys()}) + { + printf $f, $st->name() if $_ eq 'PRIMARY'; + printf $f, $st->name().$_ if $_ ne 'PRIMARY'; + my $sti= $st->indexes->{$_}; + printf $v, ($sti->ver_im_exists($_)) + ?$sti->ver_im->{$_} + :$sti->im() foreach @{$st->dm_versions}; + $supp_total{$_}+= ($sti->ver_im_exists($_)) + ?$sti->ver_im->{$_} + :$sti->im() foreach @{$st->dm_versions}; + + } + } + print "\n"; + printf $f, ''; + printf $v, '--' foreach @{$t->dm_versions}; + print "\n"; + print "\n"; + printf $f, 'Total Suppt IM/Row'; + printf $v, $supp_total{$_} foreach @{$t->dm_versions}; + print "\n"; + } + + print "\n\n\nSummary (for THIS table):\n"; + printf $f, ''; + printf $v, $_ foreach @{$r->versions}; + print "\n"; + printf $f, 'Fixed Overhead DM/Row'; + printf $v, $t->row_dm_overhead->{$_} foreach @{$t->dm_versions}; + print "\n"; + printf $f, 'NULL Bytes/Row'; + printf $v, $t->dm_null_bytes->{$_}||0 foreach @{$t->dm_versions}; + print "\n"; + printf $f, 'DataMemory/Row'; + printf $v, $t->row_dm_size->{$_} foreach @{$t->dm_versions}; + print " (Includes overhead, bitmap and indexes)\n"; + + print "\n"; + printf $f, 'Varsize Overhead DM/Row'; + printf $v, $t->row_vdm_overhead->{$_}||0 foreach @{$t->dm_versions}; + print "\n"; + printf $f, 'Varsize NULL Bytes/Row'; + printf $v, $t->vdm_null_bytes->{$_}||0 foreach @{$t->dm_versions}; + print "\n"; + printf $f, 'Avg Varside DM/Row'; + printf $v, (exists($t->row_vdm_size->{$_})? + $t->row_vdm_size->{$_}: 0) + foreach @{$r->versions}; + print "\n\n"; + printf $f, 'No. Rows'; + printf $v, $t->rows foreach @{$r->versions}; + print "\n\n"; + printf $f, 'Rows/'.($t->dm_pagesize()/1024).'kb DM Page'; + printf $v, $t->dm_rows_per_page->{$_} foreach @{$r->versions}; + print "\n"; + printf $f, 'Fixedsize DataMemory (KB)'; + printf $v, $t->dm_needed->{$_}/1024 foreach @{$r->versions}; + print "\n\n"; + printf $f, 'Rows/'.($t->vdm_pagesize()/1024).'kb Varsize DM Page'; + printf $v, $t->vdm_rows_per_page->{$_}||0 foreach @{$r->versions}; + print "\n"; + printf $f, 'Varsize DataMemory (KB)'; + printf $v, ($t->vdm_needed->{$_}||0)/1024 foreach @{$r->versions}; + print "\n\n"; + printf $f, 'Rows/'.($t->im_pagesize()/1024).'kb IM Page'; + printf $v, $t->im_rows_per_page->{$_} foreach @{$r->versions}; + print "\n"; + printf $f, 'IndexMemory (KB)'; + printf $v, $t->im_needed->{$_}/1024 foreach @{$r->versions}; + + print "\n\n\n"; + } + + print "\n\n\n"; + print $self->ul("Parameter Minimum Requirements"); + print "* indicates greater than default\n\n"; + printf "%25s ","Parameter"; + printf "%15s ",'Default' ; + printf "%15s%1s ",$_,'' foreach @{$r->versions}; + print "\n"; + while( my ($pname, $p)= $r->parameters_each()) + { + printf "%25s ",$pname.(($p->unit)?' ('.$p->unit.')':''); + printf "%15u ", $p->default; + printf "%15u%1s ", $p->value->{$_}, + ($p->value->{$_} > $p->default)?'*':'' + foreach @{$r->versions}; + print "\n"; + } + print "\n\n\n"; +} + +sub table +{ + my $self= shift; + my $t= shift; +} + +package MySQL::NDB::Size::Output::HTML; + +sub new { bless { report=> $_[1] }, $_[0]} + +sub tag +{ + my ($self,$tag,$content)= @_; + return "<$tag>$content\n"; +} + +sub h1 { my ($self,$t)= @_; return $self->tag('h1',$t); } +sub h2 { my ($self,$t)= @_; return $self->tag('h2',$t); } +sub h3 { my ($self,$t)= @_; return $self->tag('h3',$t); } +sub h4 { my ($self,$t)= @_; return $self->tag('h4',$t); } + +sub p { my ($self,$t)= @_; return $self->tag('p',$t); } +sub b { my ($self,$t)= @_; return $self->tag('b',$t); } + +sub th +{ + my ($self)= shift; + my $c; + $c.=$self->tag('th',$_) foreach @_; + return $self->tag('tr',$c); +} + +sub tr +{ + my ($self)= shift; + my $c; + $c.=$self->tag('td',$_) foreach @_; + return $self->tag('tr',$c); +} + +sub td { my ($self,$t)= @_; return $self->tag('td',$t); } + +sub ul +{ + my ($self)= shift; + my $c; + $c.= " ".$self->li($_) foreach @_; + return $self->tag('ul',$c); +} + +sub li { my ($self,$t)= @_; return $self->tag('li',$t); } + +sub href +{ + my ($self,$href,$t)= @_; + $href =~ s/\$/__/g; + return "$t"; +} + +sub aname +{ + my ($self,$href,$t)= @_; + $href =~ s/\$/__/g; + return "$t"; +} + +sub output +{ + my $self= shift; + my $r= $self->{report}; + + print < + + + + +ENDHTML +print "MySQL Cluster size estimate for ".$r->database().""; +print < + table { border-collapse: collapse } + td,th { border: 1px solid black } + + + +ENDHTML + + print $self->h1("ndb_size.pl report for database ". $r->database(). + " (".(($r->tables_count()||0)-($r->supporting_tables_count()||0)). + " tables)"); + + print $self->p("Connected to: ".$r->dsn()); + + print $self->p("Including information for versions: ". + join(', ',@{$r->versions})); + + if(@{$r->tables_keys()}) + { + print $self->h2("Table List"); + my @tlist; + foreach(sort @{$r->tables_keys()}) + { + push @tlist, $self->href("#$_",$_); + } + print $self->ul(@tlist); + } + + foreach my $tname (sort @{$r->tables_keys()}) + { + my $t= $r->tables->{$tname}; + + print $self->h2($self->aname($tname,$tname)); + + # format strings + my $f= "%25s "; + my $v= "%10s "; + + # Columns + print $self->h3("DataMemory for Columns"); + print $self->p("* means varsized DataMemory"); + print "\n"; + print $self->th('Column Name','Type','Varsized', 'Key', + @{$r->versions}); + + my %dm_totals; + my %vdm_totals; + while(my ($cname, $c)= $t->columns_each()) + { + $c->type =~ /^([^\(]*)/g; + my @verinfo; + foreach(@{$r->versions}) + { + if($c->ver_dm_exists($_)) + { + push @verinfo, $c->ver_dm($_).(($c->is_varsize)?'*':''); + if($c->is_varsize()) + { + $vdm_totals{$_}+= $c->ver_dm($_); + } + else + { + $dm_totals{$_}+= $c->ver_dm($_); + } + } + else + { + push @verinfo, $c->dm||'N/A'; + $dm_totals{$_}+=$c->dm||0; + } + } + + print $self->tr( + $cname, + $1.( + ( $c->size and not $c->type() =~ /(enum|set)/) + ? '('.$c->size.')' + :'' ), + ($c->is_varsize)? 'Y':' ', + (defined($c->Key))?$c->Key:' ',@verinfo); + } + + { + my @dmtot; + push @dmtot, $self->b($dm_totals{$_}) foreach @{$r->versions}; + print $self->tr($self->b('Fixed Size Columns DM/Row'),'','','', + @dmtot); + + } + { + my @vdmtot; + push @vdmtot, $self->b($vdm_totals{$_} || 0) + foreach @{$r->versions}; + print $self->tr($self->b('Varsize Columns DM/Row'),'','','', + @vdmtot); + } + + print "
\n"; + + # DM for Indexes + print $self->h3('DataMemory for Indexes'); + print "\n"; + print $self->th('Index Name','Type',@{$r->versions}); + + my %idx_dm_totals; + while(my ($iname, $i)= $t->indexes_each()) + { + my @verinfo; + foreach(@{$r->versions}) + { + if($i->ver_dm_exists($_)) + { + push @verinfo, $i->ver_dm($_).(($i->is_varsize)?'*':''); + $idx_dm_totals{$_}+= $i->ver_dm($_); + } + else + { + push @verinfo, ((defined($i->dm))?$i->dm:'N/A'); + $idx_dm_totals{$_}+= $i->dm if defined($i->dm); + } + } + printf $self->tr($iname,$i->type(),@verinfo); + } + { + my @idxtot; + push @idxtot, $self->b((defined($idx_dm_totals{$_})) + ? $idx_dm_totals{$_}:0) + foreach @{$r->versions}; + print $self->tr($self->b('Total Index DM/Row'),'', + @idxtot); + } + + print "
"; + + if(@{$t->supporting_tables()}) + { + print $self->h3("Supporting Tables DataMemory/Row"); + my %supp_total; + + print ""; + print $self->th('Table',@{$r->versions}); + foreach(@{$t->supporting_tables()}) + { + my $st= $r->tables->{$_}; + my @stdm; + push @stdm, $st->row_dm_size->{$_} foreach @{$st->dm_versions}; + + print $self->tr($_,@stdm); + + $supp_total{$_}+=$st->row_dm_size->{$_} + foreach @{$st->dm_versions}; + } + { + my @rdmtot; + push @rdmtot, $self->b($t->row_dm_size->{$_}) + foreach @{$t->dm_versions}; + print $self->tr($self->b('This DataMemory/Row'),@rdmtot); + } + $supp_total{$_}+=$t->row_dm_size->{$_} + foreach @{$t->dm_versions}; + + { + my @tdmr; + push @tdmr, $self->b($supp_total{$_}) + foreach @{$t->dm_versions}; + print $self->tr($self->b('Total DM/Row (inludes DM in other tables)'),@tdmr); + } + print "
"; + } + + # IM for Columns + print $self->h3("IndexMemory for Indexes"); + print "\n"; + print $self->th('Index Name', @{$r->versions}); + + my %im_totals; + foreach my $iname (@{$t->indexes_keys()}) + { + my $i= $t->indexes->{$iname}; + next if $i->is_supporting_table(); + + my @verinfo; + foreach(@{$r->versions}) + { + if(!defined($i->im)) + { + push @verinfo,'N/A'; + next; + } + if($i->ver_im_exists($_)) + { + push @verinfo, $i->ver_im->{$_}; + $im_totals{$_}+= $i->ver_im->{$_}; + } + else + { + push @verinfo, $i->im; + $im_totals{$_}+=$i->im; + } + } + print $self->tr($iname, @verinfo); + } + { + my @v; + push @v, $self->b($im_totals{$_}) foreach @{$r->versions}; + printf $self->tr('Indexes IM/Row',@v); + } + print "
\n"; + + if(@{$t->supporting_tables()}) + { + print $self->h3("Supporting Tables IndexMemory/Row"); + print "\n"; + my %supp_total; + foreach(@{$t->supporting_tables()}) + { + my $st= $r->tables->{$_}; + foreach(@{$st->indexes_keys()}) + { + my @r; + push @r, $st->name() if $_ eq 'PRIMARY'; + push @r, $st->name().$_ if $_ ne 'PRIMARY'; + my $sti= $st->indexes->{$_}; + push @r, ($sti->ver_im_exists($_)) + ?$sti->ver_im->{$_} + :$sti->im() foreach @{$st->dm_versions}; + $supp_total{$_}+= ($sti->ver_im_exists($_)) + ?$sti->ver_im->{$_} + :$sti->im() foreach @{$st->dm_versions}; + print $self->tr(@r); + } + } + { + my @r; + push @r, $self->b($supp_total{$_}) foreach @{$t->dm_versions}; + print $self->tr($self->b('Total Suppt IM/Row'),@r); + } + print "
\n"; + } + + print $self->h3("Summary (for THIS table)"); + print $self->h4("Fixed Sized Part"); + print "\n"; + + print $self->tr('',@{$r->versions}); + + { my @r; + push @r, $t->row_dm_overhead->{$_} foreach @{$t->dm_versions}; + print $self->tr('Fixed Overhead DM/Row',@r); + } + { my @r; + push @r, $t->dm_null_bytes->{$_}||0 foreach @{$t->dm_versions}; + print $self->tr('NULL Bytes/Row',@r); + } + { my @r; + push @r, $t->row_dm_size->{$_} foreach @{$t->dm_versions}; + print $self->tr('DataMemory/Row (incl overhead, bitmap, indexes)', + @r); + } + print "
\n"; + print $self->h4("Variable Sized Part"); + print "\n"; + + { my @r; + push @r, $t->row_vdm_overhead->{$_}||0 foreach @{$t->dm_versions}; + print $self->tr('Varsize Overhead DM/Row',@r); + } + { my @r; + push @r, $t->vdm_null_bytes->{$_}||0 foreach @{$t->dm_versions}; + print $self->tr('Varsize NULL Bytes/Row',@r); + } + { my @r; + push @r, (exists($t->row_vdm_size->{$_})? + $t->row_vdm_size->{$_}: 0) + foreach @{$r->versions}; + print $self->tr('Avg Varside DM/Row',@r); + } + print "
\n"; + print $self->h4("Memory Calculations"); + print "\n"; + + { my @r; + push @r, $t->rows foreach @{$r->versions}; + print $self->tr('No. Rows',@r); + } + { my @r; + push @r, $t->dm_rows_per_page->{$_} foreach @{$r->versions}; + print $self->tr('Rows/'.($t->dm_pagesize()/1024).'kb DM Page',@r); + } + { my @r; + push @r, $t->dm_needed->{$_}/1024 foreach @{$r->versions}; + print $self->tr('Fixedsize DataMemory (KB)',@r); + } + { my @r; + push @r, $t->vdm_rows_per_page->{$_}||0 foreach @{$r->versions}; + print $self->tr('Rows/'.($t->vdm_pagesize()/1024). + 'kb Varsize DM Page', @r); + } + { my @r; + push @r, ($t->vdm_needed->{$_}||0)/1024 foreach @{$r->versions}; + print $self->tr('Varsize DataMemory (KB)', @r); + } + { my @r; + push @r, $t->im_rows_per_page->{$_} foreach @{$r->versions}; + print $self->tr('Rows/'.($t->im_pagesize()/1024).'kb IM Page', @r); + } + { my @r; + push @r, $t->im_needed->{$_}/1024 foreach @{$r->versions}; + print $self->tr('IndexMemory (KB)', @r); + } + + print "

\n\n"; + } + + print $self->h1("Parameter Minimum Requirements"); + print $self->p("* indicates greater than default"); + print "\n"; + print $self->th("Parameter",'Default',@{$r->versions}); + while( my ($pname, $p)= $r->parameters_each()) + { + my @r; + push @r, $p->value->{$_}. + (($p->value->{$_} > $p->default)?'*':'') + foreach @{$r->versions}; + + print $self->tr($pname.(($p->unit)?' ('.$p->unit.')':''), + $p->default, + @r); + } + print "
"; +} + +sub table +{ + my $self= shift; + my $t= shift; +} diff --git a/storage/ndb/tools/ndb_size.tmpl b/storage/ndb/tools/ndb_size.tmpl deleted file mode 100644 index 1e19ea132ba..00000000000 --- a/storage/ndb/tools/ndb_size.tmpl +++ /dev/null @@ -1,231 +0,0 @@ - - - - - - MySQL Cluster size estimate for <TMPL_VAR NAME="db" ESCAPE="HTML"> - - - -

MySQL Cluster analysis for

-

This is an automated analysis of the database for migration into MySQL Cluster. No warranty is made to the accuracy of the information.

- -

This information should be valid for MySQL 4.1 and 5.0. Since 5.1 is not a final release yet, the numbers should be used as a guide only.

- -

5.1-dd is for tables stored on disk. The ndb_size.pl estimates are experimental and should not be trusted. Notably we don't take into account indexed columns being in DataMemory versus non-indexed on disk.

- -

Parameter Settings

-

NOTE the configuration parameters below do not take into account system tables and other requirements.

- - - - - - - - - - - - - - - -
Parameter
- -

Memory usage because of parameters

- -

Usage is in kilobytes. Actual usage will vary as you should set the parameters larger than those listed in the table above.

- - - - - - - - - - - - - - - -
Parameter
- -

Table List

-
    - -
  • ">
  • -
    -
- -
- - -

">

- - - - - - - - - - - - - - - - - - - - - - - -
ColumnTypeVARSIZESizeKey NDB Size
YES 
- -

 

- -

Indexes

- -

We assume that indexes are ORDERED (not created USING HASH). If order is not required, 10 bytes of data memory can be saved per row if the index is created USING HASH

- - - - - - - - - - - - - - - - - - - - - - - - - -
IndexTypeColumns IdxMem DatMem
- -

DataMemory Usage

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
Nr Varsized Attributes
Row Overhead
Varsized Overhead
Column DataMemory/Row
Index DataMemory/Row
Total DataMemory/Row
Rows per 32kb page
Current number of rows
Total DataMemory (kb)
- -

IndexMemory Usage

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
IndexMemory/Row
Rows per 8kb page
Current number of rows
Total IndexMemory (kb)
- -
-
- -

This is the output of ndb_size.pl.

- - -