読者です 読者をやめる 読者になる 読者になる

fetchrow時の使用メモリ

topコマンドで調べてもよく分からんので、http://www.drk7.jp/MT/archives/000803.htmlを参考にDevel::Size::Reportを使って調べてみる。

上記サイトによれば

SCALAR 型のデータは、Integer 型である場合、最小で16byte のメモリを必要とする。以後、32bit 単位で 4byte 増加する。String 型の場合、最小で25 byte のメモリを必要とし、以後 1 ASCII につき 1byte 必要とする。

とのこと。実際に自分で試してみてもこんな感じだった。


さて、実験。
テーブルスキーマはこんな感じ。

mysql> show create table data \G
*************************** 1. row ***************************
       Table: data
Create Table: CREATE TABLE `data` (
  `id` int(10) unsigned NOT NULL,
  `user_id` int(10) unsigned NOT NULL,
  `post_date` int(10) unsigned NOT NULL,
  `content` varchar(100) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `i1` (`user_id`,`post_date`),
  KEY `i2` (`post_date`)
) ENGINE=InnoDB DEFAULT CHARSET=cp932


調べるために10行をfetchrow_hashrefしてみる。

use Devel::Size::Report qw/report_size/;

my $LIMIT = 10;

my $dbh = getHandle();
my $sth = $dbh->prepare(<<"SQL");
select * from data limit $LIMIT;
SQL

$sth->execute();
my @list = ();
while (my $hash = $sth->fetchrow_hashref) {
   push (@list, $hash);
}

print report_size(\@list, { indent => ' '});


調べてみると以下のことが分かった。

  • id, post_date, user_idに関して
    • int(10)だが文字列として扱われている
    • 30byte = 25bytes + 5ascii(=5bytes)
    • 5asciiなのはSeqが10000〜にしているため(5桁)
  • contentはvarcharなのでメモリサイズも可変
  • Hash refのoverheadがメチャクチャデカイ(=ほぼ2/3)
  • Array refのoverheadはHash refに比べて小さいのでほぼ無視できるはず

ものすごく単純化すると、

①selectされるcolumnの合計byte数 × ②レコード数 × 3倍(hash refのoverhead)

という計算式が成り立ちそうである。


試しに10万行をSELECTしたとして、計算してみる。

  `id` int(10) unsigned NOT NULL,
  `user_id` int(10) unsigned NOT NULL,
  `post_date` int(10) unsigned NOT NULL,
  `content` varchar(100) NOT NULL,

どんなデータが入っているか分からないので、それぞれaverageと仮定する。
①は (25+5)+(25+5)+(25+5)+(25+50) = 165 bytes
となるので、

165byte × 10万行 × 3 = 48339 Kbytes = 47Mbytes

と予測が立つ。実際、プログラムを実行して確認すると、

35461 Kbytes = 34 Mbytes

でオーダーとしては正しかった。

数10Mbytesレベルなら、今日のサーバならなんなく捌けるはずなので問題はないはず。
ただ、コピーしまくるとなかなか大変そうなので、リファレンスをうまく使う必要はありそうだ。





ちなみに、詳細結果は以下の通り。

Size report v0.13 for 'ARRAY(0x936bb60)':
  Array ref 3677 bytes (overhead: 120 bytes, 3.26%)
   Hash ref 370 bytes (overhead: 229 bytes, 61.89%)
    'id' => Scalar 30 bytes
    'content' => Scalar 46 bytes
    'post_date' => Scalar 35 bytes
    'user_id' => Scalar 30 bytes
   Hash ref 355 bytes (overhead: 229 bytes, 64.51%)
    'id' => Scalar 30 bytes
    'content' => Scalar 31 bytes
    'post_date' => Scalar 35 bytes
    'user_id' => Scalar 30 bytes
   Hash ref 354 bytes (overhead: 229 bytes, 64.69%)
    'id' => Scalar 30 bytes
    'content' => Scalar 30 bytes
    'post_date' => Scalar 35 bytes
    'user_id' => Scalar 30 bytes
   Hash ref 354 bytes (overhead: 229 bytes, 64.69%)
    'id' => Scalar 30 bytes
    'content' => Scalar 30 bytes
    'post_date' => Scalar 35 bytes
    'user_id' => Scalar 30 bytes
   Hash ref 354 bytes (overhead: 229 bytes, 64.69%)
    'id' => Scalar 30 bytes
    'content' => Scalar 30 bytes
    'post_date' => Scalar 35 bytes
    'user_id' => Scalar 30 bytes
   Hash ref 354 bytes (overhead: 229 bytes, 64.69%)
    'id' => Scalar 30 bytes
    'content' => Scalar 30 bytes
    'post_date' => Scalar 35 bytes
    'user_id' => Scalar 30 bytes
   Hash ref 354 bytes (overhead: 229 bytes, 64.69%)
    'id' => Scalar 30 bytes
    'content' => Scalar 30 bytes
    'post_date' => Scalar 35 bytes
    'user_id' => Scalar 30 bytes
   Hash ref 354 bytes (overhead: 229 bytes, 64.69%)
    'id' => Scalar 30 bytes
    'content' => Scalar 30 bytes
    'post_date' => Scalar 35 bytes
    'user_id' => Scalar 30 bytes
   Hash ref 354 bytes (overhead: 229 bytes, 64.69%)
    'id' => Scalar 30 bytes
    'content' => Scalar 30 bytes
    'post_date' => Scalar 35 bytes
    'user_id' => Scalar 30 bytes
   Hash ref 354 bytes (overhead: 229 bytes, 64.69%)
    'id' => Scalar 30 bytes
    'content' => Scalar 30 bytes
    'post_date' => Scalar 35 bytes
    'user_id' => Scalar 30 bytes
Total: 3677 bytes in 51 elements