負荷テストは、ある程度の規模でのシステム開発では避けて通れない工程です。これを疎かにすると、同時アクセスが増えた時やテーブルにデータが溜まった時に思うような時間でレスポンスが帰ってこないことになりかねません。擬似的に同時アクセスをかけたり、想定される件数分事前データをテーブルに格納した上で操作して想定どおりのレスポンスが得られるかをリリース前に確認する必要があります。
で、私もシステム開発者の端くれとして何回か負荷テストの場数をこなし、自分のやり方を確立しつつあったのですが、今回手間取りまくってしまい、自慢の鼻をへし折られてしまったので反省がてら備忘録として残しておくことにしました。どうか同じような状況でハマる人がいなくなりますように。
大量データの準備
負荷テストをする際に大量データの準備があります。今までのプロジェクトでは幸運にも、せいぜい1テーブル辺り数万、数十万のオーダーだったのですが、今回は、数百万、数千万のデータ量のケースを行うことになりました*1。
大量データを格納するのにはいくつか方法があると思います。
- 実際にシステムにリクエストを送る
- CSVデータファイル(or Excel的なもの)を用意し、DBViewer的なツールにコピペ
- CSVを用意し、JDBCドライバ経由でINSERT処理を行うプログラムを作成
- DBMSのツール(○racleならSQL*Loader)
- 頑張ってINSERT文を手入力
もちろん私は技術者ですから、1件ずつINSERT文を手入力するなんて非効率なことはしません。つまんない作業は機械に任せて早く帰りたい。対象テーブルも十数個あります。CSVを準備するのもかったるい(というか昔のExcelだと6万件くらいしかデータが作れない)。
ということで、
- 負荷テストツール(JMeter)を使い、実際のシステムにアクセスしてデータ登録するシナリオを流す
方針をとりました。システムのレスポンスが悪くても思うようにデータが作れない時はThreadの多重度を上げて登録させれば何とかなっていたのです。
時間が足りない
ところが、登録処理に時間がかかるのか、多重度を上げても思うように単位時間当たりの登録件数が伸びませんでした。1時間辺りの登録件数を見ると、数日がかりでないとデータの準備が完了しない見込みでした。
アプリの出来が悪いからか?なんて思いましたが、一般的なWebアプリとして見ると劇的に遅いわけでもありません。チューニングをする為のデータを取る為の事前データを取る為にチューニングしろ、というのも無茶な話です。
アプリ経由だと時間がかかりすぎます。負荷テストの開始が遅れるとプロジェクトのスケジュールにも影響します*2。APサーバのリソースは全然消費されておらず、DBサーバ側で待たされています。だから、リクエストの多重度をを上げてもDBサーバのボトルネックは変わらないので解決策にはならないでしょう。この時点でCSVデータファイルを作ってそれをJDBC経由でINSERTする方針に変更しても状況は好転しない。SQL*Loaderもコントロールファイルやデータファイルを作成する手間を考えると時間がかかりすぎる・・・。このままJMeter経由で何日も流し続けるか、新たな策を講じるか。軽く錯乱状態になりました。
JDBC経由はやっぱり遅い
で、結論としてプロシージャを作成することになりました。最初は半信半疑だったのです。JDBCは昔は遅かったけど、最近のマシンスペックならそんなことないだろう、プロシージャだからってそんなに劇的に早くなるものかよ、と。
すみません、正直見くびってました。単位時間当たりの処理件数は軽く10倍を超えます。INSERTを早くする為に、INDEXを外さなきゃとかパーティーションテーブルで再構成しなきゃいけないとかバージョン上げれば何とかなるんじゃね?とかぎゃーぎゃー騒いでた自分が恥ずかしくなるくらいです。DBMS自体のチューニング無し&プロシージャ呼ぶコネクション1つでこの結果。あ・・・、圧倒的じゃないか。
でも全面的に認めたわけじゃないんだからね!
というわけで、今回の救世主になったプロシージャさんですが、ロジックをDBMS側に登録しておいてそれを呼び出す、というスタイルなのでModel部分のロジックが分散されてしまうことになる為に、使用することについてはあまり好意的ではありませんでした。テストもしづらいし、DBMSよりAPサーバの方が負荷分散しやすいのでロジックはAPサーバで持て、というのが私のポリシーです。ですが、今回の騒動で「あいつも中々やるじゃねぇか」と思ったのも事実。
大部分のWebアプリケーションにおいては登録処理の応答が0.5秒から0.1秒になっても対して喜ばれまないと思っている(画面描画に時間がかかるから)ので、応答速度がある程度以内であれば、保守性/メンテナンス性を良くすべきだ、という信念を持っていたので、プロシージャは一歩引いた感じだったのですが、適材適所を心がける必要性に気づかされました。バッチ系は使わないと夜間で終わらないこともありそうですね。自分が携わってきたシステムでは今まで無くても何とかなっていたのですが、いつもと同じで今回もうまくいくと思うなよ、ってことに気づかされました。
今回は自分の無知と食わず嫌いと、今までのやり方を押し通してなんとかなると思った見通しの甘さのコンボが発動した為に起きた事象だと思っています。関係各位には迷惑をかけました。反省。食わず嫌い良くない。他のDBMSはどうなんだろうとか、他のバージョンでも同じなのかな、とか疑問は沸いてきますが、できてよかった。でも、プロシージャを多用してDBMSに常に負荷をかけるのは良くないと思ってます。お前は奥の手扱いだ。
おまけ
データファイルのサイズ見積もサボってて、「AUTO EXTEND」してるからいいや、と思って気にしてなかったのですが、初期で用意してたサイズじゃ収まりきらず、INSERTの度に拡張処理が走って、時間がかかってしまったこともあります。後は、そもそも確保しているディスク領域を超えないかの見極めも必要ですね*3。テストのシナリオや、DBに格納されるデータパターンによってはえらく応答が速くなることもあるし。負荷テスト、奥が深い。気にしなきゃいけないこと多すぎだから、片手間になんて思わないほうが良いです。