page_adsence

2010年12月21日火曜日

MySQLでやってはいけないこと

MySQLを使用する時点で気をつけておかないといけないことを漢(オトコ)のコンピューター道さんの方で記事があったので、転載させてもらうことにした。

1. 全てのカラムにインデックスをつける

データベース初心者がもっともやってしまいがちな間違いはコレではないだろうか。インデックスはいい。検索がとても速くなるから。しかし、それと引き替えにインデックスは更新するときにコストがかかるし、その分多くのディスクスペースを消費する。特に更新にかかるコストは時に甚大で、該当するインデックスのページがキャッシュ上にない場合はディスクからいったんそのページを読み込まなければいけない。ディスクアクセスは動作にとても時間がかかるので、インデックスが多数、例えば全てのカラムに付いていたりすると「あれ?固まったか?」というような状態になってしまうことがあるだろう。インデックスは必要なカラムにだけつけるようにテーブルを設計しよう。インデックス数が増えてきたら、テーブルの分割や正規化を検討した方がいいだろう。

2. デフォルトのまま使う

何も足さない。何も引かない。

一切の手を加えずに、デフォルトのままMySQLを使ってしまう。デフォルトの状態でもそれなりのパフォーマンスを発揮出来るが、バッファが足りないので思うように性能が出ないだろう。特に、大きなテーブルを扱う時には大きなバッファが必要である。(テーブルがごく小さなものであばデフォルトのままでも何ら問題ないかも知れない。)バッファが小さいと、キャッシュされないインデックスページが多数存在することになり、特にインデックスページを更新する時にディスクアクセスが生じるのが問題になってしまうだろう。

1との合わせ技で一本。

3. インデックスを利用しない

インデックスなどというこざかしい技は利用しない。オトコは黙って真っ向からテーブルスキャン!!などと馬鹿なことを考えてはいけない。データベースの検索はインデックスを使ってナンボである。インデックスをつけすぎるのも良くないが、インデックスを使わないのはもっといけない。特にインデックスを使わずにJOINをするようなクエリ(FULL JOIN)は強烈に遅いので気をつけよう。

4. バッファを多く割り当てすぎる

バッファは多ければ多いほど高速になる。しかし、欲張って空きメモリをはるかに上回るバッファを割り当ててしまっては元も子もない。スワップが発生してしまうからだ。スワップが発生すると、目を覆いたくなるほど性能が劣化する。MySQLはメモリへアクセスしているつもりでも、ディスクへのR/Wが完了しないと処理を継続することが出来ないからだ。しっかりと空きメモリを確認し、スワップが起きないように気をつけよう。

セッションごとのバッファには要注意だ。これらは負荷に応じて多く割り当てられる可能性があるからだ。システムの負荷がスカスカのときに大丈夫だと安心していたら、負荷がピークになったときにスワップが発生してエライ目にあった・・・などということにならないよう気をつけよう。パッケージに含まれるmy- innodb-heavy-4G.cnfサンプルファイルには注意が必要である。これは、heavyなクエリ、つまり複雑なクエリを処理するための構成であって、セッションごとのバッファが多く割り当てられている。負荷が重いという意味ではないのである。

5. ネストしたサブクエリ

既に解説したように、MySQLのサブクエリは外側から順に評価される。従って、サブクエリをネストしていると、内側のサブクエリは無駄に何回も実行されてしまうことになる。特にIN句を用いているときは注意が必要である。

ネストしたサブクエリのように遅いクエリが存在すると、ロックモードによっては他のセッションの参照や更新がブロックされてしまうことがある。システム全体の足を引っ張ってしまうわけである。その他には、JOIN + ORDER BY + LIMITを合わせて利用する場合、一行ずつINSERTするような場合、ストアドプログラムを多用しているような場合には注意が必要である。

6. ストレージエンジンの用法を間違えている

たまに見かけるのが、InnoDBなのにテーブルロックを利用している場合である。InnoDBは行レベルで排他制御を行うので、テーブルロックを利用するとわざわざ同時実行性能を下げてしまうことになる。

トランザクションが必要なのにMyISAMを利用しているというような場合もある。BEGIN;....COMMIT;などとやっているが、MyISAM ではCOMMITをしても意味がないので意味がないし、ロールバックは一切出来ない。トランザクションが必要なら、トランザクション対応のエンジンを利用しよう。

InnoDBを利用している時にデッドロック時のエラー処理がない場合がある。トランザクションは常にデッドロックによるロールバックの発生を意識して利用しなければならない。MyISAMを利用していたプログラムをInnoDBに変換した場合に、このような過ちを犯してしまいがちなのだろう。

7. バラバラの文字コードを使う

MySQLの便利なところは、文字コードの変換を自動的に行ってくれるところである。それにあぐらをかいて文字コードに無頓着でいると、痛い目を見ることになるだろう。文字コードはテーブルごとに設定することができるが、セッションに対して設定することも可能である。また、システムレベルで利用する文字コードも設定することが可能である。全ての文字コードがバラバラだと、テーブルから行をフェッチ --->文字列処理関数--->クライアントへ送信、という処理を行うような場合に、各ステップの間で文字コードの変換が発生してしまう。全てをアプリケーションが利用する文字コードへ統一しよう。

8. 同時接続数が多すぎる

MySQLは同時接続数が増えても軽快に動作する・・・ということはない。数千の接続が同時にアクティブな状態では、ロックの競合が多発してスラッシングが起きてしまう。適切な接続数を見極めよう。クライアント数が増えた場合には、レプリケーションや MySQL Clusterによるスケールアウトを検討するといいだろう。

上記とは異なる問題であるが、徐々にセッション数が増えてしまい、新規の接続が出来なくなるというような問題をたまに見かける。これは、単にアプリケーションがセッションを閉じない場合に生じる問題であり、例外発生時などに的確にセッションを閉じるように注意されたい。

9. 他のアプリケーションがリソースを食い過ぎる

MySQLが稼働しているホストで、他のアプリケーションも動作している場合には、メモリやCPU、ディスクアクセスなどの資源が枯渇しないように注意を払う必要がある。メモリが足りなくなってしまった場合、MySQLはOOMキラーの格好の餌食となるだろう。(OOMキラーに終了させられないように設定しておくと良い。)

10. いにしえのバージョンを使う

MySQLの開発者達は、日々バグの修正を行って膨大な数の問題を修正している。従って、新しいバージョンのMySQLは新しい機能が追加されているだけでなく、バグが少なく安定している。とても古いバージョン、特に3.xや4.xを使っているような場合には気をつけよう。

古いバージョンにだけしか搭載されていない機能にも要注意である。例えば、かつてはBerkeley DBがストレージエンジンとして搭載されていることがあった。しかし、BDBストレージエンジンが安定することはなかった。MyISAMの前身である ISAMストレージエンジンというのがあるが、ISAMは既にメンテナンスが終了している。MyISAMのRAIDモードなどというものもかつては存在したが、5.0以降のバージョンには搭載されていない。

MySQLだけを最新にしても、OSが古すぎると元も子もない。Linuxカーネル 2.4またはそれ以前、FreeBSD 5.x、Solaris 8など、まだまだ使われている場合も多いが、古いOSを使うメリットはあまりない。OSも出来るだけ新しいものを利用しよう。