Slide 1

Slide 1 text

High memory usage server in Go Go Conference ‘19 Summer in Fukuoka July 13th @haminick

Slide 2

Slide 2 text

自己紹介 ○ DeNAサーバサイドエンジニア ○ 2016年 ~ ゲームプラットフォーム ○ 2018年 ~ ○ オートモーティブ事業部 ○ 次世代タクシー配車アプリMOV

Slide 3

Slide 3 text

本日の資料 twitter #gocon #fukuokago で URL共有中

Slide 4

Slide 4 text

本日のお題 ○ 弊社サービス紹介 ○ 導入: 緯度経度から住所を引くアルゴリズムの話 ○ 直面 & 想定しうる課題 ○ マスターデータのメモリ読み込みを高速化するには ○ データがメモリに乗り切らない場合の対策 ○ 無停止でマスターデータを高速に切り替えるには

Slide 5

Slide 5 text

弊社のサービス紹介 MOV

Slide 6

Slide 6 text

地図を利用したサービスでは住所表示が ユーザの利便性向上に寄与することがある

Slide 7

Slide 7 text

どうすれば緯度経度から住所が引けるのだろう

Slide 8

Slide 8 text

現在地が九州か本州か判定する例

Slide 9

Slide 9 text

現在地にピンを指す ピンが九州か本州か調べたい

Slide 10

Slide 10 text

格子状に区切る

Slide 11

Slide 11 text

現在地がどの格子に所属するかハッシュ関数で計算

Slide 12

Slide 12 text

格子に紐づく本州・九州の多角形データと 衝突判定を行いどちらと衝突したかで決定

Slide 13

Slide 13 text

現在地が九州と判明 なぜ格子で切り出したか -> 多角形データを小さくして高速化する ため

Slide 14

Slide 14 text

多角形データが九州や本州といった属性と紐付き 衝突判定ができれば緯度経度から住所判定可

Slide 15

Slide 15 text

実際のデータでやってみる

Slide 16

Slide 16 text

とある地域の住所に紐づく多角形データをプロットした図

Slide 17

Slide 17 text

高速に応答を返すため全てメモリにキャッシュ予定だったが メモリに乗り切るのだろうか

Slide 18

Slide 18 text

High memory usage server in Go

Slide 19

Slide 19 text

構成

Slide 20

Slide 20 text

課題 #1 データのメモリ読み込みに時間が 掛かる

Slide 21

Slide 21 text

データのメモリ読み込みに時間が掛かる対策 起動時の読み込みを諦め、必要になった場合だけ取得 メリット - サーバ起動時間が高速化 デメリット - サーバdeploy・再起動直後に処理が遅延しサーバが停止する可能性

Slide 22

Slide 22 text

データのメモリ読み込みに時間が掛かる対策 ファイルをアーカイブ - ファイル単位からアーカイブ単位読み込みに変更してオーバーヘッド減り 高速化

Slide 23

Slide 23 text

データのメモリ読み込みに時間が掛かる対策 ファイルフォーマットをJSONからProtocol Buffersに変更 - 4745 ns/op から 914 ns/op に高速化

Slide 24

Slide 24 text

benchmark JSON deserialize sample The Go Playground: https://play.golang.org/p/Y1NwD8KWjhF (Playgroundでは動かないソース共有のみ )

Slide 25

Slide 25 text

課題 #2 データがメモリに乗り切らない規模 になる可能性がある

Slide 26

Slide 26 text

データがメモリに乗り切らない場合の対策 巨大な1ファイル - streamで処理 複数ファイル - 事前にメモリを確保しLRUアルゴリズムで古いデータから消す

Slide 27

Slide 27 text

LRUアルゴリズムとは Least Recently Used (LRU) はキャッシュメモリや仮想メモリが扱うデータのリソース への割り当てを決定するアルゴリズムである。対義語はMost Recently Used (MRU)。 和訳すると「最近最も使われなかったもの」つまり「使われてから最も長い時間が経った もの」「参照される頻度が最も低いもの」である。 source: https://ja.wikipedia.org/wiki/Least_Recently_Used

Slide 28

Slide 28 text

メモリ確保し古いデータをメモリから消すサンプル The Go Playground: https://play.golang.org/p/7J4qWw7fzdr

Slide 29

Slide 29 text

メモリ確保し古いデータをメモリから消すサンプル -

Slide 30

Slide 30 text

書き込み時にLockを取る場合は読み込み処理がブロックされ全体実行速度が 落ちる。分散してキャッシュを持つと影響が最小限になる。 sharding して並列実行性能を高める The Go Playground: https://play.golang.org/p/otst7gRww35

Slide 31

Slide 31 text

メリット - メモリ使用量を制御できる - Garbage Collectionなしでメモリを使いまわせる デメリット - value ごとのsizeバラつきが大きいとメモリ効率悪化 - LRUの実装コスト重い - 既存ライブラリで解決 -> https://github.com/coocood/freecache

Slide 32

Slide 32 text

課題 #3 サーバ起動中に高速に 新しいデータへ切り替えたい

Slide 33

Slide 33 text

データ切り替え 高速に切り替える工夫1 - キャッシュA, Bを用意しA利用中に、Bを準備し準備完了したら切り替える ことで、準備時間が長くても影響ないよう工夫した。

Slide 34

Slide 34 text

データ切り替え 高速に切り替える工夫2 - sync.RWMutex で RLock同士は排他しない仕組みを利用して、逐次処理 を最低限にして高速化する。 RLock() を利用したデータアクセス例

Slide 35

Slide 35 text

ref: https://qiita.com/y_matsuwitter/items/36565a3a53ac52732cae

Slide 36

Slide 36 text

Goを採用してここが良かった Lock, Protocol Buffers, Cache といった部品が揃っている 並列処理をシュっと書ける サーバのCPU / メモリを使いきれる

Slide 37

Slide 37 text

DeNA Go 2019年7月18日にWeb配信予定

Slide 38

Slide 38 text

twitter: @haminick GitHub: https://github.com/subc