Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

JavaでインメモリSQLエンジンを作ってみた

2,731 views

Published on

JJUG CCC 2018 Spring での発表資料です。 #jjug_ccc #ccc_c4

Published in: Software
  • Be the first to comment

JavaでインメモリSQLエンジンを作ってみた

  1. 1. JUSTSYSTEMSJUSTSYSTEMS JavaでインメモリSQLエンジンを 作ってみた 2018/5/26 JustSystems 毛利元彦 1
  2. 2. JUSTSYSTEMSJUSTSYSTEMS 自己紹介 • 株式会社ジャストシステム 毛利元彦 • なんでも速くする係 • 検索エンジン、Webクローラ、Hadoopを使った自然言 語処理、BIツールなど – 大規模データを高速処理することがメインタスク 2
  3. 3. JUSTSYSTEMSJUSTSYSTEMS 今日の内容 • RDBでの集計 • BIツールでの集計 • インメモリSQLエンジンの高速化 • 性能計測のTips • まとめ 3
  4. 4. JUSTSYSTEMSJUSTSYSTEMS RDBでの集計 • select sum(c0) from table; • select c1, sum(c0) from table group by c1; • select c1, sum(c0) from table group by c1 where c2 in ('hoge'); 4
  5. 5. JUSTSYSTEMSJUSTSYSTEMS インメモリSQLエンジンの集計速度 • select sum(c0) from table; 5 行数 10万 100万 1000万 1億 PostgreSQL (9.6.3) 70ms 251ms 3561ms 39751ms IMSQL
  6. 6. JUSTSYSTEMSJUSTSYSTEMS インメモリSQLエンジンの集計速度 • select sum(c0) from table; 6 100倍ぐらい高速 行数 10万 100万 1000万 1億 PostgreSQL (9.6.3) 70ms 251ms 3561ms 39751ms IMSQL 1ms 5ms 39ms 389ms
  7. 7. JUSTSYSTEMSJUSTSYSTEMS BIツールでの集計 • クロス集計 (Actionista!デモ) 7
  8. 8. JUSTSYSTEMSJUSTSYSTEMS BIツールでの集計 • クロス集計 8 8 太郎 花子 ATOK 売上 売上 売上 1月 ○ 2月 △ 3月 □ 商品 月 売上 商品 月 売上 太郎 1月 ○ 花子 2月 △ ATOK 3月 □ IMSQLOLAPエンジン ResultSet SQL select 月,商品,sum(売上) group by 月, 商品 SQLを高速実行することが、全体の高速化のキモ
  9. 9. JUSTSYSTEMSJUSTSYSTEMS インメモリSQLエンジンの設計方針 • 行指向ではなく列指向 – 列指向は集計に特化したアーキテクチャ • 更新系はばっさり捨てる – インメモリーデータベースではなくインメモリSQLエンジン – 定期的にバッチで取り込む • 参照系もBIツールで必要なSQLだけ実装 – 集計関数 sum, count, min, max(avgはあえて実装しない) – グループ化 group by – 絞り込み where • 可能な限りOLAPエンジンで吸収する – SQLエンジンを小さく保つ、と同時にSQLの発行回数を減らす 9 高速化のために徹底的に割り切る (あと工数削減)
  10. 10. JUSTSYSTEMSJUSTSYSTEMS avgを実装しない理由 • BIツール側でsum/countで求める 10 平均売上 1月 100 2月 200 3月 150 平均売上 1月 100 2月 200 3月 150 平均売上 売上合計 売上個数 1月 100 500 5 2月 200 1600 8 3月 150 450 3 平均売上 売上合計 売上個数 1月 100 500 5 2月 200 1600 8 3月 150 450 3 平均 平均売上 売上合計 売上個数 1月 100 500 5 2月 200 1600 8 3月 150 450 3 平均 2550 16 平均売上 売上合計 売上個数 1月 100 500 5 2月 200 1600 8 3月 150 450 3 平均 159 2550 16
  11. 11. JUSTSYSTEMSJUSTSYSTEMS 行指向vs列指向 • 行指向では一つのカラムにアクセスするときにも全デー タを取得 • 1000万行の集計時間 • select sum(column) from table; 11 年 月 日 製品名 氏名 売上 … … … … … … … … … … … … 2014年 2月 15日 一太郎 徳島次郎 20,000 … … … … … … … … … … …
  12. 12. JUSTSYSTEMSJUSTSYSTEMS • 列指向では列がアクセス単位なので、必要なカラムの みアクセス 製品名 … … … … … 行指向vs列指向 月 … … … … … 氏名 … … … … … 売上 13,000 15,000 20,000 5,000 8,000 日 … … … … … 年 … … … … …
  13. 13. JUSTSYSTEMSJUSTSYSTEMS • バッチで列データを作成 製品名 … … … … … 行指向vs列指向 月 … … … … … 氏名 … … … … … 売上 13,000 15,000 20,000 5,000 8,000 日 … … … … … 年 … … … … … long[]
  14. 14. JUSTSYSTEMSJUSTSYSTEMS • 集計がシンプルになる 製品名 … … … … … 行指向vs列指向 月 … … … … … 氏名 … … … … … 売上 13,000 15,000 20,000 5,000 8,000 日 … … … … … 年 … … … … … long[] for (int i = 0; i < c0.lengh; i++) { sum += c0[i]; }
  15. 15. JUSTSYSTEMSJUSTSYSTEMS 行指向と列指向の性能傾向比較 • select sum(c0) from table; • 1000万行 15 カラム数 1 10 100 PostgreSQL 2009ms 2984ms 29889ms IMSQL
  16. 16. JUSTSYSTEMSJUSTSYSTEMS 行指向と列指向の性能傾向比較 • select sum(c0) from table; • 1000万行 16 列数が増えても性能劣化がない カラム数 1 10 100 PostgreSQL 2009ms 2984ms 29889ms IMSQL 48ms 42ms 45ms
  17. 17. JUSTSYSTEMSJUSTSYSTEMS • select sum(c0) from table; • select c1, sum(c0) from table group by c1; • select c1, sum(c0) from table group by c1 where c2 in ('hoge'); 17 次のようなSQLの高速化について説明していく
  18. 18. JUSTSYSTEMSJUSTSYSTEMS select sum(c0) from table; • 取り込みでlongの配列になっている 18 集計関数sum for (int i = 0; i < c0.lengh; i++) { sum += c0[i]; } 実はむちゃくちゃ速い! この性能はそのままは出ない 行数 100万 1000万 1億 long[] 2ms 6ms 57ms
  19. 19. JUSTSYSTEMSJUSTSYSTEMS select sum(c0) from table; • 繰り上がりを考慮 19 集計関数sum BigInteger sum = BigInteger.zero; for (int i = 0; i < c0.lengh; i++) { sum = sum.add(BigInteger.valueOf(c0[i])); } 行数 100万 1000万 1億 long[] 2ms 6ms 57ms BigInteger 37ms 342ms 3470ms BigIntegerやばい
  20. 20. JUSTSYSTEMSJUSTSYSTEMS select sum(c0) from table; • 繰り上がりを自前でサポート 20 集計関数sum for (int i = 0; i < c0.lengh; i++) { sum += c0[i]; if (sum >= max) { over++; sum -= max; } else if (sum <= -max) { over-- sum += max; } } (取り込み時に15桁に制限)
  21. 21. JUSTSYSTEMSJUSTSYSTEMS 21 自前で繰り上げ vs BigInteger 行数 100万 1000万 1億 long[] 2ms 6ms 57ms BigInteger 37ms 342ms 3470ms 自前 3ms 24ms 236ms BigIntegerに比べて15倍程度高速
  22. 22. JUSTSYSTEMSJUSTSYSTEMS マルチスレッド • 集合関数はマルチスレッドと相性がいい 22 売上 13,000 15,000 8,000 売上 20,000 5,000 38,000 売上 18,00 4,000 9,000 売上 13,000 15,000 8,000 20,000 5,000 38,000 18,00 4,000 9,000 36,000 63,000 13,000 分割 スレッドごと に集計 合算 112,000
  23. 23. JUSTSYSTEMSJUSTSYSTEMS • select sum(c0) from table; マルチスレッド性能 23 行数 100万 1000万 1億 1スレッド 15ms 149ms 1499ms 2スレッド 9ms 86ms 806ms 3スレッド 7ms 51ms 520ms 4スレッド 5ms 39ms 389ms
  24. 24. JUSTSYSTEMSJUSTSYSTEMS • PostgreSQL9.6以降ではParallel Queryをサポート (参考)Parallel Query 24 • データが大きいと却って遅くなる • 最新版では改善されている可能性あり 並行数 100万 1000万 1億 1並行 251ms 3561ms 39751ms 2並行 165ms 1575ms 54286ms 3並行 144ms 1118ms 112000ms 4並行 158ms 979ms 112000ms
  25. 25. JUSTSYSTEMSJUSTSYSTEMS select c1, sum(c0) from table group by c1; 25 グループ化 Map<String, Long> map = new HashMap<>() for (i = 0; i < c0.length; i++) { long sum = map.getOrDefault(c1[i], Long.Zero)+c0[i]; map.put(c1[1], sum); }
  26. 26. JUSTSYSTEMSJUSTSYSTEMS select c1, sum(c0) from table group by c1; • 取り込み時にID化 26 グループ化 性別 男性 男性 女性 男性 女性 性別 0 0 1 0 1 男性→0 女性→1
  27. 27. JUSTSYSTEMSJUSTSYSTEMS select c1, sum(c0) from table group by c1; • primitive型を利用したコレクションフレームワークが利 用可能 27 グループ化 Int2LongOpenHashMap map = new Int2LongOpenHashMap() for (i = 0; i < c0.length; i++) { map.addValue(c1[i], c0[i]); }
  28. 28. JUSTSYSTEMSJUSTSYSTEMS • ID化が威力を発揮するのは複数カラムのグルーピング select c1.,c2,...,cn, sum(c0) from table group by c1.,c2,...,cn; 28 グループ化 Object2LongOpenCustomHashMap<String[]> map = new Object2LongOpenCustomHashMap<>(...); for (int i = 0; i < c0.length; i++) { String[] id = new String[n]; for (int j = 1; j <= n; j++) { id[j] = c[j][i]; } map.addTo(id, c0[i]); }
  29. 29. JUSTSYSTEMSJUSTSYSTEMS • ID化により複数カラムを一つのprimitive型に押し込める select c1.,c2,...,cn, sum(c0) from table group by c1.,c2,...,cn; 29 グループ化 Int2LongOpenHashMap map = new Int2LongOpenHashMap<>(); for (int i = 0; i < c0.length; i++) { int id = 0; for (int j = 1; j <= n; j++) { id <<= SHIFT_LENGTH; id += c[j][i]; } map.addTo(id, c0[i]); }
  30. 30. JUSTSYSTEMSJUSTSYSTEMS 30 グループ化の性能比較 • 行数1000万行 グループ化 カラム数 1 2 3 4 String[] 90ms 197ms 307ms 471ms int 28ms 74ms 80ms 112ms カラム数が増えるほど高速化
  31. 31. JUSTSYSTEMSJUSTSYSTEMS • String[] を使った絞り込み select c1, sum(c0) from table group by c1 where (year,month,day) in (('2018','05','26'),('2018','05','27')); 31 絞り込み Set<String[]> where = new OpenObjectCustomHashSet<>() /* where に日付に対応したオブジェクトを入れる */ for (int i = 0; i < c0.length; i++) { String[] id = new String[n]; for (int j = 1; j <= n; j++) { id[j] = c[j][i]; } if (!where.contains(id)) { continue; }}
  32. 32. JUSTSYSTEMSJUSTSYSTEMS • ID化により複数カラムを一つのprimitive型に押し込める select c1, sum(c0) from table group by c1 where (year,month,day) in (('2018','05','26'),('2018','05','27')); 32 絞り込み Set<Integer> where = new IntOpenHashSet<>(); /* where に日付に対応したIDを入れる */ for (int i = 0; i < c0.length; i++) { int id = 0; for (int j = 1; j <= n; j++) { id <<= SHIFT_LENGTH; id += c[j][i]; } if (!where.contains(id)) { continue; }}
  33. 33. JUSTSYSTEMSJUSTSYSTEMS 33 絞り込みの性能比較 • 行数1000万行 絞り込み カラム数 1 2 3 4 String[] 46ms 94ms 101ms 138ms int 28ms 26ms 23ms 26ms カラム数が増えても性能劣化が少ない
  34. 34. JUSTSYSTEMSJUSTSYSTEMS 性能測定のTips • 性能測定では指数的なリソース増加がおすすめ – 10万, 100万, 1000万, 1億 – 10万, 20万, 40万, 80万, 160万, 320万 – 10万, 20万, 50万, 100万, 200万, 500万, 1000万 • 線形的な増加 – 10万,20万,30万,40万,50万,60万,70万,80万,90万,100万 34 • 指数的に増やすと傾向・問題が見つけやすい • 両対数グラフでみるため
  35. 35. JUSTSYSTEMSJUSTSYSTEMS BIツール全体の性能(OLAP+IMSQL) 35 集計行数 応答[ms] 10万 91 20万 91 50万 118 100万 120 200万 193 500万 259 1000万 387 2000万 638 5000万 1272 1億 2205 Actionist!のある集計表の 応答速度
  36. 36. JUSTSYSTEMSJUSTSYSTEMS BIツール全体の性能(OLAP+IMSQL) 36 0 500 1000 1500 2000 2500 0 20000000 40000000 60000000 80000000 100000000 時間[ms] 行数
  37. 37. JUSTSYSTEMSJUSTSYSTEMS BIツール全体の性能(OLAP+IMSQL) 37 10 100 1000 10000 100000 1000000 10000000 100000000 時間 行数 両対数
  38. 38. JUSTSYSTEMSJUSTSYSTEMS BIツール全体の性能(OLAP+IMSQL) 38 10 100 1000 10000 100000 1000000 10000000 100000000 時間[ms] 行数 両対数 OLAPエンジンの性能
  39. 39. JUSTSYSTEMSJUSTSYSTEMS BIツール全体の性能(仮説) 39 • IMSQLは行数に比例、OLAPは行数によらず一定では? – (集計時間)=(行数)/α+βで表せる? – α=45,000, β=100 行数 応答[ms] 仮説[ms] (行数)/45000 100 10万 91 102 2 100 20万 91 104 4 100 50万 118 111 11 100 100万 120 122 22 100 200万 193 144 44 100 500万 259 211 111 100 1000万 387 322 222 100 2000万 638 544 444 100 5000万 1272 1211 1111 100 1億 2205 2322 2222 100
  40. 40. JUSTSYSTEMSJUSTSYSTEMS 仮説検証(集計時間=行数/45,000+100) 40 10 100 1000 10000 100000 1000000 10000000 100000000 時間[ms] 行数 両対数
  41. 41. JUSTSYSTEMSJUSTSYSTEMS まとめ: 高速化のコツ 1. primitive型あるいはprimitive型を利用したコレクション フレームワークを使う • メモリー使用量も大きく削減 • (参考) JJUG 2017 fall Javaが「書ける」から「できる」になれる!メモリ節約ノウ ハウ話 ジャストシステム 猪鼻哲也 https://www.slideshare.net/JSUXDesign/java-82338809 2. 測る(Measure Don't Guess) • ボトルネックを特定 • どのぐらい速くなったか観測 • 当てずっぽうのチューニングは効かない事が多い... 3. 自動化されたテスト • 大胆に手を加えられるようになる 41
  42. 42. JUSTSYSTEMSJUSTSYSTEMS さらに詳しく知りたい方へ • ぜひこちらに応募してください! https://www.justsystems.com/jp/employ/ 42

×