インメモリーで超高速処理を実現する場合のカギ

30
ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved. Proprietary & Confidential Powered by Apache GEODE Meetup Tokyo #2 インメモリーで超高速処理を実現する場合のカギ 2016/10/11 ウルシステムズ株式会社 http://www.ulsystems.co.jp mailto:[email protected] Tel: 03-6220-1420 Fax: 03-6220-1402

Upload: masaki-yamakawa

Post on 12-Jan-2017

277 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by

Apache GEODE Meetup Tokyo #2

インメモリーで超高速処理を実現する場合のカギ

2016/10/11

ウルシステムズ株式会社 http://www.ulsystems.co.jp

mailto:[email protected]

Tel: 03-6220-1420 Fax: 03-6220-1402

Page 2: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 1

About Me

山河 征紀

Business

Private

• GEODE歴:9年(Since 2008) • GEODEバグ報告数:nnn 件

• マラソン • 横浜マラソン2016:4h17m • 目標は今年度中のサブ4

• 登山 • 目標はココ

Page 3: インメモリーで超高速処理を実現する場合のカギ

ULS 2 Copyright © 2011-2013 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by

インメモリーで 超高速処理を実現する場合のカギ

Page 4: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 3

処理に応じてデータの分散を繰り返す

Apache Geodeでの大量データ処理

Geodeでもバッチ処理はとかもやらないことはありませ

これである必要はない

→普通のリアルタイム処理にする?

どちらにせよ、たくさんメモリーに乗せる、ってところが

ポイント

処理A 処理B 処理C

処理A 処理B 処理C

処理A 処理B 処理C

全体的にいまいちなので後で見直す

ひとまず「処理」を小さくしてみるか?

分散、のところを強調した方が良い

パーティション リージョン

パーティション リージョン

パーティション リージョン

パーティション リージョン

どうしてもメモリーにのらないデータはDiskやRDBへ保持

高速に処理を行うためには、

たくさんのオブジェクトをメモリーへのせることが重要!

Page 5: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 4

リージョンのおさらい

リージョンとは分散するデータを保持する入れ物のこと

データの特性によってレプリケーションとパーティションを使い分ける

パーティション レプリケーション

すべてのマシンで同一のデータを保持する

何れかのマシンにデータが存在する

分散するデータを保持する入れ物のこと、ってところの日本語がおかしい

今回の例で言うと

処理への入出力データは「パーティションリージョン」 マスターデータは「レプリケーションリージョン」

Page 6: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 5

データは、Javaのヒープメモリ上にJavaのオブジェクトとして管理される

Geodeにおけるデータ管理

JVM

Cache

Region Key Value

ABC

GGG

XYG

Region

Region

• インメモリデータへのエントリポイント • アプリケーションはキャッシュを介して、データへの一連の操作を行う • 任意のリージョンにより構成

• 論理的なデータのグループ(RDBにおけるテーブルのイメージ) • Key-Value形式でのデータ管理 • 任意のオブジェクトに対して、一意のKeyを指定 • スキーマの概念はなく、任意のオブジェクトを格納

Cache

Region

public class Employee { private int employeeNo; private String firstName; private String lastName; private String deptCode; ・・・ ・・・ }

Page 7: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 6

1. Javaオブジェクトのメモリー使用量

2. 通信レイテンシー

今日、お話したいこと

処理A 処理B 処理C

処理A 処理B 処理C

処理A 処理B 処理C

パーティション リージョン

パーティション リージョン

パーティション リージョン

パーティション リージョン

Page 8: インメモリーで超高速処理を実現する場合のカギ

ULS 7 Copyright © 2011-2013 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by

Javaオブジェクトのメモリー使用量

Page 9: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 8

Apache Geodeのメモリー使用量

1億件のオブジェクトをデータ分散してみると…

public class ObjectPerson implements Serializable { private Integer id;

private String firstName; private String lastName; private Integer age; private Address address; private List<String[]> phoneNumbers; private List<String> emails;

private Date createdAt; private Date updatedAt; …(setter/getter略)…

JVM

サーバ

JVM

サーバ

JVM

サーバ

JVM

サーバ

1億件 データ分散

(パーティション)

コメントでどのようなデータかと桁数を明記

クラスが見ずらいので他を参考に見やすくする

パーティションで、ってのが足りない

1オブジェクトのサイズ

200byte (感覚値)

全体サイズ 20GB

(200byte×1億件)

1Nodeあたりメモリ使用量

5GB (20GB÷4Node)

見積値 (感覚)

Page 10: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 9

Apache Geodeのメモリー使用量

予想を大幅に超えるメモリー使用量!

0.0 GB

20.0 GB

40.0 GB

60.0 GB

80.0 GB

100.0 GB

120.0 GB

140.0 GB

160.0 GB

180.0 GB

見積値 実測値

Server1 Server2 Server3 Server4

見積値の 約9倍!

44.2 GB

45.7 GB

44.5 GB

45.5 GB

179.9 GB

20.0 GB

Page 11: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 10

なぜJavaオブジェクトが肥大化するのか

Javaでは、フィールドが何もないオブジェクトを生成するだけでも、メモリーを消費してしまう public class EmptyObject { }

public class NullSetObject { private String str01 = null; (略)

private String str50 = null; private Integer int01 = null; (略)

private Integer int50 = null; }

String, Integerそれぞれ50個あり、すべてnullを設定したオブジェクト

フィールドがひとつもない オブジェクト

それぞれ100万オブジェクト生成し、生成にかかった時間とメモリー使用量を測定した結果は…

EmptyObject NullSetObject

生成時間 17 ms 382 ms

メモリー使用量 33.5 MB 430.2 MB

22倍も遅い

13倍も多い

Page 12: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 11

Stringのために88Byte必要

int配列4Byteのために32Byte必要

なぜJavaオブジェクトが肥大化するのか

オブジェクトを1つ生成する毎に、標準のメタデータが必要! メタデータ:クラスポインター、状態フラグ、同期情報フラグなど

標準のメタデータ int

0 24 28 [Byte]

標準のメタデータ サイズ int

0 24 32 [Byte] 36

標準のメタデータ Obj固有のメタデータ ポインタ char char 標準のメタデータ サイズ char ・・・

0 56 24 48 0 38 24 32

int用の4Byteのために24Byte必要

数値をIntegerで保持する場合

数値配列をInteger配列で保持する場合

Stringの場合

String間違っているので正確に記

Page 13: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 12

jmapによりオブジェクト数を確認してみると…

num #instances #bytes class name ---------------------------------------------- 1: 28000397 896018224 [Ljava.lang.String; 2: 14000714 812049584 [Ljava.lang.Object; 3: 28000000 672000000 java.util.Date 4: 14000012 336000288 java.util.ArrayList 5: 7000000 336000000 geodemeetup2.object.optimize.person.ObjectPerson 6: 14000128 224002048 java.lang.Integer 7: 7000000 224000000 geodemeetup2.object.optimize.person.ObjectAddress 8: 7000001 168001040 [Ljava.lang.Integer; 9: 5254 616936 [C 10: 575 126992 [B 11: 5171 124104 java.lang.String 12: 614 69544 java.lang.Class 13: 879 35160 java.util.LinkedHashMap$Entry 14: 785 31400 java.util.TreeMap$Entry 15: 305 19520 java.net.URL 16: 529 16928 java.util.HashMap$Node 17: 130 12480 java.util.jar.JarFile$JarFileEntry 18: 24 11584 [Ljava.util.HashMap$Node; 19: 138 11040 [Ljava.util.WeakHashMap$Entry; 20: 271 10840 java.lang.ref.Finalizer 21: 135 8640 java.util.jar.JarFile … Total 119019230 3669289272

Dateだけで670MB!

Integerと配列を合わせて390MB!

Page 14: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 13

オブジェクトではなくプリミティブ型を使用

プリミティブ型を使用すれば、このオーバーヘッドは削減できる

public class ObjectPerson implements Serializable { private Integer id;

private String firstName; private String lastName; private Integer age; private Address address; private List<String[]> phoneNumbers; private List<String> emails;

private Date createdAt; private Date updatedAt; …(setter/getter略)…

private int id;

private int age;

private long createdAt; private long updatedAt;

日付はlongで表せるので

Page 15: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 14

プリミティブ型利用によりメモリー使用量削減

num #instances #bytes class name ---------------------------------------------- 1: 28000195 896008088 [Ljava.lang.String; 2: 14000519 812030088 [Ljava.lang.Object; 3: 7000000 392000000 geodemeetup2.object.optimize.person.PrimitiveObjectPerson 4: 14000007 336000168 java.util.ArrayList 5: 7000000 280000000 geodemeetup2.object.optimize.person.PrimitiveObjectAddress 6: 7000101 168003768 [I 7: 2639 402240 [C 8: 2557 61368 java.lang.String 9: 491 55976 java.lang.Class 10: 785 31400 java.util.TreeMap$Entry 11: 9 25008 [B 12: 283 11320 java.util.LinkedHashMap$Entry 13: 158 10112 java.net.URL 14: 283 6792 java.io.ExpiringCache$Entry 15: 69 4968 java.lang.reflect.Field 16: 256 4096 java.lang.Integer 17: 10 3760 java.lang.Thread … Total 77009353 2884700216

Date、Integerは出現しなくなった

オブジェクト数、 バイト数ともに削減 (約780MB) Total 119019230 3669289272

before

Page 16: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 15

オブジェクト内のすべてのフィールドを1つのバイト配列として保持!

通常のオブジェクト

究極のオーバーヘッド削減コンセプト

Person

String firstName

String lastName

Integer age

Address address

Integer[] phoneNumber

Taro

Yamada

32

03 1234 5678

Taro

Yamada

32

03 1234 5678

YY-ZZ ABCマンションNNN 2 1 2 神奈川県横浜市XX区YY-ZZ ABCマンションNNN

1つのバイト配列としたオブジェクト

列挙型 共有オブジェクト

Person

byte[] data Taro Yamada 32 03 1234 5678

String[] prefectureList 東京都,神奈川県,埼玉県,…

String[] cityList 横浜市,川崎市,…

YY-ZZ ABCマンションNNN 2 1 2

1つのbyte配列として保持することで

オーバーヘッドを0へ

リスト型データの場合、内部的にリスト番号で保持することにより さらにデータを削減

Page 17: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 16

バイト配列オブジェクトのメモリー使用量

Javaのオーバーヘッドを大幅に削減!

45.5 GB

8.4 GB

44.5 GB

8.4 GB

45.7 GB

8.4 GB

44.2 GB

8.7 GB

0.0 GB

20.0 GB

40.0 GB

60.0 GB

80.0 GB

100.0 GB

120.0 GB

140.0 GB

160.0 GB

180.0 GB

実測値(オブジェクト) 実測値(バイト配列)

Server1 Server2 Server3 Server4

約81% 削減

179.9 GB

33.9 GB

Page 18: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 17

加えて、GC処理の発生を大幅に低減

メモリー使用量の削減のみならず、GCへの影響を極小化してパフォーマンスも向上!

22.8 sec 12.2 sec

22.9 sec

12.1 sec

22.8 sec

12.1 sec

22.8 sec

12.0 sec

0.0 sec

10.0 sec

20.0 sec

30.0 sec

40.0 sec

50.0 sec

60.0 sec

70.0 sec

80.0 sec

90.0 sec

100.0 sec

オブジェクト バイト配列

GC時間

Server1 Server2 Server3 Server4

オブジェクト数が少ないためGC時間も少ない

91.2 sec

48.3 sec

Page 19: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 18

バイト配列オブジェクトの実装

オブジェクト内部のデータはバイト配列のみ

public class ObjectPerson implements Serializable { private Integer id; private String firstName; …

public class BinaryPerson implements Externalizable { private byte[] data = new byte[150];

通常のオブジェクトの場合 バイト配列オブジェクトの場合

Page 20: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 19

バイト配列オブジェクトの実装

アクセサ(setter,getter)呼び出し時にバイト配列を検索

public void setId(Integer id) { this.id = id; } public Integer getId() { return id; }

public void setId(Integer id) { int offset = 0; int val = id.intValue(); data[offset] = (byte) (val >>> 24); data[offset + 1] = (byte) (val >>> 16); data[offset + 2] = (byte) (val >>> 8); data[offset + 3] = (byte) (val); } public Integer getId() { int offset = 0; int val = 0; for (int i = 0; i < 4; i++) val = (val << 8) + (data[offset + i] & 0xff); return Integer.valueOf(val); }

通常のオブジェクトの場合 バイト配列オブジェクトの場合

Page 21: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 20

バイト配列オブジェクトの実装

アクセサ(setter,getter)呼び出し時にバイト配列を検索

public void setFirstName( String firstName) { this.firstName = firstName; } public String getFirstName() { return firstName; }

public void setFirstName(String firstName) { int offset = 22; String val = firstName; byte[] bytes = val.getBytes(); int newLen = data.length + 2 + bytes.length; byte[] newBytes = new byte[newLen]; System.arraycopy(data, 0, newBytes, 0, data.length); data = newBytes; data[offset] = (byte) (bytes.length >>> 8); data[offset + 1] = (byte) (bytes.length); System.arraycopy(data, offset + 2, bytes, 0, bytes.length); } public String getFirstName() { int offset = 22; int len = 0; for (int i = 0; i < 2; i++) len = (len << 8) + (data[offset + i] & 0xff); byte[] byteString = new byte[(short) len]; System.arraycopy(data, offset + 2, byteString, 0, len); return new String(byteString); }

通常のオブジェクトの場合 バイト配列オブジェクトの場合

Page 22: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 21

バイト配列オブジェクトのjmap

num #instances #bytes class name ---------------------------------------------- 1: 7000572 1568125136 [B 2: 7000000 112000000 geodemeetup2.object.optimize.person.BinaryPerson 3: 714 28049584 [Ljava.lang.Object; 4: 5264 617488 [C 5: 5181 124344 java.lang.String 6: 616 69752 java.lang.Class 7: 879 35160 java.util.LinkedHashMap$Entry 8: 785 31400 java.util.TreeMap$Entry 9: 305 19520 java.net.URL 10: 397 18224 [Ljava.lang.String; 11: 529 16928 java.util.HashMap$Node 12: 130 12480 java.util.jar.JarFile$JarFileEntry 13: 24 11584 [Ljava.util.HashMap$Node; 14: 138 11040 [Ljava.util.WeakHashMap$Entry; 15: 269 10760 java.lang.ref.Finalizer 16: 135 8640 java.util.jar.JarFile … Total 14019353 1709288312

ほぼバイト配列のみ!

もとのオブジェクトから約2GB削減!

Total 119019230 3669289272

before

Page 23: インメモリーで超高速処理を実現する場合のカギ

ULS 22 Copyright © 2011-2013 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by

通信レイテンシー

Page 24: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 23

Apache GeodeではPut等のデータ操作処理においてサーバを跨いだ通信が発生

データ分散による通信

サーバ#1

オブジェクト

シリアライズ処理

バイト配列

サーバ#2

オブジェクト

デシリアライズ処理

バイト配列

通信

Put等

Event Latency

CPUキャッシュアクセス(L2) 3 ns

メモリーアクセス 120 ns

SSDディスク I/O 150,000 ns

同一データセンター内通信 500,000 ns

ディスク I/O 10,000,000 ns

通信は、メモリーで完結する処理に比べ、

4000倍以上も開きがある

Page 25: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 24

高速処理を行うためには、次の2つが重要!

高速処理を実現する上で大事なこと

パフォーマンス シリアライズ 処理時間

通信時間 = +

高速シリアライザー 通信プロトコル

と コンパクトなデータ

Page 26: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 25

シリアライズ処理とは

メモリー上に存在する情報を、ネットワークを超えた別のJVMでも、同じデータとして扱えるようにする形式へ変換すること

メモリー(Javaヒープ)

T a r o 3 2

Y a m a d a

t a r o . y a m a d

a @ e x a m p l e . c o m

Person String firstName Taro

String lastName Yamada

Integer age 32

String Email [email protected]

<Person> <firstName>Taro</firstName> <lastName>Yamada</lastName> <age>32</age> </Person>

{ "firstName" : "Taro", "lastName" : "Yamada", "age" : 32 }

XMLの場合 JSONの場合

TaroYamada32

バイナリーの場合

代表的なシリアライズ形式

Page 27: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 26

通信プロトコル

Geodeではアプリケーションの特性やクラスタ構成によって次のプロトコルが選択可能

通信箇所 通信プロトコル

クラスタ間の通信

TCP

(デフォルト) 信頼性が高いが低速

UDPユニキャスト

TCPより信頼性が低いが高速

※独自にデータの到達確認を行い信頼性を向上させている

UDPマルチキャスト 大規模クラスタで有効

クライアント-サーバ間の通信

TCP

図を適切に修正

クライアント

Page 28: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 27

シリアライズ/デシリアライズ

通信の際のデータのシリアライズ/デシリアライズ処理は選択することが可能

オブジェクトの更新部分が一定となる場合は差分のみのシリアライズを行うことで通信時のデータサイズをさらに圧縮することが出来る

シリアライズ方式 備考

Java標準の方式 (java.io.Serializable)

Java以外のクライアントと通信することが出来ない

Geode独自の高速シリアライズ方式

(com.gemstone.gemfire.DataSerializable) 処理速度は最速となる

PDX方式(Geode独自)

(Portable Data eXchange)

異なるバージョンのオブジェクトでも通信することが出来る

シリアライズ方式毎の処理速度の比較

Page 29: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 28

まとめ

Javaオブジェクトのメモリー使用量

–Javaのオーバーヘッドは予想以上に大きい

–プリミティブ型の方がメモリー使用量は少ない

–オブジェクト内部を1つのバイト配列で表現するのが究極

–オブジェクト数の削減はGC処理にも効果あり

通信レイテンシー

–高速処理を実現するためには以下の選択が重要

通信プロトコル

高速シリアライザー

通信データをコンパクトに

簡潔にする

Page 30: インメモリーで超高速処理を実現する場合のカギ

ULS Copyright © 2011-2016 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 29

宣伝:簡易にメモリー使用量の削減できる!

Memory Optimizer