スマイルゼミの裏側(db編)
Post on 16-Apr-2017
661 Views
Preview:
TRANSCRIPT
© 2016 JustSystems Corporation.
スマイルゼミの裏側~データベース編~
(株)ジャストシステムILS 事業部開発部
菅井友之
© 2016 JustSystems Corporation.
• スマイルゼミ中学生コース– サーバー担当
• スマイルゼミ小学生コース、中学生コースの立ち上げ– サーバー主担当として参加
• 主に Tomcat 上で動いて PostgreSQL を使うアプリケーションをやっています。
自己紹介
© 2016 JustSystems Corporation.
スマイルゼミでやらかしてしまった話
( DB 的な意味で)
© 2016 JustSystems Corporation.
スマイルゼミでやらかしてしまった話
( DB 的な意味で)PostgreSQL 9.3
© 2016 JustSystems Corporation.
その前に
© 2016 JustSystems Corporation.
スマイルゼミご存じですか?
© 2016 JustSystems Corporation.
• 小中学生向けの家庭学習サービス• すべての学習がタブレット上で完結できる日本初のサー
ビス• 小中一貫で学べる唯一のタブレット教材
© 2016 JustSystems Corporation.
facebook やってます
https://www.facebook.com/smile.zemi/
© 2016 JustSystems Corporation.
小学生 中学生
ある一週間のアクセス状況
© 2016 JustSystems Corporation.
• 小中学生向けのサービス• ユーザーは、日中学校に行っている
小学生 中学生
ある一週間のアクセス状況
© 2016 JustSystems Corporation.
• 小中学生向けのサービス• ユーザーは、日中学校に行っている
平常時、日中の負荷は低い
小学生 中学生
© 2016 JustSystems Corporation.
_人人人人人人人人人_> 突然の負荷上昇 < ̄Y^Y^Y^Y^Y^Y^Y^Y ̄
© 2016 JustSystems Corporation.
• 中学生 DB の朝の負荷であり、垂直に立ち上がっているので、バッチ処理
• グラフが変わる前の日に、 hotfix がリリースされている。• 調べてみると、 course_results で 1300 万行の seq.scan が大量
に発生している。
© 2016 JustSystems Corporation.
CREATE VIEW AS study_log_memorycard_view ( WITH memorycard_info AS ( SELECT ... FROM course_results cr JOIN course_master cm ON cr.course_id AND cm.memory_card_info IS NOT NULL) (SELECT ... FROM memorycard_set_results msr JOIN memorycard_info ... WHERE ...) UNION ALL (SELECT ... FROM memorycard_set_results msr JOIN ... JOIN memorycard_info ... WHERE ... ))
© 2016 JustSystems Corporation.
• 処理の内容は変えずに、 view を使うようにプログラムを変更していた。
© 2016 JustSystems Corporation.
• 処理の内容は変えずに、 view を使うようにプログラムを変更していた。
• view を使わずに処理する場合には、オプティマイザが後続の WHERE を先に処理してくれていたのだろう
© 2016 JustSystems Corporation.
• 処理の内容は変えずに、 view を使うようにプログラムを変更していた。
• view を使わずに処理する場合には、オプティマイザが後続の WHERE を先に処理してくれていたのだろう
• view を使ってしまったために、 course_results が絞り込まれずに含まれてしまう。
© 2016 JustSystems Corporation.
CREATE VIEW AS study_log_memorycard_view ( WITH memorycard_info AS ( SELECT ... FROM course_results cr JOIN course_master cm ON cr.course_id AND cm.memory_card_info IS NOT NULL) (SELECT ... FROM memorycard_set_results msr JOIN memorycard_info ... WHERE ...) UNION ALL (SELECT ... FROM memorycard_set_results msr JOIN ... JOIN memorycard_info ... WHERE ... ))
© 2016 JustSystems Corporation.
• 処理の内容は変えずに、 view を使うようにプログラムを変更していた。
• view を使わずに処理する場合には、オプティマイザが後続の WHERE を先に処理してくれていたのだろう
• view を使ってしまったために、 course_results が絞り込まれずに含まれてしまう。
• さらに、ユーザーごとに view に対するSELECT をしている。– 長時間の高負荷状態に
© 2016 JustSystems Corporation.
対策
© 2016 JustSystems Corporation.
• 前日の学習結果を集計する処理なので、 view 定義の段階で学習日で絞り込むことにした
• view 定義の各 SELECT 文に以下の絞り込み条件を追加
対策
© 2016 JustSystems Corporation.
• 前日の学習結果を集計する処理なので、 view 定義の段階で学習日で絞り込むことにした
• view 定義の各 SELECT 文に以下の絞り込み条件を追加
対策
study_end > (now() - '2 days'::interval)
© 2016 JustSystems Corporation.
• 前日の学習結果を集計する処理なので、 view 定義の段階で学習日で絞り込むことにした
• view 定義の各 SELECT 文に以下の絞り込み条件を追加
• 幸い、他の処理では使っていない view だったので、同名のまますぐに定義変更できた。
対策
study_end > (now() - '2 days'::interval)
© 2016 JustSystems Corporation.
© 2016 JustSystems Corporation.
他の事例
© 2016 JustSystems Corporation.
2013 年 12 月小学生向けみまもるトーク
拡張され続けるサービス
© 2016 JustSystems Corporation.
2013 年 12 月小学生向けみまもるトーク
拡張され続けるサービス
2014 年 11 月中学生向けみまもるトーク
© 2016 JustSystems Corporation.
小学生向けに作ったトーク機能に、中学生を参加させる
© 2016 JustSystems Corporation.
• あるトークグループに参加しているユーザー( talker )の一覧を取得する SQL 文
• - talker は、親、小学生、中学生、その他(親が招待した人)があり、それぞれ別体系の id で管理されている。
SELECT talkers.* FROM talkers LEFT OUTER JOIN accounts ON talkers.account_id = accounts.idWHERE (((accounts.parent_id = xxxxxxxx OR child_id = xxxxxxxx ) OR jh_child_id = xxxxxxxx ) OR account_id = xxxxxxxx )
© 2016 JustSystems Corporation.
• 中学生が増えたのでこの条件が増えた• OR 条件が追加されたことにより、実行計
画がくるってしまって、 Seq Scan on accounts 、 Seq Scan on talkers が発生
• index はあるのに使ってもらえない。
OR jh_child_id = xxxxxxxx
© 2016 JustSystems Corporation.
対策
© 2016 JustSystems Corporation.
対策SELECT talkers.* FROM talkers LEFT OUTER JOIN accounts ON talkers.account_id = accounts.id WHERE (accounts.parent_id = xxxxxxxx)UNIONSELECT talkers.* FROM talkers WHERE (child_id = xxxxxxxx)UNIONSELECT talkers.* FROM talkers WHERE (jh_child_id = xxxxxxxx )UNIONSELECT talkers.* FROM talkers WHERE (account_id = xxxxxxxx ) ORDER BY id;
© 2016 JustSystems Corporation.
• Index scan ですむ単純 SQL にして結果を UNION すると 1000-10000 倍速になった!
– (というよりは、ロジック変更で 1000 倍遅くなっていた)
対策SELECT talkers.* FROM talkers LEFT OUTER JOIN accounts ON talkers.account_id = accounts.id WHERE (accounts.parent_id = xxxxxxxx)UNIONSELECT talkers.* FROM talkers WHERE (child_id = xxxxxxxx)UNIONSELECT talkers.* FROM talkers WHERE (jh_child_id = xxxxxxxx )UNIONSELECT talkers.* FROM talkers WHERE (account_id = xxxxxxxx ) ORDER BY id;
© 2016 JustSystems Corporation.
• UNION が あるところを変更するときは慎重に– UNION していいのは、中間処理結果が十分
に小さいとわかっている場合のみ。– 特に view の中で UNION するのは危険
• 中間テーブルが十分に小さく、素早く、作れるところでは UNION はとても有効!
まとめ
© 2016 JustSystems Corporation.
スマイルゼミを一緒に育てる人、募集しています。
top related