システムとインフラ間の設計矛盾

森廣隆行氏:そして最後に、ここのSolrの反映のところに関してです。ここでの問題は何かというと、システムとインフラ間の設計の矛盾になります。

まず、この部分をフロー化すると、このようになっております。

原稿テーブルと関連テーブルのほうからSolrに出力するための情報を中間テーブルに集め、そこからCSVに吐き出すという処理が主になります。

まずここでやったことですが、何が問題かもわからなかったので、とりあえず各処理の地盤を調査しました。

基本的に、みなさんがコードのほうは注力されているようで、ぜんぜん遅くはなってなかったんですけど、主にSQLのほうが重くなっていることがわかりましたので、そこについて対応を行いました。ここですね。

やったのは一般的なことです。テーブルの件数を減らしてからジョインしましょうとか、増やされたところに対してインデックスが貼られていないとか。複合機でとっているにもかかわらずそこが考慮されていないとか、もともとの処理が遅くなっているみたいなところがあったので、貼り直したりとか。

使用関数のところが、昔はバッチサーバーのほうが遅いみたいなことがあったので、SQLにやれることはやらせてしまおうみたいな感じで、いろいろ関数を叩き込むということをやられていました。その部分をJava側に処理を寄せたり、関数自体を違うものに置き換えたり、ということをやりました。あと参照、テーブルの見直し等々もやっております。

ちょっとSQL自体は出せないんですけど、実行結果がこんな感じになっています。

こいつ、歴史を感じる500行超えのSQLですね。

(会場笑)

ヤバい。どこがヤバいかというと、ここで見てもらったらわかるんですけど、キューをネストしていまして、テーブルをジョインしまくっています。

(会場笑)

かつ、よくよく見てみたらINDEX FULL SCANして、UNIQUE SCANしてがっちゃんこしてるんだけど、コストがいきなり300くらい上がっています。このような感じだったので、今回見直しを行っております。

まずやったことは、ちょっと苦しかったんですけど、ネスト自体を減らしつつ条件を絞り込んで、ジョインするテーブルの数を減らしてコストを下げるということです。

なんとか作業したのですが、その結果、このくらい改善しました。あとSolrのインプットファイルは1個である必要がなく、複数でもまったく問題ないんですけれども、そこも直列になっていました。とりあえず脳死して並列化してみました。

これでなんとかなるかなと思ったんですが、案の定、2時間かかっていたものが20分しか改善しませんでした。

(会場笑)

SQLのパフォーマンスを調査した結果

ここで本腰を入れて、Oracleに付いているOracle Enterprise Managerというツールを使って、SQLのパフォーマンス調査をやりました。

それを使って中間テーブルで並列化したところが詰まっていることがわかったので、その部分を対応しました。

これがEnterprise Managerの結果なんですけど、知ってる人はわかるかな……とりあえず、水色がいっぱいです(笑)。

ほとんどがキャッシュフュージョンという、Oracleの機能によるデータの読み込み待機イベントに占められていました。見てもらったとおり、黄緑のところが実処理なんですが、基本的に濃い緑や水色がほとんどを占めていました。

これが何かがわからなかったので、とりあえず調べてみて、自分なりにまとめたのがこちらです。

OracleはRAC構成をとっています。そのおかげで、まずSQLを発行した場合、バッチ1号機に対してディスクのほうからデータの読み込んで、処理を行います。

その次に違うSQLが3号機のほうに走った場合にどうなるかというと、そのままディスクから読み込むのではなくて、各サーバーに対して「存在していないか」という問い合わせを行います。存在していた場合は応答を行いまして、サーバー間でデータのブロックを転送することによって、ディスクのI/Oを減らしてデータを同期するというかたちをとっております。

こいつが今回悪さをしておりました。どうなっていたかというと、今回バッチを並列化したおかげでSQLが刺さるサーバーが複数台になっちゃっていました。

例に挙げたのは、先にバッチ1号機に処理した場合です。まず、バッチ1号機が動いてしまった場合、データが1号機に展開されます。そのあとにバッチ2号機・3号機も動くんですけど、データを処理するためにはデータ転送を行わないといけません。しかし、バッチ1号機がまだ処理しているという状態になってしまいます。

なのでこの場合、バッチ2と3は待つかたちになりますが、このロックが長くなってしまうと、自動的に変更があるかないかの確認を行って、「データを読み込んでください」というかたちで2号機・3号機に対して命令を送ります。

これによって2号機と3号機もディスクから読み込むことになりますが、ここで詰まりが発生してしまったのが、先ほどのDB File Sequential Readのところです。

今回、それに対してどういう対応を行ったかというと、せっかくRAC構成をとっているにもかかわらず、無理やり疑似的なMaster-Slave構成をとるという方法で改善を行いました。

これにより、バッチ1・2・3号機を4号機・5号機に寄せて、メインを4号機に寄せて処理することによって、データ転送の回数を減らしつつ速くするというかたちをとりました。これが功を奏して、けっこう速くなりました。とくにアップデートとデリートに効果的で、セレクトはさすがに速くなりませんでしたが、そこの2つがかなりの効力を得ました。

データフローの洗い出し

これで解決したかなと思ったんですが、まだ(データの読み込み待機イベントが)出ているということがわかりました。

実行時間自体は減っているんですが、やっぱり水色の部分を解決できておらず、そこについてまた深堀りを行っております。

やった内容としては、もうお手上げだったんで、とりあえず全部洗ってみるかということで、この処理自体の全部のデータフローを洗い出しました。

結果がこれです。

さっきのSQLがこの右側のものなんですけど、技術負債の巣窟です。何が技術負債なのかといいますと、この中間テーブルが負債です。

よくある話、基本的に初期フローはたぶんこんな感じだったと思うんですけれども、通常、企画とかが起案してやる案件は、「一覧のところに情報を出したい」みたいな感じです。

それによって何が起こるかというと、Solrにデータを入れないといけないので、この部分のバッチを改修しないといけません。ただ、これを改修しようとするとデータがないので、その前段のところに加工用のバッチを増やします。

でも、ここに変えたということは、ここも変えないといけないよね、というかたちで開発をするんですけれども……こんな見積りとかしたら、めちゃめちゃかかっちゃうんで、絶対に怒られるんですね。みんながどうやるかというと、やっぱりこうなっちゃうんですね。

(会場笑)

中間テーブルをこんな感じで増やして、バッチを改修するところを減らしつつ、工数を減らしてやりたいことはやる、みたいな感じです。「あとで直せばいいや」みたいな感じでやっていくんですけど、結局どうなったかというと、こうなりました。

(会場笑)

こいつの何が悪いか。この原稿テーブルというのは、タウンワークの詳細画面に逐次オンラインからアクセスされるテーブルです。

こいつに対して、長時間ロックするSQLを発行しまくるおかげで待ってました。かつ、なぜか知らないですけど、原稿テーブルに更新するやつまでいると。

(会場笑)

なんでかわかんないですけどね(笑)。ここで起こっていたのが、さきほどのキャッシュフュージョンです。それもオンラインとバッチで(笑)。

これをどうしたかというと、いたるところを削除しまくって、この赤いバツのところを徹底的に潰して、こんな感じにしました。

まず、原稿テーブルへのアクセスを最小限にして、オンラインとオフラインのアクセステーブルの分離をしました。そのあと原稿反映処理が、朝に大きいものが1回と、昼間に停止・修正系が3回で、計4回流れます。その部分でバッチを機能ごとに分けて、中間テーブルを入れて分けるかたちをとりました。

それによってSQLがどうなったかというと、こんな感じになりました。

最終的には2時間かかっていたものが、15分に改善して、このようになりました。

入稿システムのところに編集を入れたり、マスターチェック・削除を入稿側に寄せて、かつタウンワークでは新規・停止・修正というところを分けつつ、Solrの反映のところを寄せました。メインがバッチだったんですけど、棚ぼた的にタウンワークの詳細画面のレスポンスもすごく向上しまして、円満な感じで本改善を行いました。

品質の担保について

ここで気になってくるのが、これだけ改善して修正を加えてしまったら品質担保はどうなるのという話です。ここをどうしたかについてですが、一般的にやろうとするとテスト環境とテストデータを準備し、修正前と修正後でジョブを流してそのデータベースを比較する、単純にやるとこうなります。

ですが、先ほど見ていただいたとおり、システムが複雑すぎて商品がとても多いです。この知識を持っている人が限られていて、その人でも本当に正しいのかわからない状態で、担保ができません。かつ1週間分もあるので比較量も膨大になってしまいます。

最近は一般的なんですが、本番同等の環境を用意し、本番の処理前と処理後のデータを丸々コピーして、かつ処理後のジョブネットを流した結果を比較するというかたちをとりました。

かつ、リクルートはオフショアを使っており、このデータをひたすらオフショアを使って比較することによって1週間分担保することで、リリースすることができました。

最終的な処理時間

その結果どうなったかといいますと、想定どおり、掲載数は増加の一途を辿りましたが、終了時間はこんな感じで激減させることができました。

ズームアップするとこんな感じで、マックスで6時間、かつ平均でも4時間くらい短縮できまして、かついままで障害も0件で、うまく回させていただいております。

もう1つ、いいことがありました。運用チームが夜中のバッチの遅延監視を行っているんですが、それが朝5時に設定されています。

過去、対応する前とかは、毎週月曜日の朝5時に、6週連続でモーニングコールがかかるということがあったんですが、2018年の6月からいままで、1回もコールがありません。そこの改善にも携わることができました。

泥臭い成果の積み重ねでも結果は出る

最後にまとめになります。

レガシーシステムの改善というのは、よくリビルドやリプレイスに頼りがちですが、泥臭い成果の積み重ねで、十二分な結果を出せます。また、業務やシステム単体で対応することがあると思いますが、みんな、疎結合がいいというかたちでやられます。しかし、システムは年を追うと絶対疎結合にできません。無理に疎結合にすることによって悪化することもあるので、レガシーになったら密結合にしたほうがいいのかなと思っています。

そういうことも考えつつ、いろんな観点で見ていただけると効果が増大することが今回わかりましたので、ぜひやっていただければなと思います。

最後にリクルーティングをさせてください。自分の責任と技術力の限りを尽くして「リアルISUCON」をしたい人には最高の環境だと思っています。このあとに古川(陽介)さんが講演しますが、社内ISUCONもやっていますので、興味のある方はぜひブログ等でご覧頂けましたらと。それから一緒に仕事をしたい方、お待ちしております。以上になります。ご清聴ありがとうございました。

(会場拍手)