メイン

2010年8月13日

MySQLのチューニングのためのデータの集め方
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

いつの間にか会社で古株になったyamaokaです。

webアプリケーションのバックエンドにMySQLを使っている場合、 クエリ(SQL)のチューニングをする必要がありますよね。 皆さんはチューニングの計画をどのように立てていますか。

もちろん、既に明らかに重いことが想定されているページがあれば、 その処理で使われているクエリを中心にEXPLAINなどを使って解析していけばいいと思います。

でもそうではなく、全体的にクエリの見直しやチューニングを行いたい場合は 実際に実行されているクエリを確認していくという作業が必要です。 そこで使うことができる3つの方法について書きたいと思います。

遅いクエリを記録する

MySQLにはスロークエリログといって、 実行に時間がかかったクエリを記録する機能が最初から付いています。 /etc/my.cnfに次のように設定を書けば実行時間が1秒を超えたクエリが出力されるようになります。

slow_query_log = 1
slow_query_log_file = /path/to/mysql-slow.log
long_query_time = 1
オンラインでset globalを使って変更する場合は次のようにします。
set global slow_query_log = 1;
set global slow_query_log_file = '/path/to/mysql-slow.log';
set global long_query_time = 1;

出力されたログファイルをMySQLに付属しているmysqldumpslowというツールで解析すると便利です。次のように実行すれば、平均の実行時間が長い順にソートして表示してくれます。

mysqldumpslow -s at /path/to/mysql-slow.log
クエリのパラメータは数値はN、文字列はSに置換して表示してくれるので 同じクエリをまとめてチェックすることができます。

実行回数の多いクエリを記録する

実行時間は短いけれど多くの回数実行されるクエリというのもあります。 これらは通常のスローログには出てこないのですが、 実は負荷の大部分を占めている、ということもありえます。 キャッシュなど別の方法を考えることで アプリケーションの負荷を減らすことができるかもしれません。

従来MySQLではlong_query_timeに1秒以上の値しか設定できませんでしたが、 MySQL 5.1から1秒未満の値も設定できるようになりました。 つまり、次のように/etc/my.cnfで「0」を設定すれば 全てのクエリが記録できることになります。

slow_query_log = 1
slow_query_log_file = /path/to/mysql-slow.log
long_query_time = 0

もちろん、全てのクエリの記録は負荷も大きくかかることを 理解してから設定を行うようにしてください。 また、ログの容量も大きくなるので、ディスクの空き容量にも注意が必要です。 オンラインでset globalを使って一時的に値を0に変更し、すぐに元の値(1など)に戻すのがオススメです。

set global slow_query_log = 1;
set global slow_query_log_file = '/path/to/mysql-slow.log';
set global long_query_time = 0;
# 後で set global long_query_time = 1; で元に戻す

出力されたログファイルをmysqldumpslowで次のように解析することで、 実行回数の多い順に表示することが可能です。

mysqldumpslow -s c /path/to/mysql-slow.log

mysqldumpslowは他にもいろいろ機能を持っているので、「--help」を付けて実行して 一度オプションを確認してみるといいかもしれません。

追記: MySQLでは/etc/my.cnfに次のような設定をすることで全てのクエリを記録することが可能です。 最初から全ての記録を保存したい場合はこちらの方法もいいかもしれません。

log = /path/to/mysql-query.log

インデックスを使っていないクエリを記録する

多くの場合、インデックスを使用しないクエリは遅いです。 インデックスの設計は計画的に行う必要があると思いますが、 今現在インデックスを使用していないクエリはどれなのか知りたい場合があると思います。 次のように/etc/my.cnfに記述することでインデックスを使用していないクエリを スロークエリログに記録することができるようになります。

slow_query_log = 1
slow_query_log_file = /path/to/mysql-slow.log
long_query_time = 5
log_queries_not_using_indexes = 1
オンラインでset globalを使って設定する場合は次のようにします。
set global slow_query_log = 1;
set global slow_query_log_file = '/path/to/mysql-slow.log';
set global long_query_time = 5;
set global log_queries_not_using_indexes = 1;

出力されたログファイルは今までと同じように mysqldumpslowを使って解析していくことになると思います。

終わりに

最近は、開発するときにフレームワークに付属のORマッパーを使ったりして 実際に発行されるクエリを意識しないことが多くなっていると思います。 もちろんそれはメリットだと思うのですが、実際に実行されるのはクエリ(SQL)である以上、 完全に意識しないで済むということはありません。

実際に発行されているクエリを眺めつつ、 少しでもパフォーマンスのよいwebアプリケーションを作っていけたらいいと思います。

追記: オプションの変数名がMySQL 5.1基準のものになっていなかったのでMySQL 5.1をベースに修正しました。

2010年6月11日

「ちわさん、奥さんが来ましたよ」
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

「え!?妻が会社に!?」と一瞬戸惑いましたが奥一穂さんでした。
別に妻が会社に来てもやましいことなど何もありませんが、こんちには、ちわです。こんにちわ。

以前の記事でもご紹介した通り弊社が提供しているまちつく!というサービスでは Q4M 0.9.0 を利用して地図の画像生成と Amazon S3 への転送を行っています。
その Q4M に障害が発生し弊社ではどうも解決できそうにないので Q4M の作者の奥一穂さんに相談させてもらいました。
その際は、デッドロックのバグを踏んでいる可能性があるので Q4M を 0.9.1 以上のものしてみてはどうかと返答を頂きました。
弊社では 64bit 環境であった為、0.9.2 は除外され、0.9.3 は新しすぎるので 0.9.1 を採用することにし、バージョンアップ後に障害もなく安定稼働を続けています。

そんな経緯もありまして、先日弊社に奥一穂さんが来社されることになり Q4M について10ページ程のスライドで Q4M の現状とこれからを紹介して頂いたり、弊社からいくつか質問をさせて頂く機会がありましたので今回は弊社からの質問とその回答をご紹介したいと思います。

queue_end() を呼ぶ頻度とデッドロックについて
ウ:(Q4M 0.9.0 の環境で)障害が起きたサービスと一度も障害が起きていないサービスがあります。障害の起きたサービスでは queue_wait() のループ内で毎回 queue_end() を呼んでいますが、他方では queue_wait() のループの外で queue_end() を呼んでいます。障害の起きたサービスでは頻繁に queue_end() を呼んでいることになりますが、queue_end() を頻繁に呼ぶことがデッドロックを誘発する原因になりうるのでしょうか?
奥(敬称略):まずデッドロックが発生するのは確率的な問題です。
queue_end() を頻繁に呼ぶことがデッドロックの発生率を高める主な要因にはならないと思います。

キューのリトライについて
ウ:キューのリトライの常套手段としてはどのようなものがありますか?
奥:例えば、優先度が高・中・低のテーブルを用意して高で失敗したキューをリトライ用として中のテーブルに入れます。
高が空になった時点でリトライ用の中に入っているキューを処理させる等でしょうか。

ウ:リトライを時間で制御したい場合はどのようにすればよいですか?例えば、キューの処理に失敗したらX秒後に処理するとか。
奥:リトライ用のキューに入れた時間とそのキューを取り出した時点での時刻を比較する。リトライ用のキューを取り出した時点でX秒経っていなかったら任意の時間 sleep する。
というのはどうでしょうか。

Conditional Subscription について
ウ:Conditional Subscription というのがありm
奥:あっ、Conditional Subscription 使ってますか?
ウ:使ってますね。使わないですか?
奥:私自身はあまり使っていないですね。
単純に queue_wait('table') するなら先頭からレコードを取り出せばよいですが、Conditional Subscription を使用すると条件に合致するレコードを走査する必要があるので溜まっているレコードが多ければそれだけコストがかかります。

アプリケーションとのアトミックな処理について
ウ:アプリケーションである処理の成功時に Q4M へ INSERT する、というロジックがあったとして、そのある処理は成功したけど Q4M への INSERT には失敗したという状況がありますが、このような場合はどうすればよいでしょうか?
奥:そうですね、Q4M ではなくて InnoDB でなんちゃってメッセージキューを実装するという解決方法はありますね。現状だとどうしようもないですね。Q4M でもトランザクションを扱えるようにしたいとは思っています。

処理したキューの統計情報について
ウ:例えば、一日に処理したキューの数を知りたいと思ったら SHOW ENGINE QUEUE STATUS; の数字を見ればよいのですか?
奥:そうですね。
ちょっとパースしづらいとは思いますが、その数字を見てください。

Parallel::Prefork について
ウ:Parallel::Prefork で fork している子プロセスが queue_wait() を呼んでタイムアウトになればその子プロセスは死にますが、現在のところ queue_wait() がタイムアウトする状況は把握していません。そうなると子プロセスはいつまでたっても死んでくれないという実装をしてしまったのですが Parallel::Prefork に Apache で言うところの MaxRequestsPerChild のようなものは実装される予定はありますか?
奥:ないですね。ImageMagick 等で画像を作っているのであればメモリリークも心配ですよね。必要であれば worker で実装してみてください。
ウ:はい、実装させて頂きます。

YAPC Asia 2010 について
ウ:YAPC Asia 2010 にスピーカーで参加する予定はありますか?
奥:今のところないです。

まとめ
「奥さんがウノウに来てくれるらしい」という話から実際に来社されるまでの時間が短く、奥さんへの質問の準備が足りなかったことが悔やまれますが以上が今回質問させて頂いた内容とその回答です。
どのような背景でどのように Q4M を使用するかは様々だとは思いますが、開発者の奥さんが Conditional Subscription をあまり使用していないというのは少々驚きました。まちつく!では Conditional Subscription は使用していませんが他プロジェクトでは使用しているようです。
同じ社内でも Q4M の使い方が違うので他社の方がどのように Q4M を使用しているかは気になるところです。

奥さんへ
お忙しい中来社して頂き、またウノウラボへの掲載を快諾していただきありがとうございました。

ウノウでは特に最近、積極的にエンジニアを採用しています。
採用ページをご覧になり興味のある方、ぜひご応募ください!!
Find Job!でも募集開始してます!

ウノウでは積極的にエンジニアを募集していますが、弊社にて勉強会を開いて頂けるエンジニアも募集したいと思っています。Twitter の弊社社長のリストのメンバーに向けて「勉強会がしたい」等つぶやいて頂ければ誰かしらが反応するとおもいます。日時の調整等がうまくいけば今回のように勉強会が実現するかもしれません。

2009年7月16日

国産MySQLストレージエンジン「Spider」の作者、斯波健徳氏に聞く
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは。中村です。

MySQLにはMyISAM、InnoDB、CSVなどのいくつかストレージエンジンがありますが、皆さんはSpiderというストレージエンジンを聞いたことはありますでしょうか。Spider Storage Engineは斯波健徳さんにより作成されたDatabase Shardingを可能にするストレージエンジンでMySQL 5.1で利用可能です。

先日、某集まりで斯波さんとお会いしたときにSpiderを作っているということを教えてもらったので、早速詳しい内容を教えてもらうことにしました。
※Spiderについての説明資料はMySQLカンファレンス 2009にて斯波さんが発表されたときのスライドがあります。スライドの直リンク(zip)

Spider Storage Engine について
Spider Storage Engine について posted by (C)フォト蔵

Spider Storage Engineとは?

Spider Storage Engine(以下Spider)はMySQL 5.1で動作するストレージエンジンです。MyISAMやInnoDBと同列の位置付けになります。Spiderをコンパイルして利用可能にするとCREATE TABLEのENGINE指定をして利用できるようになります。

Spiderはデータを持たないストレージエンジンで、他のMySQLサーバーへのテーブルリンクを行います。データを持たないストレージエンジンと聞くと不思議に感じられますが、プロキシサーバのように他のMySQLサーバへ中継を行います。中継先を複数台サーバに分けることができるため負荷分散やデータ分割に利用することができます。

                                +----------+
                           <=>  |  mysqld  |
                                +----------+
Request  =>  +----------+       +----------+
             |  Spider  |  <=>  |  mysqld  |
Response <=  +----------+       +----------+
                                +----------+
                           <=>  |  mysqld  |
                                +----------+

Spiderから接続する先の"データを持つ"ストレージエンジン(上記図ではmysqld)は通常のMySQLサーバで、Spiderがインストールされている必要はありません。このおかげで、Spiderの設定さえ行えば、後は使いなれたInnoDBやMyISAMのノウハウをそのまま利用することができます。また、Spider経由に接続するからといって接続方法やSQL文などに変化はありません。

インストール

Spider for MySQL trunk series よりソースとドキュメントを取得することができます。インストール方法は配布されているドキュメントに記載があります。ここではコンパイル部分のみ転載しておきます。参照したドキュメントはspider-doc-0.12-for-5.1.35.tgzになります。

MySQLのソースの展開。
# tar zvfx ./mysql-5.1.35.tar.gz

spiderストレージエンジンのソースの展開と移動。
# tar zvfx ./spider-0.12-for-5.1.35.tar.gz
# mv ./spider ./mysql-5.1.35/storage

コンパイル準備、コンパイル、インストール。
# cd ./mysql-5.1.35
# autoconf
# automake
# ./configure --prefix=/usr/local/mysql --localstatedir=/usr/local/mysql/data --libexecdir=/usr/local/mysql/bin --enable-thread-safe-client --enable-local-infile --with-pic --with-fast-mutexes --with-client-ldflags=-static --with-mysqld-ldflags=-static --with-zlib-dir=bundled --with-big-tables --with-ssl --with-readline --with-embedded-server --with-partition --without-innodb --without-ndbcluster --without-archive-storage-engine --without-blackhole-storage-engine --with-csv-storage-engine --without-example-storage-engine --without-federated-storage-engine --with-extra-charsets=complex
(オプションは適当に変更すること)
# make
# make install

このあとにストレージエンジンとして認識させるためのSQL文をいくつか流して準備完了です。
※CentOS 5.3の場合、MySQL 5.1が必要とするautoconfのバージョンがCentOSのyumリポジトリよりも高いため今のところautoconf時に失敗します。

Spiderの利用例(テーブルパーティショニング)

Spiderは多くの用途に応用できますが、簡単な例としては大量データの分散があります。MySQL 5.1では標準でテーブルパーティショニングを行うことができますが、Spiderを利用することで各パーティションをそれぞれ別のサーバに割り当てることができます。参照クエリはもちろん、更新クエリも複数サーバに処理を分散できるため、レプリケーションを使ってマスタ・スレーブ構成にしているときに悩ましい更新の集中を回避できます。

JOIN、XAトランザクションをサポート

データ量や負荷が問題になったときに今まではPHPなどのアプリケーション側で複数台サーバへの分散を行うケースが多かったのではないかと思います。しかし、アプリケーション側で実装しようとすると、JOINやトランザクションなど問題があり一筋縄ではいきません。

例えばサーバ1とサーバ2にデータを分割しているとします。JOINするときにはサーバ1とサーバ2の両方に接続してJOINする必要がありますが、コネクションをまたがったJOINを行うことはできないため、複数台のサーバに問い合わせるための機構を別途アプリケーション側で実装する必要があります。

トランザクションについてはサーバ1でCOMMITが成功したがサーバ2でCOMMITしようとするとサーバ2が落ちていた、という場合に問題になります(データ整合性が崩れます)。対応するためにはアプリケーション側で自作のロールバックなどを実装する必要がありますが、個人的には現実的ではないと思います。

SpiderではJOINおよびXAトランザクション(複数台サーバで同期をとるための仕組み)をサポートしているため、これらの悩みがなくなります。普通にJOINを使用して普通にBEGIN、COMMITすれば良いので、ローカルのInnoDBを使うのと変わりありません。

複数サーバに分割されていることを意識しない

Spiderでは「どのデータがどのサーバにあるのか」という情報をSpider側で管理してくれます。また、上記のようにJOINやXAトランザクションをサポートしているため、通常使用するSQLを特に意識することなくそのまま利用できます。つまり、あたかもローカルホストのMySQLサーバをいつも通り操作しているだけのような状態になります。

このおかげで、アプリケーション側ではデータ分割などについて意識する必要がありません。うまくいけば、データベース側の設定こそ必要ですが、アプリケーションの改修を全く行わずにデータ分散などを実現することができます。

導入実績

サグールテレビカドカワード.jpにて採用され、実践稼働しているそうです。利用ケースはスライドに記載されています。

斯波さんにスライドにないことを質問

せっかくオフィスにお越しいただいたので、いくつか気になることを質問してみました。

Q1: Spiderの名前の由来は?

斯波(敬称略): リモートにあるサーバにあるデータを網を引っ掛けて取ってくるイメージでSpiderという名前になりました。

Q2: Spiderを作ろうと思ったきっかけは何ですか?

斯波: Database Shardingを実現するためです。JOINやトランザクションに対応しているものがなく、データベースだけで問題解決するのが難しかったので自分で作ることにしました。Spiderがあればアプリケーションエンジニアのリソースを使うことなくデータベースエンジニアがいるだけで問題解決できます。

Q3: 開発を始めてからα版リリースまでどのくらいかかりましたか?

斯波: 2カ月です。不具合はありましたが、やりたいことは一通り動作しました。

Q4: どの程度のデータ量でテストしていますか?

斯波: テストツールでは1億2千万件程度で行っています。

Q5: Spiderを使うことで制限されることはありますか?

斯波: 現状では全文検索とR-Treeインデックスがサポートされていません。随時対応していく予定です。

Q6: Spiderにはデータの冗長性やサーバトラブルを考慮した仕組みはありますか?

斯波: 冗長化についてはDRDBやMySQL Clusterなど良いソリューションがありますので、Spiderと組み合わせて使うことで問題解決できます。Spider自体が機能を提供する予定は今のところありません。

Q7: 海外での反響はありますか?

斯波: たまにメールが来たりします。

Q8: Spiderの今後について教えてください。

斯波: 今年夏にGAリリースされます。秋にはSavePointの対応とDrizzleに対応予定です。冬にはOracleのテーブルへもリンクできるように対応予定です。またSpiderの力をより発揮できるように新しいストレージエンジンを現在開発中です(注記:現在は既に公開されブログにエントリされています)。

Spider Storage Engine について
Spider Storage Engine について posted by (C)フォト蔵

まとめ

Spider Storage EngineはGPLライセンスで公開されているオープンソースで、完全に斯波さん個人の開発成果物とのことです。サポート業務はST Globalさんでやられているみたいです。

個人的な印象ですが、「既存システムでデータ量も増えてきたしそろそろ分散しないとパフォーマンスが追いつかないけれど、アプリケーションを改修しようとするとSQLたくさん直さないといけなくて大変」、という場合にSpiderはかなり相性が良いかもしれません。特に更新処理の分散ができる点が特徴的です。データの冗長性なども同時に考える必要がありますが、今後の選択肢の一つとして検討していきたいところです。

興味を持たれた方は、Spiderで可能になることがその他多数スライドに掲載されていますので、是非スライドもご覧になることをお勧めします。

斯波さんへ、今回はウノウラボへの掲載について快諾していただきありがとうございました。

その他情報など


About mysql

ブログ「ウノウラボ by Zynga Japan」のカテゴリ「mysql」に投稿されたすべてのエントリーのアーカイブのページです。過去のものから新しいものへ順番に並んでいます。

前のカテゴリはMacです。

次のカテゴリはnginxです。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

Zynga Japan