Upload
s-akai
View
8.090
Download
0
Embed Size (px)
Citation preview
遅いクエリと向き合う仕組みサイボウズ株式会社⾚井 駿平
1
whoami
▌⾚井 駿平l 2013年4⽉⼊社l アプリケーション基盤チーム所属l プログラミング⾔語の研究→博⼠号取得→サイボウズ
▌ミドルウェアからWebアプリまでl 副業では個⼈でiOSアプリを作ってます
2
アプリケーション基盤チーム
▌サイボウズ内の複数の製品で使うプログラムを作るチームl ミドルウェア
l メッセージキュー、全⽂検索、サムネイル作成、ログ…l Webアプリ
l ユーザー管理機能、プロフィール表⽰…l Webアプリのライブラリ、フレームワーク
l Javaで作ってる製品l 選定したり、作成したり
3
今⽇話すこと
▌ライブラリを作った話l データベース周り
l 障害を減らす仕組みl パフォーマンスを改善するためのORマッパー
4
kintoneの話
5
kintone?
▌Webでデータベースを作れるようなサービスl ドラッグ&ドロップで⼊⼒フォームとデータベースが作れるl SQLでのtable ≒ アプリ
l ただし、スキーマがどんどん変わるl ドキュメント指向データベースっぽい
6
7
8
kintoneの中⾝のはなし
▌永続化にはRDBMS (MySQL) を使っているl 1アプリ=1table ではないl 固定のtableに⾊々な形のアプリがマッピングされるl 複雑なクエリが多く発⾏される場合が…
l 複雑な条件での絞込l パフォーマンスが…
▌詳しくは「kintoneの検索⾼速化への取り組み」を参照l https://www.slideshare.net/RyoMitoma/kintone-73674134
9
複雑なクエリで障害が
▌複雑な絞り込みが⾏われるとリクエストに⼗分単位で時間が掛かる場合がl DBが遅い/⼤量のクエリ/⼤量のメモリ
▌ロードバランサーでは5分以上掛かるリクエストは強制切断しているl アプリケーションサーバー側ではリクエストは⽣き続けるl リロードされると…
10
どうしたものか
▌ クエリ⾃体を改善するl 当然必要l kintoneの性質上、遅いクエリはどうしても発⽣し得る
l 起きちゃった場合の対応をしないといけない▌ クエリのタイムアウト
l MySQL 5.7ではSELECTのタイムアウトが出来るl 当時使ってたのは5.6…l kintoneではそこそこ遅いクエリが多く⾛る
l 使えない▌ 遅すぎるリクエストのみを強制的に終了させたい
l JavaのServlet上で動いているのでkillとか出来ない11
リクエストが⾃分で終了すればいい
12
▌リクエストの中のチェックポイントで経過時間を判定l 超えていたら例外を投げて終了
▌コード中のあらゆる場所に判定するコードを書く?l 量が多くて⼤変l コードが汚くなるl 漏れが発⽣する
よし、アスペクト指向だ!
▌アスペクト指向プログラミング(AOP)l プログラミングの⾊々な場所に処理を挿⼊出来る技術l リフレクションの凄いやつl JavaのWebフレームワークには⼊ってたりする
l あまり流⾏らなかったけど
▌データベースにアクセスするたびに経過時間をチェックする
13
aspectの例
14
public class RepositoryAspect {@Pointcut("execution(public * com.cybozu.Repository+.*(..))") void timeoutTarget() { }
@After(value = "timeoutTarget()") public void after() {
DateTime expire = ((DateTime) request.getAttribute(“expire”));
if (expire != null && expire.isBeforeNow()) {throw new RequestTimeoutException();
}}
}
DBにアクセス全てのメソッド
を実⾏した後で以下の処理を実⾏す
る
効果は?
▌とある1⽇にタイムアウトしてたリクエストは90件くらいl 致命的な障害は防げてそう
▌タイムアウトしたログを頼りにクエリを改善していけるかも
15
ORマッパーを乗り換える
16
S2Dao
▌kintone等ではS2Dao/Seasar2を使ってましたl アノテーションでクエリを書くと、メソッドが⾃動⽣成
17
@S2Dao(bean = User.class)public interface UserDao {@Query(“SELECT * FROM user WHERE id = /*id*/”)List<User> getById(Long id);
}
ORマッパーがEOL!?
▌S2DaoがEOLになりそう! 移⾏しないと▌Spring Data JPA + Hibernate に移⾏しましたl S2Daoと同じようにアノテーションで書けるl JPAという標準だl JPAの中ではHibernateがメジャー?
18
⼤失敗19
パフォーマンスが出ませんでした
▌制御できないキャッシュl kintone では可変⻑の巨⼤なIN句が使われていたl それぞれの⻑さでクエリがキャッシュ
l メモリを圧迫してGC多発l 他にも性能問題がぽろぽろと
▌詳しくはブログにl 「我々はいかにして技術選択を間違えたのか? 2016」l http://blog.cybozu.io/entry/2016/12/28/101500
20
Hibernateをやめたい
▌よい乗り換え先ある?l 他のJPA実装?
l 仕組み上あまりかわらなそうl MyBatis?
l IN句を使うのにforeach⽂みたいなのを書かないといけないl SpringのJdbcTemplate
l 書き⽅が⼤きく変わるl jdbcTemplate.query(“SELECT * FROM user”, rowMapper)
▌困った困った21
⾃分で作っちゃえ
▌JdbcTemplateをSpring Dataでラップするものを作るl 何故か誰も作ってないっぽい
▌コンセプトl 今までと同じようにアノテーションで書けるl 余計なことはしないシンプルなラッパー
l has-many has-one等すらサポートしない
22
コードの例
▌S2DaoやSpring Data JPAと⼤体同じように書ける
23
@Table(name = “user”)class User {
@Id@GeneratedValuepublic Long id;
@Column(“login_name”)public String loginName;
}
@Repositorypublic interface UserRepository extends JdbcTemplateRepository<User> {@Query(“SELECT * FROM user WHERE id=:id”)List<User> getById(@Param(“id”) Long id);
}
切り替えた結果
▌kintoneで全⾯的に乗り換えてから約1ヶ⽉l 今のところクエリ関係が原因の障害は起きてなさそうl 社内独⾃の性能測定では、10%ほど性能が向上した
24
オープンソース化します
▌Spring Data Jdbc Templatel https://github.com/cybozu/spring-data-jdbc-template
▌まだソースのみl maven等に上げるのはこれから検討
▌もうすこし詳しい話はブログに書くかも
25
まとめ
▌アプリケーション基盤チームでは社内のライブラリの整備もしています
▌ものによってはオープンソース化もします
26