エンタープライズアプリケーションは正確に動いてナンボであり、パフォーマンスがいくら良くても正確に動いていなければ失格だ。だが、もちろんパフォーマンスも蔑ろにしてはいけない。ユーザにしてみれば、アプリケーションが正確に動くのは当たり前。「速い」「軽い」ソフトウェアを開発するのもプロの仕事と言える。
エンタープライズアプリケーションにおけるパフォーマンスチューニングの肝はいくつもあると思うが、データベース周り(※1)に絞って話をする。
SQL Server のチューニングとして、インデックスを作成する事を筆頭に挙げる人もいると思う。非クラスタ化インデックスを利用した検索は、非クラスタ化インデックスのスキャンとクラスタ化インデックスのスキャンの都合 2 度のスキャンが行われる。この 2 度のスキャンを避けるために、インデックスとなる列を増やすか「付加列インデックス」というワザを使い、クエリの列を包括するようにすれば、クラスタ化インデックスまで列を探しにいく必要がなくなり、パフォーマンス改善に繋がる。適切なインデックスの作成は劇的なパフォーマンス改善をもたらすので検討の価値はあるだろう。とは言え、どの列をインデックスにするか、またどの列を付加列とするかを決定するのは多くの知識と経験が必要であり、難易度が高い。知っての通り、下手にインデックスを作成すれば逆にパフォーマンスを悪化させる事になりかねない。
という事で、パフォーマンスチューニングで筆頭に考えるべきはインデックスの事ではなくクエリの見直しだろう。実行プランとにらめっこしてよりコストの安いクエリを模索するのも良いが、先ずはロックのメカニズムを理解すれば、しなくてよい苦労も少なくなるだろう。
SQL Server でありがちなのがロック待機(※2)をロックエスカレーションと誤解してしまう事だ。
そもそも「ロックエスカレーションが発生したらパフォーマンスが悪くなる」と考えるのは誤りだ。ちまちまと小さい単位でロックするよりも、ガツンと大きい単位でロックしてしまった方が少ないコストで済む事もある。その辺の判断はオプティマイザに任せよう。ロックエスカレーションを阻害しようとすると往々にして逆効果となる。どうしてもロックエスカレーションを避けたい場合はロックがエスカレートする閾値を調整する事になるが、最適値を発見するのは至難の業だ。
ロック待機はロックエスカレーションとは違うので、必要ならば待てば良い。SQL Server 2005 には「READ_COMMITTED_SNAPSHOT」「スナップショット分離レベル」が用意されており、他のトランザクションが何をしていようが更新前のデータを読み取る事が可能なので、ロックが解除されるのを待つ必要はない。しかし「READ_COMMITTED_SNAPSHOT」「スナップショット分離レベル」はオーバーヘッドが少なからず増すので、できれば避けたいところだ。また、更新前のレコードを本当に読み出して良いのかどうかも検討しなければならないだろう(※3)。
ところで、よくよく考えてみると、クエリによっては他のトランザクションがコミットしようがロールバックしようが関係ないという場合もある。つまり、ダーティリードしても良いというパターンだ。SQL Server のデフォルトトランザクション分離レベルは Read Committed なので、それ以下の分離レベルに下げる事がそもそも大前提としてない、という事がよくある。パフォーマンスチューニングの観点からするとこれでは勿体無い。
「ダーティリードしてもいいじゃん!」というシナリオが少なからずあるはずだ。よく探してみてはどうだろうか。
- ※1 データベースと言えば殆ど RDB だと思うので RDB、しかも SQL Server に限定して述べる。
- ※2 テーブルスキャンのロック待ち。
- ※3 特定条件のレコードが一件でも存在するかどうかを確認するクエリなどは、コミットされる可能性があるレコードのロックが解除されるのを待って確認しないといけない。