sysbenchでMySQLのベンチマーク

Yoshinori Matsunobu's blog: Non-persistent connection performance improvements in 5.6

これに触発されて僕もsysbenchをMySQLでやってみようと思いました。

CentOS。↓こんな感じでやれば出来ました。
しかしながら、なにを読み取っていいのか分かりません/(^o^)\

# mysql -u root -p
# mysql>CREATE DATABASE test_db;
Query OK, 1 row affected (0.00 sec)
# mysql>Ctrl-C -- exit!
# sysbench --test=oltp --db-driver=mysql --mysql-table-engine=innodb --oltp-table-size=1000000 --mysql-user=root --mysql-password=root --mysql-host=localhost --mysql-port=3306 --mysql-db=test_db --oltp-table-name=test_t prepare
sysbench 0.4.12:  multi-threaded system evaluation benchmark

Creating table 'test_t'...
Creating 1000000 records in table 'test_t'...

# sysbench --test=oltp --num-threads=10 --db-driver=mysql --mysql-table-engine=innodb --oltp-table-size=1000000 --mysql-user=root --mysql-password=root --mysql-host=localhost --mysql-port=3306 --mysql-db=test_db --oltp-table-name=test_t run
sysbench 0.4.12:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 10

Doing OLTP test.
Running mixed OLTP test
Using Special distribution (12 iterations,  1 pct of values are returned in 75 pct cases)
Using "BEGIN" for starting transactions
Using auto_inc on the id column
Maximum number of requests for OLTP test is limited to 10000
Threads started!
Done.

OLTP test statistics:
    queries performed:
        read:                            140084
        write:                           50030
        other:                           20012
        total:                           210126
    transactions:                        10006  (283.12 per sec.)
    deadlocks:                           0      (0.00 per sec.)
    read/write requests:                 190114 (5379.21 per sec.)
    other operations:                    20012  (566.23 per sec.)

Test execution summary:
    total time:                          35.3424s
    total number of events:              10006
    total time taken by event execution: 353.0065
    per-request statistics:
         min:                                 10.70ms
         avg:                                 35.28ms
         max:                                123.77ms
         approx.  95 percentile:              51.98ms

Threads fairness:
    events (avg/stddev):           1000.6000/3.44
    execution time (avg/stddev):   35.3007/0.02

【記事途中】各種Databaseの性能を Apache Bench でいろいろと比較【MySQL, MemSQL, Redis, MongoDB】

まだ途中の記事です。
いろいろなDBを追加していきます。

長い長い記事なのでまとめ↓

安定性: MySQL >= MongoDB >> MemSQL = Redis
高速性: MongoDB > Redis > MySQL > MemSQL
使うならMySQLかMongoDBですね(もしくはどちらも)。Redisは安定性がなさすぎて、MemSQLは早くもなければ安定でもないという誰得状態です(私のベンチのとり方が悪いのかな?)

※個人の検証です


☆ 動機

DBというものは複数からアクセスがあって初めてその性能が分かるものでしょう。

【まだ途中】SQLite, Redis の Insert 比較 (By PHP)【ツッコミ歓迎】 - 自分用備忘録
↑先日このように比較しましたが、自分で比較しておいて何ですがあまり参考にならないかと思いました。なぜなら先日のものは単一のアクセスだからです。一般的にデータベースは同時にたくさんのアクセスがくるために作られているので(ほんとう?)、不適切な比較だったかもしれません。

そんなわけで、Apache BenchでURLを叩いて比較してみようと思いました。これで本当の性能が分かることを祈りつつ。。。


☆ 環境

  • 鯖A (192.168.0.100、アプリケーション、データベース)
    • Pentium Dual Core E5200
    • DDR2-800 2GB * 2
    • Crucial m4 SSD 64GB
    • P5KPL-CM
    • GbE (AR8121/AR8113 ?)
    • CentOS 6.3
    • Apache/2.2.15 (Unix) <- httpd.conf は、access_logを出力しないようにしただけでほかはデフォルト。
    • MySQL 5.1.66, for redhat-linux-gnu (x86_64) using readline 5.1
    • MemSQL 5.5.8
    • Redis 2.6.7 (by gcc version 4.4.6)
    • MongoDB v2.0.7, pdfile version 4.5
    • phpredisを使ってredis-serverにアクセス
    • php
# php -v
PHP 5.3.3 (cli) (built: Jul  3 2012 16:53:21) 
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
    with Xdebug v2.1.4, Copyright (c) 2002-2012, by Derick Rethans

# php -m
[PHP Modules]
apc
bz2
calendar
Core
ctype
curl
date
dba
dom
ereg
exif
fileinfo
filter
ftp
gd
gettext
gmp
hash
iconv
igbinary
json
libxml
mbstring
mcrypt
mongo
mysql
mysqli
odbc
openssl
pcntl
pcre
PDO
pdo_mysql
PDO_ODBC
pdo_pgsql
pdo_sqlite
pgsql
Phar
readline
redis
Reflection
session
shmop
SimpleXML
soap
sockets
SPL
sqlite3
standard
tokenizer
uuid
wddx
xdebug
xml
xmlreader
xmlrpc
xmlwriter
xsl
zip
zlib

[Zend Modules]
Xdebug
  • クライアント (192.168.0.150、鯖AにApache Benchをかける用途)


☆ 接続とRTT
鯖A <= GbE => NECルーター(GbEAterm WR8700N) <= GbE => クライアント

# 鯖A -> クライアント のPING
[root@e5200]
# ping 192.168.0.150
PING 192.168.0.150 (192.168.0.150) 56(84) bytes of data.
64 bytes from 192.168.0.150: icmp_seq=1 ttl=64 time=0.098 ms
64 bytes from 192.168.0.150: icmp_seq=2 ttl=64 time=0.107 ms
64 bytes from 192.168.0.150: icmp_seq=3 ttl=64 time=0.118 ms
^C
--- 192.168.0.150 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2723ms
rtt min/avg/max/mdev = 0.098/0.107/0.118/0.014 ms

# クライアント -> 鯖A のPING
[root@g530: ~]
# ping 192.168.0.100
PING 192.168.0.100 (192.168.0.100) 56(84) bytes of data.
64 bytes from 192.168.0.100: icmp_seq=1 ttl=64 time=0.101 ms
64 bytes from 192.168.0.100: icmp_seq=2 ttl=64 time=0.137 ms
64 bytes from 192.168.0.100: icmp_seq=3 ttl=64 time=0.099 ms
^C
--- 192.168.0.100 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2948ms
rtt min/avg/max/mdev = 0.099/0.112/0.137/0.019 ms

# 鯖A -> 鯖A のPING(localhost) (DBが起動してListen状態のデータ)
[root@e5200: ~]
# ping localhost
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.024 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.042 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.030 ms
^C
--- localhost ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2428ms
rtt min/avg/max/mdev = 0.024/0.032/0.042/0.007 ms

# クライアント -> クライアント のPING(localhost)
[root@g530: ~]
# ping localhost
PING localhost.localdomain (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=1 ttl=64 time=0.023 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=2 ttl=64 time=0.029 ms
64 bytes from localhost.localdomain (127.0.0.1): icmp_seq=3 ttl=64 time=0.027 ms
^C
--- localhost.localdomain ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2483ms
rtt min/avg/max/mdev = 0.023/0.026/0.029/0.004 ms


☆ 初期状態

[root@g530: ~]
# curl http://192.168.0.100/benchmarks/static_file.html
counter: 12345

[root@g530: ~]
# curl http://192.168.0.100/benchmarks/memsql.php
counter: 307636

[root@g530: ~]
# curl http://192.168.0.100/benchmarks/mysql.php
counter: 990695

[root@g530: ~]
# curl http://192.168.0.100/benchmarks/phpredis.php
counter: 999832

[root@g530: ~]
# curl http://192.168.0.100/benchmarks/mongodb.php
counter: 547848


☆ DBのテーブル・構造

# MySQL・MemSQL
mysql> desc incr;
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| key1   | varchar(16) | YES  |     | NULL    |       |
| number | int(11)     | YES  |     | NULL    |       |
+--------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

# Redis
redis 127.0.0.1:6379> get counter
"256598"

# MongoDB
> db.benchmarks.insert({"name":"key1", "number":1})
> db.benchmarks.update({"name":"key1"}, {"$inc": {"number":1} })
> db.benchmarks.find();
{ "_id" : ObjectId("50c8dc945139fe0dcad11646"), "name" : "key1", "number" : 11 }


☆ 計測方法
Apache Benchという、HTTPサーバーの性能を調べるソフトウェアを使って、様々なDBが単位時間あたりにどのぐらいのRequestをさばけるか調査しました。インクリメントするベンチマークです。具体的なコマンドは、

# ab -n 10000 -c 10 http://192.168.0.100/benchmarks/phpredis.php

です。クライアント(192.168.0.150)のターミナルから、↑のコマンドを打ち鯖A(192.168.0.100)がそれを捌きます。n: リクエスト数、c: 同時接続数らしいです。nは固定で、cを変えて計測します(Apache Benchについてよく分かっていないのでアレですが、↑の例だと10同時に接続して、合計1000回のリクエストになるまで計測するという意味なのでしょうか?誰か教えてください><)。リクエスト数は1万で固定、同時接続数をいろいろと変えて試してみます。ベンチ後、Failed Requestが出ていたら、topコマンドで確認してapacheのプロセスが残っていたらapache再起動して一度だけcurlでアクセスします。また、異なるDBを使う前にはやはりApacheを再起動して一度だけcurlでアクセスしてから計測しています。計測回数は1回だけです。apache benchなのかサーバーのapacheが原因なのか分かりませんが、同時接続数が250を超えてくるとエラーが出たりするようになりますのでそこで打ち止めにしています。”apr_socket_recv: Connection reset by peer (104)" ←こういうエラーが出たりします。

参考のために、静的なhtmlファイルも測定しました。

レコードが極めて小さいので、インメモリ処理の速度のベンチマークになっていますが。。。

MySQLですがMyISAMを使っていました。

mysql>show table status\G
*************************** 1. row ***************************
&#160; &#160; &#160; &#160; &#160; &#160;Name: incr
&#160; &#160; &#160; &#160; &#160;Engine: MyISAM
&#160; &#160; &#160; &#160; Version: 10
&#160; &#160; &#160;Row_format: Dynamic
&#160; &#160; &#160; &#160; &#160; &#160;Rows: 1
&#160;Avg_row_length: 20
&#160; &#160; Data_length: 20
Max_data_length: 281474976710655
&#160; &#160;Index_length: 1024
&#160; &#160; &#160; Data_free: 0
&#160;Auto_increment: NULL
&#160; &#160; Create_time: 2012-12-13 00:36:56
&#160; &#160; Update_time: 2012-12-13 23:32:54
&#160; &#160; &#160;Check_time: NULL
&#160; &#160; &#160; Collation: utf8_general_ci
&#160; &#160; &#160; &#160;Checksum: NULL
&#160;Create_options:&#160;
&#160; &#160; &#160; &#160; Comment:&#160;
1 row in set (0.00 sec)

☆ 結果

同時接続数 1 (ab -n 10000 -c 1 http://192.168.0.100/benchmarks/***.php)

DB名 Request Per Sec [#/sec]
静的ファイル 2575.24
MemSQL 786.83
MySQL 972.82
Redis 958.11
MongoDB 997.91



同時接続数 5 (ab -n 10000 -c 5 http://192.168.0.100/benchmarks/***.php)

DB名 Request Per Sec [#/sec]
静的ファイル 9072.13
MemSQL 1935.80
MySQL 2137.21
Redis 2446.51
MongoDB 2964.15



同時接続数 10 (ab -n 10000 -c 10 http://192.168.0.100/benchmarks/***.php)

DB名 Request Per Sec [#/sec]
静的ファイル 14391.80
MemSQL 1861.21
MySQL 2071.94
Redis 2411.20
MongoDB 2935.10



同時接続数 25 (ab -n 10000 -c 25 http://192.168.0.100/benchmarks/***.php)

DB名 Request Per Sec [#/sec]
静的ファイル 13811.39
MemSQL 1880.41
MySQL 2035.98
Redis 2430.54
MongoDB 2921.33



同時接続数 50 (ab -n 10000 -c 50 http://192.168.0.100/benchmarks/***.php)

DB名 Request Per Sec [#/sec]
静的ファイル 13505.19
MemSQL 1850.73
MySQL 1986.39
Redis 2378.09
MongoDB 2904.16



同時接続数 75 (ab -n 10000 -c 75 http://192.168.0.100/benchmarks/***.php)

DB名 Request Per Sec [#/sec]
静的ファイル 13720.13
MemSQL 1868.24
MySQL 2057.14
Redis 2438.65
MongoDB 2893.70



同時接続数 100 (ab -n 10000 -c 100 http://192.168.0.100/benchmarks/***.php)

DB名 Request Per Sec [#/sec]
静的ファイル 13178.53
MemSQL 1868.09
MySQL 2038.22
Redis 2422.57
MongoDB 2894.46



同時接続数 250 (ab -n 10000 -c 250 http://192.168.0.100/benchmarks/***.php)

DB名 Request Per Sec [#/sec] 備考
静的ファイル 7024.53 Failed Requestこそないものの、ガクンと遅くなりました。同時接続数が多すぎる感です
MemSQL 1590.09 httpdが発生していました。
MySQL 1595.75 httpdが発生していました。
Redis 1600.28 やはりhttpdが出てき始めました。
MongoDB 1600.16 ここでもhttpdが発生

# 同時接続数250あたりからエラーが頻発しはじめたのでここで打ち止めにします。

おまけ: リクエスト数10万、同時接続数50でやってみました。具体例-> "ab -n 100000 -c 50 http://192.168.0.100/benchmarks/***.php"。各DBの測定前に毎回 httpd を再起動しています。

DB名 Request Per Sec [#/sec] Failed requests [%] 備考
静的ファイル 13927.32 0 単純なhtmlはそりゃまあ高速ですね
MemSQL 642.98 19.7 エラー率が20%近いですね
MySQL 2001.63 0 Failed requestsなし!さすが安定のMySQL
Redis 686.96 19.3 Failed requestが20%!/(^o^)\。ただ、phpredisを使っている影響があるかも?
MongDB 2822.92 0 エラーなし、そしてこの速度!

☆ 総評

まずは計測の感想から。同時接続数が250を超えてくるとエラーが出始めます。何が原因かいまいちわかっていません。同時接続数は、OS、ApacheNICになどにより左右されそうですので。。。

以下、それぞれについて書いていきます。

静的なファイル: 単なるhtmlです。このシステムでの限界性能が測れるはず、です。10K req/sec 以上出ていますね。これを100K とかにしたいところなのですがどうすればいいのでしょうね。。。

MemSQL: MySQLのタイポではなく、MemSQLです。MySQLをインメモリデータベースにしてみたよー、というコンセプトらしいです。http://www.memsql.com/#download ←ここでフォームを入力してダウンロードできます。割と期待していましたが、(´・ω・`)ガッカリ…な速度でした。インメモリなのは結構なのですが昨今のRDBはデータがメモリに載るならほぼ間違いなくインメモリで動くので、優位性がなさそうですね。ただ、MemSQLの実行時に、

WARN: SSE4.2 is not supported. Resorting to software CRC32C.
Warning: The recommended configuration for MemSQL is at least 8 GB of RAM

と言われました。今回、DBサーバーはCore2時代のPentium Dual Core、E5200ですのでSSE4.2に対応していないのでしょう。また、DDR2-800の4GBDRAMしか詰んでいません。最新のIvyBridgeを使い、DDR3-2400のメモリを4channelにすればだいぶ結果は異なってくるかも知れません(たぶん他のDBも同様に早くなりますがね!)。

MySQL(MyISAM): いろんなところで使用されているだけあってそれなりの速度です。↑のおまけの測定結果を見てもらうと分かると思いますが、非常に安定性が高いです。むちゃなリクエストを出しても何とかしてくれます。本や資料、サポート会社がたくさんありますので、DB選択の本命と言えるでしょう。InnoDBのときも比較したいですね。

Redis: MySQLと比較して+20%の速度ですね。なかなかです。しかし安定性にかなり疑問。。。Failed Request出まくり。。。

MongoDB: この中で一番高速です。MySQL(これは5.1ですが)より50%近く高速です。人気があるだけあるなぁという印象です。安定性も高く、RDBが嫌でNoSQLを使いたいならMongoDBが良さそうです。ちなみに、最大で 600KB / s 程度のTransfer Rateになっていました。DB鯖だと、10MbpのNICで足りるのかもしれませんね(最新のNICのRTTなどは改善されている可能性は十分あるので何とも言えませんが)。

まとめ:
安定性 MySQL >= MongoDB >> MemSQL = Redis
高速性 MongoDB > Redis > MySQL > MemSQL
使うならMySQLかMongoDBですね(もしくはどちらも)。Redisは安定性がなさすぎて、MemSQLは早くもなければ安定でもないという誰得状態です(私のベンチのとり方が悪いのかな?)

追記@2012年12月14日

何となく鯖Aとクライアント(Bとします)を入れ替えてみました。そしたら、MongoDBやRedisが、4000 req/sec を超えてきました。静的ファイルだと16512.85 req/sec も出ています。MySQL(MyISAM)だと2911.94!。50%近く向上しています。うーん、鯖AはCore2時代のCPUでDDR2-800、クライアントBは、Sandy世代のCeleron G530でDDR3-1066で、そこまでスペックに差がある感じではないのですが。。。ただ、Sandy世代ということでCPU性能の向上、キャッシュへのヒット率の向上、メモリがちょっぴり高速、あたりが原因かもしれません。

ちなみにi5-3570Kを持っていてWindowsを入れているのですが、こちらでも実験したくなって来ました。MongoDBで6000 req/secぐらい行くのかも? また、研究室にSandyBridge-Eなi7があるのでそれで検証してみたいですね。L2キャッシュてんこ盛りなCPUだとすごいことになるのではと思ったり。

また、MySQLのPDOで、

$pdo = new PDO($mysql, $user, $pass, array(PDO::ATTR_PERSISTENT => true));

というものを使ってみました。コネクションが保持されるらしいです。使いまわしするみたいですね。たしかにいちいち切断するのは無駄ですね。

接続プーリング OFF↓

# ab -n 100000 -c 10 http://192.168.0.100/benchmarks/mysql.php
Requests per second:    2007.30 [#/sec] (mean)


接続プーリング ON ↓

# ab -n 100000 -c 10 http://192.168.0.100/benchmarks/mysql2.php
Requests per second:    2518.83 [#/sec] (mean)

ひゃー、30%近く高速化してしまった!すごいですね!効果絶大。MySQL最強フラグな気がしないでもないですね(MyISAMではなくInnoDBも試さねば。。。)


まとめ:
測定に用いたデータ(レコード)がメモリで収まることに留意しつつも、MySQLが予想外に早いです。phpredisは+20%ぐらい早く見えると思いますがとにかく安定性が悪いです。かなり苦労しました。しょっちゅうFailed requestsを出してきます。リクエスト件数が多くても同時接続数が多くても出ます。同じ条件でも出る時と出ない時があります。一方でMySQLは総じて安定しています。MongoDBもかなり安定しています。

ソースを見てもらえると分かると思いますが、phpredisの方は、incr一回、MySQLの方はクエリ二回なので明らかにMySQLが不利だろうと思っていましたが、結果的にはかなり均衡していました。

とにかく安定性が段違いですし、速度もそう変わらないので、RedisとMySQLだけを比較すればMySQLを使うべきなのではないかと感じました。

MongoDBはかなり高速ですね。3000req/sec近いのは魅力的です。

書いていて思いましたが、メモリにおさまるデータサイズならどのDBも2000req/sec程度出そうな雰囲気です。だからどれを使ってもいいんじゃないでしょうか(ぉ。安定性重視だとやはりMySQLですね。RedisはインメモリDBだから高速!と思っていたら他のDBもメモリにデータがおさまるならメモリアクセスしちゃうので実質インメモリDB状態だったでござるの巻ですね。

ちなみに、クライアント側のNICIntelNIC(PCI-Ex版の一番安いもの)に変えてみると、+10%程度になりました。MongoDBで最高3000 req/secを超えました。サーバー側の方も交換して計測してみたいですね。

ToDo:
グラフ化する


☆ 疑問点
localhostでのRTTが0.1msecぐらいということは、秒間に1万request程度しか原理的に出来ない計算になります。さらに鯖A、鯖B間のpingのRTTはもっとあります。そんな状態で2000req/s程度出ていたのは立派な気がします。ただ、巷では、10万req/s行ったぜーなんて話を聞きますが、本当にそんな速度出るのかなと疑問です。NICをたくさん使うとかするのでしょうか。今回は鯖一つにつきNIC一個でしたが。高速化技術を知りたいですね。今回、ルーターを一つ挟んで、すべてGbEで接続して計測しましたが、ネットワークがボトルネックにも思えます。さらに言えばたとえば鯖A内でMySQLを走らせた場合、鯖Aがクライアントからリクエストを貰った時にunix socketでMySQLと通信すると思いますが、そのときのunix socketもネックではないでしょうか(これはおそらく ping localhost のRTTですよね)。

また、PHPで比較するのが適切なのが疑問になって来ました。せめてJavaを使うべきだったかもしれません。

うーん、今回のシステムでは、アプリケーションとDBが一体化しています。tcp/ipを使わないほうが早いだろうと思ってそうしたのですが、アプリケーション・サーバー10台、DBサーバー1台の構成であれば、もっとQuery Per Secが出るのかもしれませんね。。。

☆ ソース

static_file.html

counter: 12345

memsql.php

<?php

try {
        $mysql = "mysql:host=127.0.0.1;port=3307;dbname=MemSQL_tutorial";
        $user = "root";
        $pdo = new PDO($mysql, $user);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        //$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

        $stmt1 = $pdo->prepare("update incr set number = number +1");
        $stmt2 = $pdo->prepare("select number from incr");

        //$pdo->beginTransaction();
        $stmt1->execute();
        $stmt2->execute();
        //$pdo->commit();
        $row = $stmt2->fetch(PDO::FETCH_ASSOC);

        printf("counter: %s\n", $row["number"]);
} catch (PDOException $e) {
        printf("Error: データベース接続に失敗しました。\n");
        print $e->getMessage();
        die();
}
?>

mysql.php

<?php

try {
        $mysql = "mysql:host=localhost;port=3306;dbname=benchmark";
        $user = "bm-user";
        $pass = "bm-user";
        $pdo = new PDO($mysql, $user, $pass);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        //$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

        $stmt1 = $pdo->prepare("update incr set number = number +1");
        $stmt2 = $pdo->prepare("select number from incr");

        $pdo->beginTransaction();
        $stmt1->execute();
        $stmt2->execute();
        $pdo->commit();
        $row = $stmt2->fetch(PDO::FETCH_ASSOC);

        printf("counter: %s\n", $row["number"]);
} catch (PDOException $e) {
        printf("Error: データベース接続に失敗しました。\n");
        print $e->getMessage();
        die();
}
?>

phpredis.php

<?php

$redis = new Redis();
if ($redis->connect('localhost', 6379)) {
        //print("redis connect OK");
} else {
        die("Connection to Redis Failed.");
}

print "counter: " . $redis->incr("counter") . "\n";

?>

mongodb.php

<?php

try {
        $link = new Mongo('localhost:27017');
        $db = $link->test;
        $col = $db->benchmarks;

        //はじめに追加するときはこれ。一旦加えたらあとはコメントアウト。
        //$doc = array('key' => 'value', 'number' => 1);
        //$col->insert($doc);
        
        //increment
        $col->update(array('key' => 'value'), array('$inc' => array('number' => 1)));
        $obj = $col->findOne();
        printf("counter: %s\n", $obj['number']);
} catch (MongoConnectionException $e) {
        die('error');
}

☆ おまけ

各種DB実行方法:

MemSQL (CentOS)

# MemSQLの公式サイトからバイナリを落としてきます

# ↓ memsqldを実行(DRAMが8GB以上ないとWarning出ますが気にしない)
# ./memsqld --port 3307 -u root

# ↓他の端末で行う、memsqlのcliモードに入る
#  mysql -u root -h 127.0.0.1 -P 3307 --prompt="memsql> "

# データベースとテーブルを作って値を挿入
memsql> CREATE DATABASE MemSQL_tutorial;
memsql> USE MemSQL_tutorial;
memsql> create table incr(key1 varchar(16), number int);
memsql> insert into incr values("value1", 1);
memsql> update incr set number = number +1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
memsql> select * from incr;
+--------+--------+
| key1   | number |
+--------+--------+
| value1 |      2 |
+--------+--------+
1 row in set (0.00 sec)

# memsqldを落とすときは↓
# ps aux | grep mem
root       358  0.2  2.4 1483012 197424 pts/0  Sl+  19:05   0:03 ./memsqld --port 3307 -u root
root       362  0.0  0.0 147204  1920 pts/0    Sl+  19:05   0:00 ./memsqld --port 3307 -u root
root     20633  0.0  0.0 107464   992 pts/1    S+   19:31   0:00 grep --color mem
[root@g530: ~]
# kill 358
[root@g530: ~]
# kill 362

CentOSのMySQL5.1のmy.cnfを秘伝らしきものに変えてみた。

今までは、utf-8を追加するぐらいだったのですが。。。

MySQLの設定ファイル my.cnf をgithubにて公開しました & チューニングポイントの紹介 - blog.nomadscafe.jp
↑こんなのを見かけたので。。。

事前準備

  • my.cnfのバックアップ
  • innoDBのログの削除
  • MySQLの再起動
# cp /etc/my.cnf /etc/my.cnf.bk
# cd /var/lib/mysql
# rm ib_log*
# service mysqld restart
↑ 失敗するようなら、"less /var/log/mysqld.log" を見ましょう。

/etc/my.cnf (元のものをCentOSに合わせていじっています) ↓

[client]
port = 3306
socket = /var/lib/mysql/mysql.sock

[mysql]
no-auto-rehash
#safe-updates
prompt = '\u@<HOSTNAME> mysql>'

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
port = 3306
user=mysql
skip-external-locking
key_buffer = 32M
max_allowed_packet = 8M
table_cache = 256
max_connections = 1024
max_connect_errors = 10000
sort_buffer_size = 1M
read_buffer_size = 1M
myisam_sort_buffer_size = 1M
thread_cache = 256
query_cache_size = 0M
query_cache_type = 0
thread_concurrency = 8
tmp_table_size = 64M
max_heap_table_size = 64M
skip-name-resolve
character-set-server=utf8
default-storage-engine=InnoDB

log-bin=mysql-bin
relay-log=relay-mysql-bin
binlog_format=mixed
log_slave_updates
replicate-ignore-db=mysql

slow_query_log
long_query_time = 3

#server-id = <SERVER_ID>

ignore-builtin-innodb
plugin-load=innodb=ha_innodb_plugin.so;innodb_trx=ha_innodb_plugin.so;innodb_locks=ha_innodb_plugin.so;innodb_lock_waits=ha_innodb_plugin.so;innodb_cmp=ha_innodb_plugin.so;innodb_cmp_reset=ha_innodb_plugin.so;innodb_cmpmem=ha_innodb_plugin.so;innodb_cmpmem_reset=ha_innodb_plugin.so
innodb_strict_mode
innodb_data_file_path = ibdata1:10M:autoextend
innodb_data_home_dir = /var/lib/mysql
innodb_file_format = Barracuda
innodb_file_per_table
innodb_buffer_pool_size = 2G
innodb_additional_mem_pool_size = 20M
innodb_write_io_threads = 8
innodb_read_io_threads = 8
innodb_thread_concurrency = 16
innodb_flush_log_at_trx_commit = 1
innodb_log_file_size = 512M
innodb_log_buffer_size = 16M
innodb_log_files_in_group = 2
innodb_flush_method=O_DIRECT
innodb_lock_wait_timeout = 120

[mysqldump]
quick
max_allowed_packet = 16M

[myisamchk]
key_buffer = 8M
sort_buffer_size = 8M
read_buffer = 1M
write_buffer = 0M

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

innodb_buffer_pool_size = 2G としてますがメモリ搭載量にあわせてお好みで。

結果
【記事途中】各種Databaseの性能を Apache Bench でいろいろと比較【MySQL, MemSQL, Redis, MongoDB】 - 自分用備忘録
↑これと比べてみたけれど変化ありませんでした/(^o^)\

はてな記法で表について困っています。勝手にbrが挿入されているようで表が下の方になってしまいます。->部分的に解決しました。

部分的に解決しました。
「|」を用いて表を書いたあとに、改行を入れると下の方に下がっていくようです。改行を一切入れないでやるとそれなりに表示されました。

ご存知の方助けてください。

** 比較方法
Key-Valueっぽいデータを何万件かinsert or setしてそれにかかった時間を比較。Key-Valueっぽいデータの例↓
|*key|*value|
|id:1|1|
|id:2|2|
|id:3|3|


** マシン環境
- MacBook Pro Mid 2009
- Mountain Lion (10.8.2)
- 2.53 GHz Intel Core 2 Duo
- 4 GB 1067 MHz DDR3
- インテル&#174; Solid-State Drive 330 120GB

↑このように書きますと、↓のように表示されます。勝手にbrが入っているようです。。。

比較方法

Key-Valueっぽいデータを何万件かinsert or setしてそれにかかった時間を比較。Key-Valueっぽいデータの例↓

key value
id:1 1
id:2 2
id:3 3

マシン環境

【まだ途中】SQLite, Redis の Insert 比較 (By PHP)【ツッコミ歓迎】

動機

さまざまなDBの速度比較をしたかった。

各種DBへの個人的印象

比較する前に、どんなふうに思っているかを。。。ちなみに今回、私の比較では、検索とかしていないのでDatabaseってよりはI/Oなのかなと思ったり。RDBMSをそういうふうに使うのは本末転倒...?

SQLite

クライアントサーバー型ではないDB、つまりローカルでしか使えないらしい(とは言えアプリケーションに内蔵すればいい話ですが)がかなり高速らしい。

Redis

インメモリデータベースで、Key-Value系。と言っても単純なKey-Valueではないが。RedisはREmote DIctionary Serverの省略。メモリでやり取りするだけあって、超高速なのでは。ニコニコ動画でも部分的に使われているとのこと。どうやらRedisチームではCluster化に対応したい様子。ちなみにPredisというPHPのRedisツールでは、簡易的なクラスタリング手法のコードがExampleに載っている。試していないけれど。

MySQL【あとでこれも比較】

定番DB。ライセンスが商用OKなのか不明瞭、さらにはOracleに支配されていて将来不穏。と言いつつも未来永劫使える気がするが。。。そこらかしこで使われていてDBと言えばこれ。世界のブログの30%ぐらいはMySQLさんが裏で動いている感。

MySQL Cluster 【あとでこれも比較】

MySQLクラスタ。たくさん束ねて冗長化+高速化?あまりよく知らないがインメモリらしい。

PostgreSQL 【あとでこれも比較】

定番DB。ライセンスはBSD・MITライクらしく商用利用も安心。日本で人気?

pgpool-II 【あとでこれも比較】

PostgresSQLのクラスタ版。あまり良く知らない。開発は結構盛んのよう。

Firebird 【あとでこれも比較】

日本ではあまり有名でない気がするDB。MySQL, PostgresSQL, FirebirdオープンソースRDBMS御三家。正直なところ私もよく知らない。

Cassandra 【あとでこれも比較】

Facebookの連中が開発したらしいKey-Value系DB。使ったことがないから何とも言えない。リニアにスケールするとかなんとか。

HBase 【あとでこれも比較】

HadoopベースのDB?よく知らない。

MongoDB 【あとでこれも比較】

Key-Value系では一番有名?大人気感。

比較方法

Key-Valueっぽいデータを何万件かinsert or setしてそれにかかった時間を比較。Key-Valueっぽいデータの例↓

key value
id:1 1
id:2 2
id:3 3

マシン環境

DB環境

すべてローカルで完結するように行った。1台のマシンで完結する環境である。クライアントサーバー型ではなくローカルである。

  • SQLite3 (3.7.14.1)
  • Redis (2.6.7)

ソースコード

このポストの下の方(あとでGitHubに置きます)

結果

処理時間

件数: 1万件

DB名 条件 時間(sec)
PHP PHPのarray配列に入れてみた 0.035
Redis Predis使った 1.325
SQLite3 毎回のinsertでtransaction。つまりtransaction1万件 10.252
SQLite3 1万件のinsertを1度のtransactionで済ませた 0.143
総評

上記の結果より、コードの中で配列で持つのが最強!を示した( ー`дー´)キリッ
とは言えそれではやりにくいので、DBを使うわけであるが。

Redisはやはり高速。おそらく内部的にunix socketを使ってのこの結果なわけだが、それにもかかわらず1万IOPS近く出ているのは優秀と言えるだろう。もしかするとほとんどunix socketの処理に時間が取られているのかもしれないとすら感じる。

SQLiteは、毎回トランザクションが掛かるとそれなりに時間が掛かっていた。SSDでもこの程度掛かるわけか。一度のトランザクションで済ませるとRedisを上回る早さを見せるが、一回の書き込みで1万件行うわけでそりゃ高速である。Webサービスからのアクセスを考えると、1万件一気にどうのこうのというイベントはあまり発生しないので、参考にはならないだろう。バッチ処理的な場面では有効かもしれない。

ソースコード

(注意点: SQLiteの処理で、「$pdo->query("drop table test1");」とあるが、↓のコードの初回実行時にはこれをコメントアウトする必要がある。2回目以降は、この処理を書くと良い。)

<?php
//*** Redis に set ****

require_once('./../Predis/Autoloader.php');
Predis\Autoloader::register();

$client = new Predis\Client(array(
			'scheme' => 'tcp',
			'host' => 'localhost',
			'port' => 6379,
		));
try {
	$client->connect();
} catch (Exception $e) {
	print $e->getMessage();
	die("\nRedis/(^o^)\");
}

$client->flushall();

$num = 10000;
$start = microtime(true);
for ($i = 0; $i < $num; $i++) {
	$str_i = strval($i);
	$client->set("id:" . $str_i, $str_i);
}
$end = microtime(true);
printf("%.3f sec <- Redis の %s 件の Set\n", (double)($end - $start), $num);


//*** SQLite3 に set (毎回の insert ごとに transaction版。こちらが実際に近い?)****
$pdo = new PDO('sqlite:./db.sqlite3', "", "");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->query("drop table test1");
$pdo->query("create table test1 (key varchar(32), value varchar(32))");
$stmt = $pdo->prepare("insert into test1 values (:key, :value)");
$start = microtime(true);
for ($i = 0; $i < $num; $i++) {
	$str_i = strval($i);
	$key = "id:" . $str_i;
	
	$pdo->beginTransaction();
	$stmt->bindParam(":key", $key);
	$stmt->bindParam(":value", $str_i);
	$stmt->execute();
	$pdo->commit();
}
$end = microtime(true);
printf("%.3f sec <- SQLite3 の %s 件の Insert(毎回の insert で transaction)\n",(double)($end - $start), &#160;$num);


//*** SQLite3 に set (すべてのinsertを1回のtransactionでこなす。こちらは非現実的?)****
$pdo = new PDO('sqlite:./db.sqlite3', "", "");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->query("drop table test2");
$pdo->query("create table test2 (key varchar(32), value varchar(32))");
$stmt = $pdo->prepare("insert into test2 values (:key, :value)");
$start = microtime(true);
$pdo->beginTransaction();
for ($i = 0; $i < $num; $i++) {
	$str_i = strval($i);
	$key = "id:" . $str_i;
	
	$stmt->bindParam(":key", $key);
	$stmt->bindParam(":value", $str_i);
	$stmt->execute();
	
}
$pdo->commit();
$end = microtime(true);
printf("%.3f sec <- SQLite3 の %s 件の Insert(すべてのinsertをまとめて1回のtransaction)\n",(double)($end - $start), &#160;$num);


// *** PHP内での配列生成
$php_array = array();
$start = microtime(true);
for ($i = 0; $i < $num; $i++) {
	$str_i = strval($i);
	$php_array[] = array("id:" . $str_i => $str_i);
}
$end = microtime(true);
printf("%.3f sec <- PHP内 の %s 件の Array Set\n", (double)($end - $start), $num);
// var_dump($php_array);

//MySQL版
$mysql = "mysql:host=192.168.0.150;port=3306;dbname=test";
$user = "test_user";
$pass = "test_user";
$pdo = new PDO($mysql, $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); //trueだとSQL文が1回で済む(そのぶんセキュリティが... -> と思ったが1万件で0.5sec遅くなった/(^o^)\
$pdo->query("drop table test_mysql");
// MySQL では、「key」という項目名でエラーなので、key1という名前にした↓
$pdo->query("create table test_mysql (key1 varchar(32), value1 varchar(32))");
$stmt = $pdo->prepare("insert into test_mysql values (:key1, :value1)");
$start = microtime(true);
for ($i = 0; $i < $num; $i++) {
	$str_i = strval($i);
	$key = "id:" . $str_i;
	
	$pdo->beginTransaction();
	$stmt->bindParam(":key1", $key);
	$stmt->bindParam(":value1", $str_i);
	$stmt->execute();
	$pdo->commit();
}
$end = microtime(true);
printf("%.3f sec <- MySQL(Remote) の %s 件の Insert(1件のinsertごとにtransaction)\n",(double)($end - $start),  $num);
?>
生のoutput

(sandbox.phpという名前にしている)

$ php sandbox.php 
1.325 sec <- Redis の 10000 件の Set
10.252 sec <- SQLite3 の 10000 件の Insert(毎回の insert で transaction)
0.143 sec <- SQLite3 の 10000 件の Insert(すべてのinsertをまとめて1回のtransaction)
0.035 sec <- PHP内 の 10000 件の Array Set