mysqlが必要とするメモリが多そうな件について

mysqlでサーバをたてていると、予想外に確保しようとするメモリが多いことに気がついた。

事前に計算で求まる気がしてたんだけど、どうやらそれだけではだめっぽい。

テスト用のサーバ設定でmymemcheckしたときのメモリ使用の概算が下記。

[ minimal memory ]
ref
* 『High Performance MySQL』, Solving Memory Bottlenecks, p125

global_buffers
key_buffer_size                     402653184   384.000 [M]
innodb_buffer_pool_size             402653184   384.000 [M]
innodb_log_buffer_size                8388608     8.000 [M]
innodb_additional_mem_pool_size      20971520    20.000 [M]
net_buffer_length                     1048576  1024.000 [K]

thread_buffers
sort_buffer_size                         8192     8.000 [K]
myisam_sort_buffer_size                  8192     8.000 [K]
read_buffer_size                         8192     8.000 [K]
join_buffer_size                        32768    32.000 [K]
read_rnd_buffer_size                     8192     8.000 [K]

max_connections                            3000

min_memory_needed = global_buffers + (thread_buffers * max_connections)
= 835715072 + 65536 * 3000
= 1032323072 (984.500 [M])

[ 32bit Linux x86 limitation ]
ref
* http://dev.mysql.com/doc/mysql/en/innodb-configuration.html

* need to include read_rnd_buffer.
* no need myisam_sort_buffer because allocate when repair, check alter.

2G > process heap
process heap = innodb_buffer_pool + key_buffer
+ max_connections * (sort_buffer + read_buffer + read_rnd_buffer)
+ max_connections * stack_size
= 402653184 + 402653184
+ 3000 * (8192 + 8192 + 8192)
+ 3000 * 262144
= 1665466368 (1.551 [G])

2G > 1.551 [G] … safe

[ maximum size of innodb_log_file_size ]
ref
* http://dev.mysql.com/doc/mysql/en/innodb-start.html

1MB < innodb_log_file_size < MAX_innodb_log_file_size < 4GB

MAX_innodb_log_file_size = innodb_buffer_pool_size * 1/innodb_log_files_in_group
= 402653184 * 1/2
= 201326592 (192.000 [M])

innodb_log_file_size < MAX_innodb_log_file_size
5242880 < 201326592
5.000 [M] < 192.000 [M] … safe

この計算だと最小で1GBぐらい、最大で1.5GBぐらいだと思われてた。

特にスレッドあたりのバッファを64KBぐらいにしたつもりだったので、そこだけで3000本のコネクションで192MB消費する予定。

で、実験用にphpからコネクションをがばがば張るコードを書いてみた。

<?php
define(‘MAX_CONNECTION’, 1400);

function main(){
for($i=0; $i<MAX_CONNECTION; $i++){
$pdo[$i] = new PDO
(‘mysql:host=db.l2tp.org;dbname=test’,
‘root’,
‘password’
);
usleep(100000 );
}

try {
$stmt = $pdo[$i]->query(“SELECT * FROM test3;”);
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
echo implode(“, “, $row) . PHP_EOL;
}
} catch (PDOException $e){
var_dump($e->getMessage());
}

$pdo = null;

}
main();
?>

なんかしらないけどこのコードがうまく通らない。途中で落ちる。

Fatal error: Uncaught exception 'PDOException' with message
 'SQLSTATE[HY000] [1135] Can't create a new thread (errno 35);
 if you are not out of available memory, you can consult the manual
for a possible OS-dependent bug' in /usr/home/yousan/phptest/thread_max.php:10

ウェイトをかけてtopでメモリをみながらやってみた。

topの各項目とそれぞれの値

PID USERNAME   THR PRI NICE   SIZE    RES STATE    TIME   WCPU COMMAND

71331 mysql       22  44    0   967M   135M ucond    0:00  0.00% mysqld   (何もしてないとき)

71331 mysql       31   4    0  1035M   135M sbwait   0:00  0.00% mysqld (コードを実行開始)
71331 mysql       68   4    0  1183M   136M sbwait   0:00  0.00% mysqld
71331 mysql      100   4    0  1311M   137M sbwait   0:00  0.00% mysqld
71331 mysql      127   4    0  1419M   137M sbwait   0:00  0.00% mysqld
71331 mysql      163   4    0  1564M   139M sbwait   0:00  0.00% mysqld
71331 mysql      225   4    0  1816M   144M sbwait   0:00  0.00% mysqld (順調にSIZEが増える)
71331 mysql      279   4    0  2035M   147M sbwait   0:00  0.00% mysqld
71331 mysql      307   4    0  2149M   150M sbwait   0:00  0.00% mysqld
71331 mysql      343   4    0  2295M   152M sbwait   0:00  0.00% mysqld
71331 mysql      362   4    0  2372M   154M sbwait   0:00  0.00% mysqld
71331 mysql       22  44    0   974M   141M ucond    0:00  0.00% mysqld (落ちた)

なぜだかガンガンSIZEの値が増えていって、2G超えて2.5GBにさしかかろうかというところで落ちてる。稼働してるサーバがFreeBSDの32bit版だったので、おそらくメモリによる制限だと思われる。

このSIZEが結構増えることがやっかいで、おかげさまでスワップアウトとかしてくれる。

気になるのは各項目の値。SIZEってなんだろう、RESって何だろう状態。調べてみた。

SIZE はプロセスサイズの合計 (text, data, stack)
RES は現在のメモリ常駐量 (SIZE と RES はいずれもキロバイト単位)

On-line Manual of “top”

SIZEの値は仮想メモリ的なものらしい。これの増減についてみてみると、当初22スレッドで967MBスタートから、68スレッドで1183MB、362スレッドでは2372MBになっているので、

22->68では46スレッド、216MB増え、68->362では294スレッド、1189MB増えてる。

適当に表にまとめる。

スレッド数 SIZE(MB) RES スレッド差分 SIZE差分 RES差分 SIZE/スレッド
22 967 135 9 68 0 7.555555556
31 1035 135 37 148 1 4
68 1183 136 32 128 1 4
100 1311 137 27 108 0 4
127 1419 137 36 145 2 4.027777778
163 1564 139 62 252 5 4.064516129
225 1816 144 54 219 3 4.055555556
279 2035 147 29 114 3 3.931034483
308 2149 150 35 146 2 4.171428571
343 2295 152 19 77 2 4.052631579
362 2372 154

だいたい、1コネクションあたり4MBのSIZEが増えるらしい。RESの方は値が小さかったので大きくみてみると、

31->343スレッドの増加で17MB増えてるので、44.87KB/スレッドの増加。MB以下が切り捨てってことはこれがスレッドあたりで実際に確保されるメモリだろうか。

スレッドのオーバーヘッドがあるがためにそんなことになってそうだ。マルチスレッドの時のオーバーヘッドを調べないと。

次回へつづく

コメントを残す