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
☆ 接続とRTT
鯖A <= GbE => NECルーター(GbE、Aterm 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>show table status\G *************************** 1. row ***************************            Name: incr          Engine: MyISAM         Version: 10      Row_format: Dynamic            Rows: 1  Avg_row_length: 20     Data_length: 20 Max_data_length: 281474976710655    Index_length: 1024       Data_free: 0  Auto_increment: NULL     Create_time: 2012-12-13 00:36:56     Update_time: 2012-12-13 23:32:54      Check_time: NULL       Collation: utf8_general_ci        Checksum: NULL  Create_options:          Comment:  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、Apache、NICになどにより左右されそうですので。。。
以下、それぞれについて書いていきます。
静的なファイル: 単なる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状態だったでござるの巻ですね。
ちなみに、クライアント側のNICをIntelのNIC(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(); } ?>
<?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
↑こんなのを見かけたので。。。
事前準備
# 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 - インテル® Solid-State Drive 330 120GB
↑このように書きますと、↓のように表示されます。勝手に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
- インテル® Solid-State Drive 330 120GB
【まだ途中】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さんが裏で動いている感。
PostgreSQL 【あとでこれも比較】
定番DB。ライセンスはBSD・MITライクらしく商用利用も安心。日本で人気?
pgpool-II 【あとでこれも比較】
PostgresSQLのクラスタ版。あまり良く知らない。開発は結構盛んのよう。
Firebird 【あとでこれも比較】
日本ではあまり有名でない気がするDB。MySQL, PostgresSQL, Firebird がオープンソースRDBMS御三家。正直なところ私もよく知らない。
HBase 【あとでこれも比較】
HadoopベースのDB?よく知らない。
MongoDB 【あとでこれも比較】
Key-Value系では一番有名?大人気感。
比較方法
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
- インテル® Solid-State Drive 330 120GB
DB環境
すべてローカルで完結するように行った。1台のマシンで完結する環境である。クライアントサーバー型ではなくローカルである。
- SQLite3 (3.7.14.1)
- Redis (2.6.7)
結果
処理時間
件数: 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),  $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),  $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