Upgrade to Pro — share decks privately, control downloads, hide ads and more …

成長サービスのDB負荷問題を解決する

 成長サービスのDB負荷問題を解決する

Kanazawa Yuki

October 12, 2019
Tweet

More Decks by Kanazawa Yuki

Other Decks in Technology

Transcript

  1. 1)1ΧϯϑΝϨϯεԭೄ ランサーズSREチームの業務  サーバー運⽤ 安定化 レスポンス改善 負荷改善 冗⻑化 セキュリティ対策 最新バージョン追従

    リソース管理 新サービスローンチ⽀援 キャパシティプランニング ドメイン、SSL証明書、リポジトリ管理 サーバー費⽤削減 旧サービス、旧機能の削除 効率化 サーバーのコード化 定型作業の⾃動化 開発⽀援 開発環境構築 リリースシステム構築 旧ソースの削除 社内インフラ、情シス系 社内サーバー 社内LAN PC周り 定型作業の委譲 他
  2. 1)1ΧϯϑΝϨϯεԭೄ ランサーズSREチームの業務  サーバー運⽤ 安定化 レスポンス改善 負荷改善 冗⻑化 セキュリティ対策 最新バージョン追従

    リソース管理 新サービスローンチ⽀援 キャパシティプランニング ドメイン、SSL証明書、リポジトリ管理 サーバー費⽤削減 旧サービス、旧機能の削除 効率化 サーバーのコード化 定型作業の⾃動化 開発⽀援 開発環境構築 リリースシステム構築 旧ソースの削除 社内インフラ、情シス系 社内サーバー 社内LAN PC周り 定型作業の委譲 他 •SREとして認知されている業務
  3. 1)1ΧϯϑΝϨϯεԭೄ ランサーズSREチームの業務  サーバー運⽤ 安定化 レスポンス改善 負荷改善 冗⻑化 セキュリティ対策 最新バージョン追従

    リソース管理 新サービスローンチ⽀援 キャパシティプランニング ドメイン、SSL証明書、リポジトリ管理 サーバー費⽤削減 旧サービス、旧機能の削除 効率化 サーバーのコード化 定型作業の⾃動化 開発⽀援 開発環境構築 リリースシステム構築 旧ソースの削除 社内インフラ、情シス系 社内サーバー 社内LAN PC周り 定型作業の委譲 他 •ランサーズのSREチームが重視している業務 SREチームを介さずに 完結できる仕組みの構築
  4. 1)1ΧϯϑΝϨϯεԭೄ ランサーズSREチームの業務  サーバー運⽤ 安定化 レスポンス改善 負荷改善 冗⻑化 セキュリティ対策 最新バージョン追従

    リソース管理 新サービスローンチ⽀援 キャパシティプランニング ドメイン、SSL証明書、リポジトリ管理 サーバー費⽤削減 旧サービス、旧機能の削除 効率化 サーバーのコード化 定型作業の⾃動化 開発⽀援 開発環境構築 リリースシステム構築 旧ソースの削除 社内インフラ、情シス系 社内サーバー 社内LAN PC周り 定型作業の委譲 他 •スタートアップで必要とされる業務 組織が拡⼤すれば 管理部に委譲できる クラウド化も選択肢
  5. 1)1ΧϯϑΝϨϯεԭೄ 今回お話するテーマ  サーバー運⽤ 安定化 レスポンス改善 負荷改善 冗⻑化 セキュリティ対策 最新バージョン追従

    リソース管理 新サービスローンチ⽀援 キャパシティプランニング ドメイン、SSL証明書、リポジトリ管理 サーバー費⽤削減 旧サービス、旧機能の削除 効率化 サーバーのコード化 定型作業の⾃動化 開発⽀援 開発環境構築 リリースシステム構築 旧ソースの削除 社内インフラ、情シス系 社内サーバー 社内LAN PC周り 定型作業の委譲 他
  6. 1)1ΧϯϑΝϨϯεԭೄ  ランサーズのサーバー構成 EC2 instance CloudSearch CloudFront Route 53 CloudFront

    ALB ALB API Gateway Lambda Auto Scaling App S3 Aurora Reader Aurora Reader Aurora Writer Api ElastiCache Redis AI系API サムネイル表⽰ 仕事検索 ランサー検索 ランサーズ Batch PHP7 CakePHP2.10 Python3 MySQL5.7
  7. 1)1ΧϯϑΝϨϯεԭೄ  前提 •CakePHP2 ◦全てのテーブルに以下のカラムがある ▪id ▪created ▪modified •MySQL5.7 ◦(原則)1テーブルにつき1インデックスしか使えない

    ◦B-Treeインデックス ▪インデックスを利⽤しない検索(スキャン) •O(n) ▪インデックスを利⽤する検索 •O(logn) •AWS ◦RDS Aurora ◦ElastiCache
  8. 1)1ΧϯϑΝϨϯεԭೄ  カーディナリティの低いインデックス •カーディナリティの低いとインデックスの効果が出にくくなる ◦⼩数派の検索が多いか⾒極める必要がある •カーディナリティ2の例 SELECT * FROM proposals

    WHERE deleted = 0 SELECT * FROM proposals WHERE win = 1 インデックス効果 ほとんどなし インデックス効果 あり ◦削除フラグ(deleted) ▪deleted = 0のレコードが⼤多数 ▪deleted = 0の検索が⼤半 ◦当選フラグ(win) ▪win = 0のレコードが⼤多数 ▪win = 1の検索が⼤半
  9. 1)1ΧϯϑΝϨϯεԭೄ  複合インデックスによる改善例 SELECT * FROM payments WHERE created BETWEEN

    '2019-01-01 23:59:59' AND '2019-06-30 23:59:59' AND operation_type IN ('reward', 'external_reward') AND user_id = 122864 ORDER BY id DESC •本番環境で25秒かかるクエリ
  10. 1)1ΧϯϑΝϨϯεԭೄ  スロークエリ改善のステップ •インデックスの確認 •EXPLAINで調査 ◦user_idのインデックスが適⽤されてもrowsが12万超 mysql> EXPLAIN SELECT ->

    * -> FROM -> payments -> WHERE -> created BETWEEN '2019-01-01 23:59:59' AND '2019-06-30 23:59:59' -> AND -> operation_type IN ('reward', 'external_reward') -> AND -> user_id = 122864 -> ORDER BY -> id DESC; +----+-------------+---------+------------+------+--------------------------------+---------+---------+-------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+--------------------------------+---------+---------+-------+--------+----------+-------------+ | 1 | SIMPLE | Payment | NULL | ref | user_id,operation_type_created | user_id | 4 | const | 120624 | 6.50 | Using where | +----+-------------+---------+------------+------+--------------------------------+---------+---------+-------+--------+----------+-------------+ 1 row in set, 1 warning (0.02 sec) mysql> SHOW INDEX FROM payments; +----------+------------+-------------------------+--------------+----------------+-------------+----------+--------+------+------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Cardinality | Sub_part | Packed | Null | Index_type | +----------+------------+-------------------------+--------------+----------------+-------------+----------+--------+------+------------+ | payments | 0 | PRIMARY | 1 | id | 40525056 | NULL | NULL | | BTREE | | payments | 1 | user_id | 1 | user_id | 424991 | NULL | NULL | | BTREE | | payments | 1 | belong_id | 1 | belong_id | 1872714 | NULL | NULL | YES | BTREE | | payments | 1 | operation_type_created | 1 | operation_type | 21 | NULL | NULL | | BTREE | | payments | 1 | operation_type_created | 2 | created | 10692329 | NULL | NULL | YES | BTREE | | payments | 1 | coupon_id | 1 | coupon_id | 381 | NULL | NULL | YES | BTREE | | payments | 1 | modified | 1 | modified | 5868035 | NULL | NULL | YES | BTREE | +----------+------------+-------------------------+--------------+----------------+-------------+----------+--------+------+------------+ 7 rows in set (0.00 sec)
  11. 1)1ΧϯϑΝϨϯεԭೄ  スロークエリ改善のステップ •user_id, createdの複合インデックスを作成 •user_idの単⼀インデックスを削除 ◦↑の複合インデックスで代替できる •EXPLAINを再確認 ◦rowsが120624→5274に減少 •実⾏時間

    ◦25秒→1秒に改善 +----+-------------+---------+------------+------+------------------------------------------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+------------------------------------------+---------+-------+------+----------+-------------+ | 1 | SIMPLE | Payment | NULL | ref | user_id_created,oper.. | user_id_created | 4 | const | 5274 | 50.00 | Using where | +----+-------------+---------+------------+------+------------------------------------------+---------+-------+------+----------+-------------+ 1 row in set, 1 warning (0.02 sec) mysql> ALTER TABLE payments ADD INDEX user_id_created(user_id, created) mysql> ALTER TABLE payments DROP INDEX user_id;
  12. 1)1ΧϯϑΝϨϯεԭೄ  (緊急⼿段)FORCE INDEX(USE INDEX)を使う •CakePHP2の場合 ◦FROMの直後にUSE INDEXを付与することが可能 ▪公式にサポートされている⽅法ではないので注意 •注意点

    ◦user_id_createdのインデックスを消すとエラーになる ▪PRIMARYなら⼤丈夫 ◦MySQLの統計情報に関わらず同じインデックスを使い続ける ▪特にFORCE INDEXの場合 •なるべくUSE INDEXにしておいた⽅が良い ▪定期的に観測し、問題なくなったら削除する $this->Payment->find('first', [ 'conditions' => [ 'user_id' => $id, 'created >' => date('Y-m-d', strtotime($format)), ], 'joins' => ['USE INDEX(user_id_created)'], SELECT FROM payments USE INDEX (use_id_created) WHERE user_id = $id AND created > '2019-09-16' ORDER BY id LIMIT 1
  13. 1)1ΧϯϑΝϨϯεԭೄ  インデックスが効かなくなる例(UNIONクエリ) •※status, createdにそれぞれ単⼀インデックスが付与されている場合 ◦2つに分割してそれぞれインデックスを効かせる SELECT id FROM results

    WHERE work_id = 1 AND status IN ('worked', 'working') UNION SELECT id FROM results WHERE work_id = 1 AND created > '2019-10-01' SELECT id FROM results WHERE work_id = 1 AND status IN ('worked', 'working') SELECT id FROM results AS Result WHERE work_id = 1 AND created > '2019-10-01'
  14. 1)1ΧϯϑΝϨϯεԭೄ  インデックスが効かなくなる例(SQLで条件分岐) •IF、CASEで条件を分岐しているパターン ◦条件の処理はPHP側で⾏う SELECT * FROM payments WHERE

    user_id = 1136075 AND CASE WHEN belong_to IN ('TimechargeInvoice', 'Milestone') THEN operation_type IN ('withholding_client') ELSE operation_type IN ('withholding_lancer') END; SELECT * FROM payments WHERE user_id = 1136075 AND operation_type IN ('withholding_client') SELECT * FROM payments WHERE user_id = 1136075 AND operation_type IN ('withholding_lancer')
  15. 1)1ΧϯϑΝϨϯεԭೄ  参照SQLをRead Replicaに分散する •読み込みの負荷が⾼いサービスの場合 ◦Read Replicaに分散させることで回避可能 ◦Auroraなら15台まで増やせる ▪RDS for

    MySQLは5台まで •CakePHPはRead Replicaに切り替える機能はサポートされていない ◦独⾃で実装する必要がある •CakePHP2のプラグインが提供されている ◦https://packagist.org/packages/connehito/cakephp2-master-replica EC2 Aurora Reader Aurora Reader Aurora Writer
  16. 1)1ΧϯϑΝϨϯεԭೄ  MySQLの全⽂検索 SELECT * FROM works WHERE MATCH (title)

    AGAINST ('PHP') OR MATCH (description) AGAINST ('PHP') MySQL 5.6 MySQL 5.7 EC2 on MySQL スペース区切り ※MroongaはN-Gram、Mecabをサポート スペース区切り N-Gram 形態素解析(Mecab) RDS for MySQL スペース区切り スペース区切り N-Gram Aurora スペース区切り スペース区切り •MATCH 〜 AGAINST構⽂でサポートされている全⽂検索機能 ◦※インデックスが1つしか使えない点は考慮が必要 ▪複雑な条件で検索し始めると⾏き詰まる