my sqlで2億件のシリアルデータと格闘した話

42
MySQL で 2 ででででででででででででででででででで でででで YAPC::Asia Tokyo 2015 Kenji Saito / @saiken3110

Upload: saiken3110

Post on 11-Apr-2017

7.333 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: My sqlで2億件のシリアルデータと格闘した話

MySQL で 2 億件のシリアルデータと格闘したチューニングの話

YAPC::Asia Tokyo 2015Kenji Saito / @saiken3110

Page 2: My sqlで2億件のシリアルデータと格闘した話

自己紹介

・斉藤 健二  Kenji   Saito・ @saiken3110・ Java   PHP   Perl・ Oracle   MySQL

Page 3: My sqlで2億件のシリアルデータと格闘した話

今日の内容

Page 4: My sqlで2億件のシリアルデータと格闘した話

MySQL で2 億件のシリアルデータを

つかった話

Page 5: My sqlで2億件のシリアルデータと格闘した話

主にチューニングの話です。Perl も最後にちょっとでてきます。。

Page 6: My sqlで2億件のシリアルデータと格闘した話

Topic・システムの概要・負荷テストした話・ 2 億件登録したときの話・ update ではまった話・今後の課題 ・まとめ

Page 7: My sqlで2億件のシリアルデータと格闘した話

システムの概要(フロント)

商品購入

シリアル入力

入力

・商品 A・サイズ

商品サイズ選択

登録

XXL     ▼

商品シリアル紐付 利用履歴

Page 8: My sqlで2億件のシリアルデータと格闘した話

システムの概要(バックエンド)

シリアルマスタ

商品名

シリアル数

商品・シリアル紐付け

紐付け

商品シリアル紐付

2 億件!! 1億件!!

Page 9: My sqlで2億件のシリアルデータと格闘した話

・ Red Hat 5.6・ Perl 5.20・ MySQL 5.6

DB サーバのインフラ事情①

Page 10: My sqlで2億件のシリアルデータと格闘した話

・ CPU   2 コア・メモリ 8 G・ディスク  450G・ /tmp   2G

DB サーバのインフラ事情②

 →  2 コア、 8G 。。。 2 億件。。。

Page 11: My sqlで2億件のシリアルデータと格闘した話

Topic・システムの概要

・負荷テストした話・ 2 億件登録したときの話・ update ではまった話・今後の課題 ・まとめ

Page 12: My sqlで2億件のシリアルデータと格闘した話

□ 対象テーブル ・シリアルマスタ(2億件) ・商品シリアル紐付(1億件)

□ 内容 ・インデックスを使った検索 ・プライマリーキーでの検索 ・フルスキャンでの検索 ・フルスキャンでの count ・フルスキャンでの order by ・ insert ・ alter table (カラム追加)

負荷テスト

Page 13: My sqlで2億件のシリアルデータと格闘した話

いろいろと問題が・・・

Page 14: My sqlで2億件のシリアルデータと格闘した話

○count に 84 秒、 CPU を 80 ~ 95 % 使用  → アプリケーションからは    実行しないため、対応不要とした

○order by や alter で /tmp が枯渇

  →  tmp_dir を物理ディスクに変更    → エラーがでなくなったが、       50G くらい一時領域を使ってた。。       → 運用で回避することにした

負荷テスト 結果①

ERROR 3 (HY000) at line 1: Error writing file '/tmp/MY3p25s8' (Errcode: 28 - No space left on device)

Page 15: My sqlで2億件のシリアルデータと格闘した話

○ 商品シリアル紐付のデータファイルが 28G に。。  → パフォーマンスのために、    各レコードで商品情報を持っていたのをやめた     → 管理用の TBL を作り、       正規化、 15G に縮小!

○insert が意外と早い  「 LOAD DATA 」で 3 億件の登録が 70 分   → 後でいろいろ問題が発生。。

負荷テスト 結果②

Page 16: My sqlで2億件のシリアルデータと格闘した話

Topic・システムの概要・負荷テストした話

・ 2 億件登録したときの話・ update ではまった話・今後の課題 ・まとめ

Page 17: My sqlで2億件のシリアルデータと格闘した話

2 億件登録したときの話①

シリアルマスタ

商品名

シリアル数

商品・シリアル紐付け

紐付け

商品シリアル紐付

2 億件!! 1億件!!

ココ

Page 18: My sqlで2億件のシリアルデータと格闘した話

負荷テストのときに、3億件が 70 分で登録できた

実際のデータで2億件登録しようとしたら、、       

75時間かかった。。

2 億件登録したときの話②

Page 19: My sqlで2億件のシリアルデータと格闘した話

□ 実行時間の推移

2 億件登録したときの話③

※500 万件ずつ「 LODA DATA 」で登録

8000万件から急激に遅延

Page 20: My sqlで2億件のシリアルデータと格闘した話

□ 負荷テストのときより遅くなった原因 → 負荷テストのときはシリアルが連番   実際は、ランダムな数字   →  insert 時に index の再作成が発生!

□8000 万件くらいから急激に遅くなった原因 →  innodb_buffer_pool が枯渇   → 検証してみた

2 億件登録したときの話④

Page 21: My sqlで2億件のシリアルデータと格闘した話

innodb_buffer_pool と insert①

さすがに 75 時間を何度もは無理なので、

innodb_buffer_pool_size を 10 分の1くらいで

試してみた

40 時間くらいかけて。。

Page 22: My sqlで2億件のシリアルデータと格闘した話

innodb_buffer_pool と insert②

innodb_buffer_pool_size を 1G 、 500M 、 250M で10 万件の insert × 100 回にかかる時間を計測

250M で実行した場合、340万件から遅延が発生

500M で実行した場合、700万件から遅延が発生

Page 23: My sqlで2億件のシリアルデータと格闘した話

innodb_buffer_pool と insert③

データ量(登録件数)が増加し、innodb_buffer_pool が枯渇すると、insert が大幅に遅延し始める。

 → メモリからあふれて、   ディスク I/O が発生しているため。

   → メモリを増やすことで、     データ量が多くても遅延が発生しない

Page 24: My sqlで2億件のシリアルデータと格闘した話

ついでに、、

主キーがシーケンスならインデックスの再構築が

早いんじゃないかと思った

試してみた

再びの 40 時間。。

Page 25: My sqlで2億件のシリアルデータと格闘した話

主キーと insert①

主キーをシリアル(ランダムな数字)からシーケンスに変更して、 2 億件の登録にかかる時間を比較

○ 変更前 ・主キー:シリアル ・ユニークキー:シーケンス

○ 変更後 ・主キー:シーケンス ・ユニークキー:シリアル

Page 26: My sqlで2億件のシリアルデータと格闘した話

主キーと insert②

○ 2億件の登録にかかった時間   75時間 → 30時間に改善!   → 主キーが連番になったことで、     主キーのインデックス構築にかかる時間が減少

変更前は2億件の登録に約 75時間

変更後は2億件の登録が約 30時間で完了

Page 27: My sqlで2億件のシリアルデータと格闘した話

Topic・システムの概要・負荷テストした話・ 2 億件登録したときの話

・ update ではまった話・今後の課題 ・その他  Perl のチューニング・まとめ

Page 28: My sqlで2億件のシリアルデータと格闘した話

update ではまった話①

シリアルマスタ

商品名

シリアル数

商品・シリアル紐付け

紐付け

商品シリアル紐付

2 億件!! 1億件!!

ココ

Page 29: My sqlで2億件のシリアルデータと格闘した話

負荷テストのときは、100 万件の紐付が74秒だった

お客さんの前でデモしたら、、       

1時間かかった

update ではまった話②

Page 30: My sqlで2億件のシリアルデータと格闘した話

update ではまった話③

□ アプリケーションの動き ※1回の max が 100 万件で1万件ごとにコミット

 1)シリアルマスタからステータスが   「未使用」のものを取得

 2)商品シリアル紐付へ、   入力した商品の情報をつけてシリアルを insert

 3)シリアルマスタのステータスを「使用済み」に update   → 1回の update で 35 秒かかっていた     →  100 万件の場合、 35 秒 ×100 回! 

Page 31: My sqlで2億件のシリアルデータと格闘した話

update ではまった話④

□update が遅かった原因 →  update 時に index の再構築が発生    → シリアルがランダムなため、      紐付く主キーの読込みが重い。。

read_buffer_size ( 16k)ずつ読み込まれる紐付く主キーがバラバラなため、読込が毎回発生

Page 32: My sqlで2億件のシリアルデータと格闘した話

update ではまった話⑤

□ 対応 ステータス管理をやめ、 管理テーブルを作成し、  update 自体を不要にした

 → 60分が45秒に改善!    → インデックスの再構築怖い。。。

Page 33: My sqlで2億件のシリアルデータと格闘した話

そんなこんなで、なんとか無事リリースできました

よかった

Page 34: My sqlで2億件のシリアルデータと格闘した話

Topic・システムの概要・負荷テストした話・ 2 億件登録したときの話・ update ではまった話

・今後の課題 ・その他  Perl のチューニング・まとめ

Page 35: My sqlで2億件のシリアルデータと格闘した話

今後の課題①

□ 増分バックアップ 現状mysqldump でフルバックアップ  → 今後さらにデータが増えてくると、    バイナリログを使った    増分バックアップの方がよさそう

Page 36: My sqlで2億件のシリアルデータと格闘した話

今後の課題②

□ データ退避とかパーティショニング  → 古いシリアルの退避とか、    できれば「 xxxx_2015 」とか作りたくないので、    うまくパーティションとか使えると。。

Page 37: My sqlで2億件のシリアルデータと格闘した話

Topic・システムの概要・負荷テストした話・ 2 億件登録したときの話・ update ではまった話・今後の課題

・その他  Perl のチューニング・まとめ

Page 38: My sqlで2億件のシリアルデータと格闘した話

その他  Perl のチューニング①

□ コネクションプーリングっぽいこと

 コネクションオブジェクトを シングルトンにし、 1worker 1 コネクションを共有

  → コネクション生成のコストをなくした

Page 39: My sqlで2億件のシリアルデータと格闘した話

その他  Perl のチューニング②

□ マスタデータをキャッシュ

  PSGI サーバ起動時に、メモリに DB のマスタデータを保持  →  worker 内でメモリは共有されるため、     DB アクセスが不要になる

    ※ただし、「 Copy-On-Write 」なので、     書き換えようとすると、メモリリークの原因になる

    ※ただし、マスタデータを変更する場合には、     再起動が必要になるので、     本当は memcached とかを使った方がよさそう

Page 40: My sqlで2億件のシリアルデータと格闘した話

まとめ①

□innodb_buffer_pool_size は大きい方がいい

□ 主キーはシーケンスにした方が insert が早い

□ インデックスの再構築は意外とばかにならない

Page 41: My sqlで2億件のシリアルデータと格闘した話

まとめ②

□ 負荷テスト大事 実際のデータに近いデータでやる

□ 結局計測してみないとわからないことは多い  → 効果測定にすごく時間がかかるので、    負荷テストとかは、早めにやる

Page 42: My sqlで2億件のシリアルデータと格闘した話

ありがとうございました!