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

MySQL maxconnections

CentOSだと設定場所は/etc/my.cnf

[mysqld]
max_connections=100

perlからDBI使って、DBハンドル取得&SELECTした時にどういう挙動をするのか調べてみた。
実験に使ったプログラム。

#!/usr/bin/perl
use strict;
use DBI;
use Parallel::ForkManager;

#適当に複数プロセス起動し、それぞれでSELECT
my $pm = Parallel::ForkManager->new(500);
for (1..2000) {
    my $pid;
    $pid = $pm->start && next;
    eval {
        &select;
    };
    print $@ if ($@);
    $pm->finish;
}
$pm->wait_all_children;

sub select {
    my $dbh = &getHandle; 
    # dataテーブルには30万行のレコード
    # limit 2000は適当に
    my $sth = $dbh->prepare(<<'SQL');
    select * from data limit 2000
SQL
    $sth->execute;
    sleep(1);
    print "select: $dbh", "\n";
}


SELECTした後のスリープ時間を1にするとこれくらい。

| Max_used_connections              | 14       |
| Threads_cached                    | 0        |
| Threads_connected                 | 10       |

SELECTした後のスリープ時間を3にするとこれくらい。

| Max_used_connections              | 40       |
| Threads_cached                    | 0        |
| Threads_connected                 | 39       |

SELECTした後のスリープ時間を10にするとToo Many Connection。

DBI connect('dbname=db;host=localhost;mysql_connect_timeout=10','user',...) failed: 

スリープなしにすると、すんなり。

| Max_used_connections              | 11       |
| Threads_cached                    | 0        |
| Threads_connected                 | 1        |


ちょっとプログラムを変更して、スリープ前にundefでも同様の結果を得られる。

| Max_used_connections              | 11        |
| Threads_cached                    | 0         |
| Threads_connected                 | 1         |


つまり、取得したハンドルオブジェクトがスコープから外れてメモリから消えると、
PerlがうまくMySQLとのコネクションを切ってくれるということだ。
切断されたコネクションはMySQLが新たなプロセスに割り当ててくれるので、
sleep(0);のケースでは10程度に抑えられる。


逆にハンドル握りっぱなし(メモリにオブジェクト残したまま)で時間のかかる処理をする(今回はsleepでエミュレート)と、いつまでもコネクションが解放されないので、コネクション数が逼迫するという結果に。


まとめ

ハンドル取得後に長い処理を実行する場合は、適切にハンドルを解放せよ。