バッチ高速化のあゆみ

57
ババババババババババ バババババババババ ババ ババ

Upload: dcubeio

Post on 21-Apr-2017

1.069 views

Category:

Engineering


0 download

TRANSCRIPT

Page 1: バッチ高速化のあゆみ

バッチ高速化のあゆみ株式会社ビズリーチ

阪本 康裕

Page 2: バッチ高速化のあゆみ

1 章 「はじめに」

Page 3: バッチ高速化のあゆみ

今日のお題

システム運営で必ずつきまとうバッチ処理の長時間化。今回はこの課題をを解決するまでに実施した数々の施策についての紹介

各施策については施策内容と発生し得るリスクと共に紹介。

Page 4: バッチ高速化のあゆみ

目次

1 章 「はじめに」

1. 今日のお題2. 目次3. 自己紹介

2 章「高速化のあゆみ」

1. 対象2. 課題3. 施策

a. ロジックb. データソースc. インフラ

3 章「奥の手」

1. マルチプロセス化

4 章「さいごに」

1. 最終的な成果

Page 5: バッチ高速化のあゆみ

2 章 「高速化のあゆみ」

Page 6: バッチ高速化のあゆみ

1.対象( 1/3)

環境/ミドルウェア・ Batch サーバ

言語: Java(Spring+Struts.Quartz scheduler)アプリケーションコンテナ: Tomcatデータソース: RDS(AmazonWebService,MySQL 互換 )

その他:検索サーバ Apache Solr

Page 7: バッチ高速化のあゆみ

1.対象 (2/3)

対象機能スカウトメール配信

予め企業が設定しているスカウト条件にマッチする求職者に向けてメールを配信する機能。自動で求職者に毎日朝・夕の2回、数求人を配信している。

大まかな処理としては ①マッチング処理 企業が出している求人と利用者の職務経歴書をマッチングし、マッチ度を算出(毎日)➜ ②メール配信処理 ➜ 求職者に対しては毎日数件、マッチ度の高い順にスカウトメールを自動配信の2部構成。(いずれもバッチ処理)

今回はその前者の①マッチング処理

Page 8: バッチ高速化のあゆみ

1.対象 (2/3)

マッチング処理

1. 求人のスカウト条件に一致する求職者を検索する

2. 一致したものに対してマッチ度を計算

3. ⇔ 求人 求職者情報を格納

Page 9: バッチ高速化のあゆみ

2. 課題 (1/3)  バッチ処理時間の長期化

求職者 200,000 名 求人☓ 70,000 件の条件一致検索を行うので、純粋に処理量が多い

多数 多数をイメージできるような、掛け算のようなイメージがあれば☓

求職者 ☓

200,000 名

求人☓

70,000 件

14,000,000,000 通り

Page 10: バッチ高速化のあゆみ

2. 課題 (2/3)  長時間化に伴う後続バッチの追いつき

バッチ処理時間長くなると、後続のメール配信処理の開始時間に間に合わなくなる。

スカウトバッチ メール配信バッチ(後続)

Page 11: バッチ高速化のあゆみ

2. 課題 (3/3)  運用上の問題

バッチ時間が始まると、その処理時間中はサーバを止めることができない。これは新規機能リリースや、非定期のサーバ再起動が可能な機会を著しく低下させるため運用面でも不利益が出始めていた。

帰りたい・・

まだ処理中

Page 12: バッチ高速化のあゆみ

3. 施策   a. ロジック 

① トランザクション回数の削減 効果:★★☆☆☆ リスク:★★☆☆☆

Page 13: バッチ高速化のあゆみ

① トランザクション回数の削減

1 求人に対する求職者のマッチ度情報は最大で全求職者数分のレコードが作成されることになるため、

RDS への INSERT 発行回数も最大で 14,000,000,000 通り分発生することになる。

Batch (アプリケーションサーバ)から RDS へのレコード登録は

① RDS へのコネクション接続 ※接続プールを使っている場合はこの Step は発生しない

②トランザクション開始

③ INSERT クエリ発行(レコード登録)

④コミット ⑤ RDS へのコネクション切断 ※接続プールを使っている場合はこの Step は発生しない

という流れとなる。

その中で登録処理は③だけだが、登録に必要な段取りとして①②④⑤が存在する。

①②④⑤ が属に言う「オーバーヘッド」となる。

Page 14: バッチ高速化のあゆみ

① トランザクション回数の削減

このオーバーヘッドは必ずしも1レコード登録する為に必要なものではなく、複数レコードを一括で登録する際にも同じオーバーヘッドの時間で賄う事ができる。つまり、③のステップで複数のレコードを登録することでオーバーヘッド分の時間短縮に繋がる。

イメージ

1 トラン 1 レコード ☓ 1000 件(①〜⑤ ) 1000☓ 件 = 500000ms(500秒 )

1 トラン 1000 レコード(①〜② )+③☓ 1000 件+ (④〜⑤ ) = 100400ms(100.4秒 )※但し、④はレコード件数増加により処理時間は増加する

① RDS へのコネクション接続 ➜ 100ms ②トランザクション開始 ➜ 100ms ③ INSERT クエリ発行(レコード登録) ➜ 100ms ④コミット ➜ 100ms ⑤ RDS へのコネクション切断 ➜ 100ms

約 に短縮⅕ ( )※

Page 15: バッチ高速化のあゆみ

① トランザクション回数の削減

リスク

トランザクションに登録をまとめる実装は比較的容易。

エラー時はトランザクション内の全てのレコード更新がロールバックされるため、他レコードへの影響を考慮する必要がある。(全滅は OK か?一部更新は OK か?)

前項にも記載したが、トランザクション内で登録するレコード数が多くなればコミット処理時間も増大する。

このコミット処理については RDS への負荷に直結するので、 RDS の性能と相談して件数を決める必要あり

Page 16: バッチ高速化のあゆみ

3. 施策   a. ロジック 

② 処理のマルチスレッド化 効果:★★★★☆ リスク:★★★★☆

Page 17: バッチ高速化のあゆみ

② 処理のマルチスレッド化

バッチの処理時間の内訳を算出すると、 Batch サーバの処理時間に比べてRDS への登録処理、 Solr への検索処理が圧倒的に多い。その上、 RDS や Solr の負荷も低いといった場合は Batch 、 RDS 、 Solr のスペックを十分に引き出せていないことになる。この場合に有効な策としてバッチ処理のマルチスレッド化が挙げられる。これは単一処理(スレッド)で発生していた各々の隙間時間を極力減らすことで時間の短縮に繋げる事ができる。そもそも RDS も Solr も複数アクセスを前提に設計されているので、単一アクセスでスペックを十分に発揮し切ることは少ない。

Batch サーバ

Batch サーバ

Page 18: バッチ高速化のあゆみ

② 処理のマルチスレッド化

具体的には

①スカウト条件に一致する求職者を検索する ②一致したものに対してマッチ度を計算 ⇔③求人 求職者情報を格納

だった処理を

・処理対象となる求人のリストを抽出 ・抽出した求人群をスレッド数分に分割する ・下記の処理を行う子スレッドを並列分だけ生成し、前項の分割分を渡す ・スカウト条件に一致する求職者を検索する ・一致したものに対してマッチ度を計算 ⇔・求人 求職者情報を格納 ・並列分の子スレッドが全て終わるのを待機する

へと変更する。

Page 19: バッチ高速化のあゆみ

② 処理のマルチスレッド化

before

Page 20: バッチ高速化のあゆみ

② 処理のマルチスレッド化

afterこの処理をスレッド化

Page 21: バッチ高速化のあゆみ

② 処理のマルチスレッド化

after

Page 22: バッチ高速化のあゆみ

② 処理のマルチスレッド化

マルチスレッド化に伴う子スレッドの管理。バッチ処理本体を本スレッド、実処理を子スレッド(複数)として位置付けすると本スレッドは全ての子スレッドの終了を監視する必要がある。親スレッドは子スレッドの状況、特にエラーは検知し辛いので子スレッドのエラーは自身で対処できるようにしないと行方不明になるサーバのスペック以上に分割しても返って遅くなる&誰かが負荷倒れすることになるので注意

リスク

Page 23: バッチ高速化のあゆみ

3. 施策   a. データソース 

① インデックスチューニング 効果:★★★★☆ リスク:★★★★☆

Page 24: バッチ高速化のあゆみ

② インデックスのチューニング

RDS(MySQL) への検索時に使用するインデックスを見直し

検索しようとしている条件に一致したインデックスが用意されているか?

用意されていた場合、実際に使われているのか?

Page 25: バッチ高速化のあゆみ

② インデックスのチューニング検索しようとしている条件に一致したインデックスが用意されているか?

テーブルレイアウト 実データ

Page 26: バッチ高速化のあゆみ

② インデックスのチューニング検索しようとしている条件に一致したインデックスが用意されているか?

テーブルレイアウト 実データ

IDX_1

Page 27: バッチ高速化のあゆみ

② インデックスのチューニング検索しようとしている条件に一致したインデックスが用意されているか?

テーブルレイアウト 実データ

IDX_2

Page 28: バッチ高速化のあゆみ

② インデックスのチューニング検索しようとしている条件に一致したインデックスが用意されているか?

テーブルレイアウト 実データ

IDX_1_2

Page 29: バッチ高速化のあゆみ

② インデックスのチューニング検索しようとしている条件に一致したインデックスが用意されているか?

テーブルレイアウト 実データ

Page 30: バッチ高速化のあゆみ

② インデックスのチューニング検索しようとしている条件に一致したインデックスが用意されているか?

テーブルレイアウト 実データ

explain結果

Page 31: バッチ高速化のあゆみ

② インデックスのチューニング検索しようとしている条件に一致したインデックスが用意されているか?

テーブルレイアウト 実データ

Page 32: バッチ高速化のあゆみ

② インデックスのチューニング検索しようとしている条件に一致したインデックスが用意されているか?

テーブルレイアウト 実データ

explain結果

Page 33: バッチ高速化のあゆみ

② インデックスのチューニング

RDS(MySQL) への検索時に使用するインデックスを見直し

インデックスの有無により DB の検索対象レコード範囲が全景になるか否か挙動が異なる ➜ 大量のレコードがテーブルに存在するほど影響が大きくなる

Page 34: バッチ高速化のあゆみ

② インデックスのチューニング

RDS(MySQL) への検索時に使用するインデックスを見直し

定義インデックスが実装で発行するクエリに添っているか? ➜ 実装の改修などで DDL変更されたケースなどでインデックスも再設計されているか?

Page 35: バッチ高速化のあゆみ

コード変更を伴わない為、アプリの挙動には影響しないのがこのチューニングの強み。

但し、インデックス登録時の ALTER TABLE 中は対象テーブルが共有ロック (読み取り専用 ) になる可能性があるので注意。特に外部制約でリンクしている子テーブルも同様のロックが発生するので、システムの肝となるテーブルが対象となった場合はサービス停止もあり得るため、運用の事故は大きく発生し得る

② インデックスのチューニング

リスク

Page 36: バッチ高速化のあゆみ

3. 施策   a. データソース 

② クエリ (insert文 ) チューニング

効果:★★★☆☆ リスク:★★☆☆☆

Page 37: バッチ高速化のあゆみ

② クエリ (insert文 ) チューニング

レコードの登録時に発行する SQL は

1レコード目: INSERT INTO VALUES (AA,AA,AA)◯◯ 2レコード目: INSERT INTO VALUES (BB,BB,BB)◯◯ 3レコード目: INSERT INTO VALUES (CC,CC,CC)◯◯ ・ ・

といった形で RDS に SQL クエリを発行している。この個々のクエリ発行には RDS のの通信が発生するため、この通信オーバーヘッドがレコード数分発生することになる。例えばこのオーバーヘッド時間が 10ms だとすると、 1000 件更新では 10000ms(100秒 ) にまで膨れ上がるこのクエリを

INSET INTO VALUES (AA,AA,AA),(BB,BB,BB),◯◯ ・・・・・

と、 1INSERT文で複数レコードを登録してしまうとこのオーバーヘッド時間(回数)の削減に繋がる

Page 38: バッチ高速化のあゆみ

② クエリ (insert文 ) チューニング

あまりに複数登録しすぎると発行 SQL を文が大きくなりすぎてデバッグが困難になる

INSET INTO VALUES ◯◯ (AA,AA,AA),(BB,BB,BB),(CC,CC,CC),(DD,DD,DD),(EE,EE,EE),(FF,FF,FF),(GG,GG,GG),(HH,HH,HH),(II,II,II),(JJ,JJ,JJ),(KK,KK,KK)(LL,LL,LL) ・・・・・ (LL,LL,LL) ・・・・・・・・・・・・・・・・・・・・・

リスク

Page 39: バッチ高速化のあゆみ

3. 施策   a. インフラ 

①RDS スケールアップ 効果:★★★★☆ リスク:★★★☆☆

Page 40: バッチ高速化のあゆみ

①RDS のスケールアップ

AWS サービスの1つ RDS 。こちらはインスタンスのサイズを1段階上げることにより純粋の処理スペックの向上また、インスタンスサイズに連動してネットワーク性能も向上するので通信上の高速化もさらにストレージをマグネチック( DISK) から SSD へと変更することで IO も高速化

RDS料金 / スペック

Page 41: バッチ高速化のあゆみ

①RDS のスケールアップ

AWS の使用料金が増加スケールアップ時には RDS の再起動が必要。全システムが RDS に依存しているため全てのサービスを停止する必要がある

リスク

Page 42: バッチ高速化のあゆみ

3. 施策   a. インフラ 

②Solr のクラスタリング 効果:★★★★☆ リスク:★★★☆☆

Page 43: バッチ高速化のあゆみ

②Solr のクラスタリング

Solr の台数( EC2 インスタンス ) を増やすことで、必要な処理を分散して全体の許容量を上げるクラスタリングには AWS のロードバランサーにて 2台の Solr へとアクセス分散を実現

Page 44: バッチ高速化のあゆみ

②Solr のクラスタリング

EC2 インスタンスが 1台( Solr 分)と、 EBS の料金が運用コストとして増加するデータ同期が必須。

リスク

Page 45: バッチ高速化のあゆみ

それでも間に合わない・・・

・どれだけ高速化の策を打っても1バッチの処理時間を短縮するには限界がある ➜ インスタンスの性能を上げても、費用に見合う成果は出ない

Page 46: バッチ高速化のあゆみ

3 章 「奥の手」

Page 47: バッチ高速化のあゆみ

4. 奥の手 

マルチプロセス化 効果:★★★★★ リスク:★★★★☆

Page 48: バッチ高速化のあゆみ

② マルチプロセス化

残された課題   1つのバッチ処理時間が長くなりすぎて、将来的に頭打ちになる➜

処理方式の転換   複数バッチサーバ処理へと変更し、処理ペースを掛け算で確保できるようにする➜

Page 49: バッチ高速化のあゆみ

② マルチプロセス化

新しい処理方式① 処理内容の細分化   現在の処理を分割可能な単位で細分化する➜

Page 50: バッチ高速化のあゆみ

② マルチプロセス化

新しい処理方式②細部化された処理を実装   より細かい単位で処理を行う実装へと変更し、複数のサーバ実行に対応する➜

単位で処理を行う実装

Page 51: バッチ高速化のあゆみ

② マルチプロセス化

新しい処理方式③細部化された情報をキューイング   複数サーバが分割された処理対象を順次要求し、処理を行う➜   キューについては AWS の SQS(Simple Queue Service) を採用

Page 52: バッチ高速化のあゆみ

SQS とは?  Amazon Web Service が提供するキューイングシステム 安価で大量のメッセージの送信/配信をサポートし、 SDK も公式で提供している

 

② マルチプロセス化

新しい処理方式

注意点 ・メッセージ配信時に同じものが取れる場合がある ・ FIFO を保証していない 

Page 53: バッチ高速化のあゆみ

② マルチプロセス化

処理サーバ1 処理サーバ2

新しい処理方式

Page 54: バッチ高速化のあゆみ

② マルチプロセス化

SQS の特性「メッセージ配信時に同じものが取れる場合がある」への対応 

同一メッセージが何回も取れる場合、同じ処理を複数実行する可能性がある。防止策として RDS(MySQL) にて処理済みメッセージの管理テーブルを用意しメッセージ受信時に処理済みか否かのチェックを行うことで回避( INSERT 時の一意制約を活用 )

Page 55: バッチ高速化のあゆみ

4 章 「さいごに」

Page 56: バッチ高速化のあゆみ

結果

ここまでの施策によって・・

Page 57: バッチ高速化のあゆみ

結果

処理サーバ2台構成により実行速度が300%UP