Your SlideShare is downloading. ×
クリスマスを支える俺たちとJava(JJUG CCC 2015 Spring AB4)
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

クリスマスを支える俺たちとJava(JJUG CCC 2015 Spring AB4)

204
views

Published on

2015/04/11に開催されたJJUG CCC 2015 SpringのタイムテーブルAB4にて発表した「クリスマスを支える俺たちとJava」の資料です。 …

2015/04/11に開催されたJJUG CCC 2015 SpringのタイムテーブルAB4にて発表した「クリスマスを支える俺たちとJava」の資料です。

---
AB-4 クリスマスを支える俺たちとJava

阪田 浩一 (フリュー株式会社/関西Javaエンジニアの会)

プリントシール機が、話題になった10年以上前のころと変わらず若い女性に利用されていることをご存知でしょうか?

私が所属するフリュー株式会社は、プリントシール機(プリ機)を出している会社です。そしてWebにてプリ機と連動して画像を取得するサービスを提供しています。実はこのサービス、会員数が1000万人を超えており、女性で特定の年齢層であれば90%以上の方が会員となっています。

JavaとRDBMS、分散ファイルシステムMogileFSにて構築したこの少し古いWebアプリケーションは、当初ここまでの規模になるとは想定していませんでした。

スーパーエンジニアがいない普通のエンジニアチームが、この数年間で増加する会員数とともにぶち当たった問題とその解決策についてご紹介します。

Published in: Technology

0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
204
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
2
Comments
0
Likes
2
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. クリスマスを支える   俺たちとJava 阪田  浩一   フリュー株式会社   関西Javaエンジニアの会 1
  • 2. 2 2 @jyukutyo 著書 3冊 関西Javaエンジニアの会 (関ジャバ) 中の人・発起人 blog:Fight the Future http://jyukutyo.hatenablog.com/ フリュー株式会社 所属 エンジニア歴12年 36歳 SI業界での客先常駐 9年 Web系? 3年 文学部哲学科卒 塾講師アルバイト 阪田 浩一
  • 3. 要約 3 普通のエンジニアたちが
  • 4. 要約 4 いろんな   技術的課題、   プロセスの課題、   チームの課題に
  • 5. 要約 5 ぶつかりつつも   サービスを開発、運用   しているお話です
  • 6. 6 扱えないこと:   大規模サービスを   運用する   ベストな方法
  • 7. 目次 7 • 対象となるアプリケーションについて   • わき上がる課題とその対処   • RDBMS編   • JVM編   • サーバ編   • まとめ
  • 8. 要約 8 本題へ
  • 9. 9 僕たちのアプリケーションは   クリスマスの負荷に   耐えられずにいた Photo by Yuichi Sakuraba https://flic.kr/p/9JYRSQ
  • 10. 10 そう、クリスマスは   1年で最もアクセスが   ある日 Photo by Yuichi Sakuraba https://flic.kr/p/9JYRSQ
  • 11. 11 恋人たちがみんな   撮影をする日だからだ Photo by Yuichi Sakuraba https://flic.kr/p/9JYRSQ
  • 12. 12 撮影?   そう、プリントシール機   での撮影…
  • 13. プリントシール機とは 13
  • 14. 14 弊社フリュー株式会社は   プリントシール機の   トップメーカーです
  • 15. 15 全国の機械の   半分以上がフリューです
  • 16. 16 なのですが、   今回はこの筐体の   実装ではなく
  • 17. 簡単な図 17 ここの詳細に   ついてです Internet
  • 18. Webとの関係は? 18 撮影した画像は   Webサイト/アプリから   取得できます
  • 19. 19 アプリを除く   Webシステム部分   (サイト、API、DB、   ファイルサーバなど)   について話します
  • 20. 20 会員数1000万人:   10代から20代の女性   (女子高生は   クラスほぼ全員が会員)
  • 21. サービスの概要 21 サービス名 ピクトリンク 会員数 1000万人   (有料会員数は秘密…) 主な機能 プリントシール機で   撮影した画像を   取得できる 提供 Webサイト   iOSアプリ   Androidアプリ 課金 携帯電話キャリア課金   (月額制) PV 数百万PV/日
  • 22. 会員数の推移 22
  • 23. 23 画像は10億枚弱   (うかつにcount(*)も   できない)
  • 24. 24 大規模と思っています
  • 25. 25 現システムは   4年前に構築   (前身サービスは   2003年に開始)
  • 26. 私の立ち位置 26 私は3年前にJoinし、   今サイト開発のリーダ   という立場です   (つまりリリース後参画)
  • 27. 開発チーム 27 アプリ開発   (ネイティブ/API)   7名 サイト開発   7名 インフラ   (サーバ/NW/DBA)   開発部門全体2名   チーム内2名   ログ分析   2名 HTML/CSS   2名
  • 28. 28 リリース後1,2年は   クリスマスに   サービスが瀕死 Photo by Yuichi Sakuraba https://flic.kr/p/9JYRSQ
  • 29. 29 なぜか?
  • 30. 30 まず、Webサーバの   ロードアベレージが   非常に高かった
  • 31. 31 平日は数百万PV/日   だが   クリスマスはその4倍 Photo by Yuichi Sakuraba https://flic.kr/p/9JYRSQ
  • 32. 32 夕食の時間「前後」に   とくに集中する   (ピークタイムが   数時間単位となる) Photo by Yuichi Sakuraba https://flic.kr/p/9JYRSQ
  • 33. 33 リクエストが   さばけていない
  • 34. 34 プリントシール機で   撮影した画像(プリ画像)   を保存する処理なので   軽い処理ではない
  • 35. 35 ここでサーバ構成を   紹介
  • 36. 現在のサーバ構成 36台数は概数です ユーザが保存処理 を実行するまでの 1週間分のみ  
  • 37. 37 ちょっと横道へ
  • 38. 38 MogileFSとは:   分散ファイルシステム   (OSS)
  • 39. MogileFSとは 39 tracker:   管理サーバ。   クライアントとやり 取りする。 storage:   ファイル保存   サーバ。   同一ファイルを   複数台で保持。   ファイルにはHTTP   でアクセスできる。 metadata:   RDBMS。   ファイル情報や   全体の設定を   保存する。
  • 40. 40 ストレージノードが   数十台   (まだ増え続ける…)
  • 41. 41 ここまで大きな問題は   出ていない
  • 42. 42 ファイルやノードを   管理するMySQLが   ボトルネックとなる   かもしれない
  • 43. 43 ストレージノードは   現実的には何台まで   いけるのかわからないので、 だんだん怖くなっている
  • 44. 44 リクエストが   さばけていない件
  • 45. 45 対応:Webサーバを   台数追加した
  • 46. 46 教訓:お金を払おう
  • 47. 47 解決(えっ
  • 48. 48 サーバは   増やせば対応できたが
  • 49. 49 今度は   RDBMSが   ネックとなってくる
  • 50. 50 next:RDBMS編
  • 51. 51 ここでアーキテクチャを   紹介
  • 52. アーキテクチャ 52 Javaフレームワーク Seasarファミリー   (Cubby/S2Dao  etc) ファイルシステム   (画像ファイル保存) MogileFS RDBMS Oracle  11g(メイン)   MySQL  5.6 Webサーバ/   サーブレットコンテナ Apache/   Tomcat Javaバージョン 6  -­>  8
  • 53. 53 • 近い将来メモリが不足する予 測ができた   • IO性能が悪かった   • レコード件数が多い   • (テーブル設計もベストではな いが)
  • 54. 54 クエリやインデックスを   変更してコストを削減、   でどうにかなる   レベルではなかった
  • 55. 55 RDBMSの移行
  • 56. 56 変更前 Oracle  Database  11g     Standard  Edition 1台 変更後 Oracle  Database  11g     Real  Application   Clusters(RAC) 複数台
  • 57. 57 RACとは:   ロードバランス型の   Oracleクラスタリング構成
  • 58. 58 Copyright© 2011, Oracle. All rights reserved. 5.7 Oracle Instance Oracle Instance Oracle Instance Active-Activeの構成をとることが可能 RACは、ノード追加によるスケールアウトで性能向上 process process process process process process 「実践!高可用性システム構築  〜~RAC基本編〜~」より引用
  • 59. 59 全ノードが全データに   アクセスできる   Acitve-­Active構成   となる
  • 60. 60 さらに   ハードウェアも変更した
  • 61. 61 パフォーマンスは   劇的に改善した
  • 62. 62 解決(えっ
  • 63. 63 教訓:お金を払おう
  • 64. 64 ただ、そうした増強分も   使い果たしつつある
  • 65. 65 レコードが増えたことで   見えていなかった問題が   浮き彫りになる
  • 66. 66 DBAが来るまで、   DBサーバのOS、   Oracleとも   適切にパラメータが   設定されていなかった
  • 67. 67 そうしたパラメータを   1つずつ精査しつつ…
  • 68. 68 クエリのコストや   実行回数を   調査すると…
  • 69. 69 DBA「夜間バッチの   1クエリがコスト   500万です!」
  • 70. 70 理由:テーブルを   フルスキャンしている
  • 71. 71 テーブルに   インデックスは…
  • 72. 72 貼られている!
  • 73. 73 クエリもインデックスを   使ったカラムを   where句で   指定している
  • 74. 74 ログに出したSQL文を   SQLDeveloperで   実行してもすぐ終わる。   実行計画でも   インデックスを使っている
  • 75. 詳細: 75 しかし実環境では   DATE型カラムへの   インデックスが   使われていない。
  • 76. 詳細: 76 ???
  • 77. 詳細: 77 Oracleを見てみる
  • 78. 詳細: 78 SELECT SA.SQL_ID, NAME, WAS_CAPTURED, BC.LAST_CAPTURED, VALUE_STRING, anydata.accesstimestamp(value_anydata), datatype_string, sql_text FROM GV$SQL_BIND_CAPTURE BC, GV$SQLAREA SA   WHERE BC.HASH_VALUE = SA.HASH_VALUE AND SA.SQL_ID = 'hoge'   ORDER BY LAST_CAPTURED DESC;
  • 79. 詳細: 79 datatype_string -> TIMESTAMP
  • 80. 詳細: 80 バインド変数の   値の型(Oracle上)が   TIMESTAMPと   なっている!
  • 81. 81 Javaアプリケーションでの型 java.util.Date 永続化ライブラリでの型 java.sql.Timestamp JDBCでセットする時の型 java.sql.Types.Timestamp Oracleでの型 TIMESTAMP
  • 82. 結論: 82 OracleのDATEは   ANSI定義と異なり   時間の情報を持つ
  • 83. 結論: 83 Types.Timestampとすることで   時間情報も保持していた   が、そのために   OracleでTIMESTAMPとなり   インデックスが使われなかった
  • 84. 結論: 84 対応:Oracle独自の   PreparedStetementを   使うことにした
  • 85. 対応: 85 if (ps instanceof OraclePreparedStatement){ OraclePreparedStatement ops = (OraclePreparedStatement) ps; // Oracle JDBCドライバ独自のメソッド、引数の型を利用する ops.setDATE(index, new oracle.sql.DATE(new java.sql.Timestamp(toDate(value).getTime()))); } else { ps.setDate(index, toSqlDate(value)); }
  • 86. 結果: 86 コスト:   500万  -­>  8万
  • 87. 結果: 87 大勝利
  • 88. 結果: 88 ってか何年間も   誰も気づかなかったのか
  • 89. 結果: 89 教訓:実行計画を   SQLクライアントで   見るだけでなく   実環境の実行コストも   見よう
  • 90. 結果: 90 RDBMS関連だと   こんな問題も   ありました
  • 91. 91 企画「アプリでiOSの   絵文字を使えるように   したい!」
  • 92. 92 お、Unicodeで   定義された   絵文字だな
  • 93. 93 SELECT VALUE FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER=‘NLS_CHARACTERSET'; ! -> JA16SJISTILDE
  • 94. 94 Shift_JISェ…
  • 95. 95 「NVARCHAR2に   カラムの型を   変更すればいける!」
  • 96. 96 JDBCでNVARCHAR2列   に絵文字を入れる場合、   JVMパラメータに   「-Doracle.jdbc.defaultNChar=true」   を追加する必要がある http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/java.102/B19275-03/global.htm
  • 97. 97 先にJVMパラメータだけ   つけてアプリケーション   起動しておくか   (ステージングでは   問題なし)
  • 98. 98 そう、あのときの   俺たちには   何の疑念も   なかったんだ…
  • 99. 結果: 99 再起動した瞬間に   全ユーザが   ログインできなくなる   (正確にはものすごく遅い)
  • 100. 100 理由:テーブルを   フルスキャンしている
  • 101. 詳細: 101 defaultNChar=true すると、VARCHAR2カラム   のバインド変数に   SYS_OP_C2C関数が   適用される
  • 102. 詳細: 102 とあるテーブルのカラムhogeが   VARCHAR2型のとき、   defaultNChar=trueがあると… PreparedStetemetの   SQL where hoge = ? Oracleで実行される   SQL where hoge = SYS_OP_C2C(:1)
  • 103. 詳細: 103 hogeへの   インデックスが   使われない
  • 104. 対応: 104 SYS_OP_C2C関数を   使った   関数インデックスを   すべて作成した
  • 105. 結果: 105 教訓:実行計画を   SQLクライアントで   見るだけでなく   実環境の実行コストも   見よう
  • 106. 将来: 106 UTF-­8への移行を   計画中
  • 107. 将来: 107 next:JVM編
  • 108. 108 ある日、   JVMオプションがほとんど   設定されていないことに気づく   (XmxとPermGenくらい)
  • 109. 109 僕「GCログ出してみよう。」   -Xloggc:gc.log -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NumberOfGCLogFiles=3 -XX:GCLogFileSize=10M
  • 110. 110 数日後
  • 111. 111
  • 112. 112 僕「なんてこった…」
  • 113. 結果: 113 確保しているヒープ:   最大3.2GB   フルGC時間:   最大0.9秒/回
  • 114. 114 こんなにヒープ領域   使うほどのシステムでは   ないような…   これかなりSTWしてるよね
  • 115. 115 対応:GCアルゴリズムを   CMSにしよう!   ※CMS  =  Concurrent  Mark  &  Sweep   ! -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSClassUnloadingEnabled
  • 116. 詳細: 116 GCアルゴリズムの変更前後 変更前 Parallel GC【デフォルトGC】 (マイナーGCのみをパラレルで) 変更後 CMS GC【J2SE 1.4から】 ご存じない方は、ぜひsugarlife's blogさんの ブログエントリ「CMS GC おさらい」を! http://cco.hatenablog.jp/entry/2014/12/01/162240
  • 117. 結果: 117 確保した   最大ヒープ領域 STW時間 変更前 3.2GB 最大 0.9秒/平均0.5秒 変更後 1.2GB 最大0.23秒/平均0.2秒 0 1 2 3 4 変更前 変更後 1.2 3.2 0 0.225 0.45 0.675 0.9 変更前 変更後 0.23 0.9
  • 118. 118 大幅改善!
  • 119. 119
  • 120. 120 ただし、CMSは   CPUリソースを使うので、   スループットが低下する   恐れがある
  • 121. 121 僕らの場合、   スループットが1%低下し   CPUの負荷も   やや上がった
  • 122. 結果: 122 教訓:JVMは   デフォルトでも   かなりいける   (人類の叡智)
  • 123. 結果: 123 教訓:ただし、   測定はしておき、   増加傾向が出たら   対処していく   (最初から考えすぎない)
  • 124. 124 ある日、   アクセスの少ない夜中に   OutOfMemoryError   が発生し、   再起動する
  • 125. 125 「-­XX:+HeapDumpOnOutOfMemoryError」   しているので   ダンプファイルがある
  • 126. 126 java_pid<9999>.hprof   4.3GB…
  • 127. 127 このダンプファイル   どうしたらいいんだっけ?
  • 128. 128 Eclipse  Memory   Analyzer(mat)   を使う https://eclipse.org/mat/
  • 129. インストール方法 129 • Eclipseプラグインとして     Update  Managerからインストール   • スタンドアローン版をダウンロード
  • 130. 130 いろいろな方法で   可視化してくれた
  • 131. 131
  • 132. 132 ふむ…
  • 133. 133
  • 134. 134
  • 135. 135
  • 136. 136 うん…
  • 137. 137
  • 138. 138 ん!?
  • 139. 139 net.rubyeye.xmemcached.impl.MemcachedTCPSession のインスタンスが   ヒープの85.28%を   占めてる!
  • 140. 140 1インスタンスが200MB   とか…
  • 141. 141 対応:このライブラリの   バージョンを上げよう!
  • 142. 結果: 142 教訓:   「-­XX:+HeapDumpOnOutOfMemoryError」   ほんとに役立つ!   (これまで本番で   OOME出たことなかった)
  • 143. 143 ある日、Java  SE  8が   リリースされる
  • 144. 144 もう6から8に   したくてしょうがなくなる
  • 145. 145 まずTomcatの   起動VMを6から8に   してみた
  • 146. 146 結果:何も問題   なかった
  • 147. 147 プロジェクトのJDKを   6から8にしてみた
  • 148. 148 結果:ユニットテストが   ほとんど全部エラーになる
  • 149. 149 Caused  by:  java.lang.ArrayIndexOutOfBoundsException:  31038     at  org.objectweb.asm.ClassReader.<init>(Unknown  Source)     at  org.objectweb.asm.ClassReader.<init>(Unknown  Source)     at  org.objectweb.asm.ClassReader.<init>(Unknown  Source)     at  org.objectweb.asm.ClassReader.<init>(Unknown  Source)     at   jp.co.dgic.testing.common.AsmClassModifier.getModifiedClass(AsmClas sModifier.java:49)  
  • 150. 150 JUnit  +  djUnit…
  • 151. 151 indyが入ったことで   djUnitが依存している   ASMがエラーとなる
  • 152. 152 ASMを最新にすると   APIが変わり   djUnitがエラーとなる
  • 153. 153 うーん…
  • 154. 154 djUnitを止め、   違うモックライブラリを   使うことにする
  • 155. 155 対応:jMockitにして   テストケースを   すべて書き直す
  • 156. 156 jMockitにした理由:   djUnitにある機能が   すべてそろっていたから   (意味を考えずに   置換できる)
  • 157. 157 その他対応:   Javassistでエラーが   出たりしたので、   これもバージョンを   上げた
  • 158. 結果: 158 教訓:   Java  SE  8を   導入しよう   (せめて実行環境   からでも!)
  • 159. 159 next:サーバ編
  • 160. 160 そうこうしている間に   サーバ台数が   増えてきた
  • 161. 161 再起動時に   各サーバのログ見るの   面倒だなあ
  • 162. 162 全台SSHで   tail  -­f   (less  +Fも僕してたよ!)
  • 163. 163 見てられん…
  • 164. 164 ログ収集:   fluentd  +  Elasticsearch   +  Kibana   で見るのが楽になった
  • 165. 165
  • 166. 166 fluentdでサーバ全台の   ログを集め、   それ1つを見るだけで   済む
  • 167. 167 さらに
  • 168. 168
  • 169. 169 Kibanaで可視化   される
  • 170. 170 教訓:   「ログの可視化は   エンジニアの責任」
  • 171. 171 ログはHDFSにも   入れているので   将来的に利用する   予定だ
  • 172. 172 話は変わり、   継続的に   サーバが追加される状況
  • 173. 173 サーバ追加で   いちいちインストール   してられん…
  • 174. 174 サーバ構築:   Ansible化を   進めている
  • 175. 175 結局、去年は   クリスマスに   何も起こりませんでした Photo by Yuichi Sakuraba https://flic.kr/p/9JYRSQ
  • 176. 176 今日の教訓のまとめ   • お金を払おう   • 実行計画をSQLクライアントで見るだけでなく実環境 の実行コストも見よう   • JVMはデフォルトでもかなりいける(人類の叡智)   • ただし、測定はしておき、増加傾向が出たら対処し ていく(最初から考えすぎない)   • Java  SE  8を導入しよう(せめて実行環境からでも!)   • ログの可視化はエンジニアの責任
  • 177. まとめ 177 スーパーエンジニアが   いるわけじゃない
  • 178. まとめ 178 普通のエンジニアたちが   悪戦苦闘しながら   運用してるよ
  • 179. まとめ 179 かっこいい方法じゃ   ない部分もあるけど、   運用できてる!
  • 180. まとめ 180 また   こうやって得たものを   発信したいです!
  • 181. 181 ご清聴   ありがとうございました