BizStationブログ

ビズステーション株式会社の公式ブログです。

MySQLからNoSQLへの移行に「ちょっと待った」

Transactdの開発過程でMySQLのパフォーマンス問題を調べてみたところ、ほとんどは「クエリーが遅い」といった問題のようでした。また、NoSQLデータベースへの移行理由としても「MySQLが遅いから」といった意見が結構あります。しかし、そのような理由でMySQLからNoSQLに移行しようとしているのなら、「ちょっと待った」です。(他の理由なら別ですが。)

NoSQLに移行すれば何もかもがバラ色というわけではありません。本当に移行する必要があるか、これを読んで考えてみてください。

TransactdはNoSQLですが、MySQLでもあります。同じMySQLでNoSQLを実現できるTransactdはこの話の中ではMySQLに入れて考えてください。

遅い理由

一口に遅いといっても、いくつかの意味があります。

応答速度が遅い

応答速度を見るには、マスターデータにselect * where id = x from xxxといった1レコードを取得するクエリを投げて、どれくらいのスピードで返るか比較するのが適当でしょう。サーバーだけでなく、ネットワーク、クライアントライブラリも含めた計測で評価する必要があります。

データがメモリ上にあることを前提とすると、応答速度を決定する主な要因は、ネットワークのレイテンシです。ネットワークのレイテンシについては、TCPであればSQLでもNoSQLでも同じだけかかります。

SQLだと、このような簡単なクエリでも、構文解析を行うためNoSQLより遅くなります。しかし、Transactdには構文解析はありません。同じネットワークであれば他のNoSQLとほとんど同じ速度です。

これを確かめるために、実際にmemcached 1.4.5と比較してみました。1つ約100バイトのレコードを2万件用意して、1レコードずつ20,000回の読み取りにかかる時間を計測しました。同じサーバーマシン、同じクライアントマシンで、クライアントはすべてPHP5.5で比較しています。

[MYSQL(PDO)]
7.3037860393524 sec
2738.3058447004 records/sec

[memcached]
5.2722289562225 sec
3793.4619619269 records/sec

[Transactd]
5.6204497814178 sec
3558.4340716153 records/sec
[TEST Environment Spec]
Server    : HP Proliant DL165G7 AMD Opteron(TM) Processor 6212 / 10GB RAM Intel 1Gbps NIC
Client    : HP Elite book 9470 Core i5 8GB RAM Intel 1Gbps NIC
MySQL     : 5.6.14 64Bit
Transactd : 2.0.0 64Bit
memcached : 1.4.5 64Bit

memcachedが少し速い結果になりましたが、Transactdと大きな差はありません。innodbは十分に高速ですね。Transactdを使えば、他のNoSQLに変更しても応答速度は大きく変わりません。
(「ホントにmemcachedと変わんないの?」と思う方は多いでしょう。TranasctdをネットワークレイテンシのないWindowsローカルで動作させると上の条件で1秒くらいになります。ネットワークの往復2万回で4秒ほどかかっている計算です。これはmemcachedも同じですからほとんど差がないのです。)

クエリが遅い

クエリが遅いのは、サーバー側で大量のレコードを読み取ってしまったり、大量のデータを保持しソートするなどしているからです。適切なindexを使って限られた範囲を読み取れば、(クライアント数の増大など以外では)予想外のパフォーマンスになってしまうことはありません。もう一度クエリを見直してみましょう。

大切なのは、どのindexを使ってどこからどの範囲を読み取るのか、その範囲にはおよそどの程度のレコードがあるのかを、将来も含めて想定することです。
SQLだけで操作してきた場合は、これらを想定するのは難しいように思えるかもしれません。しかし、これが出来ないと、他のNoSQLを使用したプログラミングはもっと難しくなります。

NoSQLではkey/valueでデータを格納していきます。シンプルでいいのですが、たとえばvalueでの絞り込みや検索を行う際に、すべてのデータを取得して探したのでは、とても遅くなってしまいます。これを回避するにはIndexテーブルなどを作成して、事前にどこにデータがあるかkeyを保存する処理などが必要になります。

実はこの「Indexテーブルなどの作成」は、先ほど強調した大切な事項とほぼ同じことを言っています。遅いクエリを書いてしまうのは、indexを使ったアクセスと読み取り範囲を想定できていないからであり、たとえNoSQLでもそこから逃れることはできないのです。

よりよいクエリを書くための提案があります。TransactdのQueryBuilderを使ってクエリを作成してみてください。QueryBuilderでは、どのインデックスを使用してどの値から読み取るのかを最初に決めなくてはなりません。同時にconditionsで終わりの条件も指定します。もし、どちらも指定しなければ、そのテーブルの全レコードを検索します。

「縛りがきついな」と思われた方、オプティマイザの夢物語はそこまでにしましょう。MySQLオプティマイザが決める実行計画も、何ひとつ変わらない全く同じ縛りの中で決定されています。

その他

その他にパフォーマンスで大切なことは、データがキャッシュに載っているかです。ディスクへの物理アクセスはとても低速です。サーバーに十分なメモリを搭載し、innodb_buffer_pool_sizeに大きなメモリを割り当てましょう。

NICは1Gbps以上のものを使用し、ドライバーのRSS(recieve side scaling)を有効にしてCPU負荷を分散させます。MySQLはCPU負荷が高いのでなるべく高速でコアの多いものを使います。

この辺はMySQLに限ったことではありません。NoSQLでもほとんど同じです。

まとめ

いかがでしたでしょうか?NoSQLに移行したからといって「遅い」が簡単には改善しないことがお分りいただけたと思います。Transactdであれば、MySQLのまま部分的に置き換えて高速化することもできます。

また、クエリの見直しには是非QueryBuilderを使ってみてください。作りながら実行できるので、すぐにindexを使ったアクセスの仕方と大切さを実感できます。理解が進めばパフォーマンスの良いSQLが自然に書けるようになるかと思います。教育や学習ツールにもなりますね!

最後に、関連記事を貼っておきます。

Transactd 2.0 その2 QueryBuilder で簡単NoSQLクエリー - BizStationブログ


Transactd 2.0 その3 データベーススケーリング - BizStationブログ