Skip to content
bonybeat edited this page Aug 16, 2014 · 20 revisions

これは、OrientDBを利用してあなたのアプリケーションを最適化するためのティップスです。 データベースのタイプ毎のガイドへのリンクも掲載しています。必要に応じて、そちらを参照して下さい。

コンフィギュレーション 設定

OrientDBの設定全般はこちらを参照して下さい。

メモリー設定

サーバーとEmbeddedセッティング

以下の設定は、通常のサーバー設定と、OrientDBをEmbeddedモードで動かす場合の両方に有効です。

チューニングで一番大事なのはメモリー設定を正確に行うことです。ヒープとメモリーマッピングによる仮想メモリーのバランスを正しくすると、まったく違った結果が得られます。特に、メガバイト、テラバイト、さらにそれ以上の大規模データベースでは、すべてがインメモリーにはなりませんから。

例えば、Javaプロセスに最大8GBを割り当てる場合、ヒープに小さめに、ディスクキャッシュバッファ(つまりオフヒープメモリー)に大きめにするほうが普通です。

    java -Xmx8g ...

よりは、こんな風にしたほうが良いでしょう。

    java -Xmx800m -Dstorage.diskCache.bufferSize=7200 ...

storage.diskCache.bufferSize で、MB単位で、どれだけ ディスクキャッシュ を割り当てるか指定します。デフォルトでは4GBです。

_注意: ヒープの最大値とディスクキャッシュバッファの合計が大きくしすぎると、OSでスワップが発生して極端に遅くなってしまいます。

JVM セッティング

JVMセッティングは、 server.sh (またはserver.bat)に記載します。HWやその他のSWに応じて設定するのですが、多くの場合、以下の設定でうまくいくでしょう。

    -server -XX:+AggressiveOpts -XX:CompileThreshold=200

同時更新が大量に発生する場合

OrientDBは、楽観的排他処理によって並列処理を制御しますが、少量のデータに対して、同時更新が大量に発生する場合は、リトライが多くなりすぎるのを避けるために、レコードロックするのが効果的な場合もあります。 そんな場合は、ストレージAPIを利用して、アクセスを同期化することもできます(これは、remotoデータベースでは利用できません)。

((OStorageEmbedded)db.getStorage()).acquireWriteLock(final ORID iRid)
((OStorageEmbedded)db.getStorage()).acquireSharedLock(final ORID iRid)
((OStorageEmbedded)db.getStorage()).releaseWriteLock(final ORID iRid)
((OStorageEmbedded)db.getStorage()).releaseSharedLock(final ORID iRid)

Writerスレッドの場合

try{
  ((OStorageEmbedded)db.getStorage()).acquireWriteLock(record.getIdentity());

  // DO SOMETHING
} finally {
  ((OStorageEmbedded)db.getStorage()).releaseWriteLock(record.getIdentity());
}

Readerスレッドの場合

try{
  ((OStorageEmbedded)db.getStorage()).acquireSharedLock(record.getIdentity());
  // DO SOMETHING

} finally {
  ((OStorageEmbedded)db.getStorage()).releaseSharedLock(record.getIdentity());
}

Remote 接続

remoteによってデータベースにアクセス場合のパフォーマンス改善は多くの方法があります。

フェッチ戦略

remoteデータベースを利用する場合は、フェッチ戦略 に注意して下さい。 デフォルトでは、OrientDBクライアントは結果セットの中にレコードだけをロードします。 例えば、クエリーが100件の結果を返す場合、これらの結果をクライアントから収集し、 一つまたは複数のネットワークから遅延ロードします。

フェッチプランを特定することで、OrientDBに、クライアントアプリケーションがアクセスする要素を事前フェッチするように指示することもできます。 フェッチプランを正しく指定すると、_単一のネットワークからだけ_結果セットを受け取ることができます。

より詳細はフェッチ戦略を参照して下さい。

ネットワーク接続プール

デフォルトで、各クライアントはサーバーと接続するのに、一つのネットワークだけを利用します。同一クライアントでマルチスレッドは、一つのネットワーク接続プールを共有します。

マルチスレッドを利用する場合、ネットワーク接続待ちが長くなって、ボトルネックになる場合があります。このため、ネットワーク接続プールの設定は重要です。

設定は非常に簡単で、パラメータは二つだけです。

  • minPool、コネクションプールの初期サイズです。デフォルト値は、グローバルパラメータ"client.channel.minPool" (parametersを参照)に設定されています。
  • maxPool、コネクションプールの最大値です。デフォルト値は、グローバルパラメータ"client.channel.maxPool" (parametersを参照)に設定されています。

最初の接続時に、minPoolの設定された分まで、サーバー接続が作成されます。また、稼働中にコネクションプールの空きがなくなると、maxPoolの値まで追加されます。

全コネクションプールがビジーなると、クライアントは待たされます。

データベースプロパティを使ってコネクションプールの設定を行う

    database = new ODatabaseDocumentTx("remote:localhost/demo");
    database.setProperty("minPool", 2);
    database.setProperty("maxPool", 5);
    
    database.open("admin", "admin");

タイムアウトの拡張

以下のようなメッセージが大量に出力されることがあります。

WARNING: Connection re-acquired transparently after XXXms and Y retries: no errors will be thrown at application level 

これは、デフォルトタイムアウト値が小さすぎて、サーバーサイドで処理が完了するのにもっと時間が必要になっている時です。 タイムアウト設定をもっと大きくして下さい。

パラメータは以下の通りです:

  • network.lockTimeout、ネットワークチャネルのタイムアウト値。単位はミリ秒、デフォルトは15秒です。
  • network.socketTimeout、TCP/IPソケットのタイムアウト値。単位はミリ秒、デフォルトは10秒。

クエリー

インデックスを利用する

クエリーの高速化の最初の一歩はWHERE条件に指定している項目に対して、インデックスを作成することです。

    SELECT FROM Profile WHERE name = 'Jay' 

'name'プロパティにインデックスを作成してみます。

    CREATE INDEX profile.name UNIQUE

値がユニークでなければ、NOTUNIQUEを使用して下さい。

より複雑のクエリーを例にします。

    select * from testClass where prop1 = ? and prop2 = ?
    CREATE INDEX compositeIndex ON testClass (prop1, prop2) UNIQUE

or via Java API:

    oClass.createIndex("compositeIndex", OClass.INDEX_TYPE.UNIQUE, "prop1", "prop2");

さらに、部分一致検索でもインデックスは利用可能です。

    select * from testClass where prop1 = ?

更にクエリーの最適化を学ぶためのテストはこちらにあります。

http://code.google.com/p/orient/source/browse/trunk/tests/src/test/java/com/orientechnologies/orient/test/database/auto/SQLSelectIndexReuseTest.java

グラフを正しく利用する

OrientDBはグラフデータベースでもあります。これはトラバースを利用してクエリーを最適化できるということです。一般的なテクニックはPivotingを参照して下さい。

Massive Insertion(大量INSERT)

Massive Insert intentをする

intentはOrientDBに対して、こうしたいという意図を伝えるものです。Massive Insert intentは、OrientDBに対して大量のINSERTを行いたいということと伝えます。 するとOrientDBは、自動コンフィギュレーションによって、最高のパフォーマンスを出します。 intentを取り除くためには、それにNULLをセットします。

    db.declareIntent( new OIntentMassiveInsert() );
    
    // YOUR MASSIVE INSERTION
    
    db.declareIntent( null );

ジャーナル(WAL)を無効にする

大量INSERTを行う場合は、ジャーナル(WAL)を無効にすると、性能を改善できます。

-storage.useWAL=false

デフォルトでは、WAL (先行書き込みいログ)は有効です。

Massive Updates(大量アップデート)

アップデートは(その前後でデータ長が一致することはまれなので)、ストレージレベルで"holes"を発生させます。"holes"はデータの空き領域であり、再利用されますが、大量の小さな"holes"は、再利用が難しいので、望ましくありません。データベースが大規模になるとパフォーマンス劣化の要因になります。

Oversize オーバーサイズ

もし、ある種のレコードがアップデートされることがあらかじめ判っているなら、クラスの生成時にオーバーサイズ(Oversize)を2かそれ以上にセットしておきましょう。 デフォルトは0です。

デフォルトでは、OGraphVertexクラスのオーバーサイズ(Oversize)は2になっています。クラスを定義する場合、2以上をセットしましょう。

[translation here]

OClass myClass = getMetadata().getSchema().createClass("Car"); myClass.setOverSize(2);

賢くトランザクションを利用する

OrientDBで、リニアなパフォーマンスを得るためには、できるかぎりトランザクションの利用を避けるべきです。 実際、OrientDBでは、トランザクションにおいて、コミットをフラッシュするまで、すべての変更はインメモリーにとどまります。 そのためヒープと、MAPとして実装されているローカルトランザクションキャッシュがボトルネックになる場合があります。

"remote"コネクションを利用しているのでなければ、トランザクションを利用すると、大量インサートが遅くなります。 クライアントサーバー接続だと、すべてのINSERTがコミット時にだけ発生するので高速になります。

トランザクションログを無効にする

If you need to group operations to speed up remote execution in a logical transaction but renouncing to the Transaction Log, just disable it by setting the property tx.useLog to false.

Via JVM configuration:

    java ... -Dtx.useLog=false ...

or via API:

    OGlobalConfiguration.TX_USE_LOG.setValue(false);

*注意: トランザクションログを無効にすると、トランザクション中にJVMがクラッシュした場合にロールバックできなくなります。

データベースを小さく保つ

より小さなデータベースは、大きなデータベースより、キャッシュメモリーにのりやすく、ファイルシステムのシークやデータのロードも高速です。例えば、以下のようなことを考慮して、無駄にデータベースを大きくs内容にしましょう。

フィールド名は小さく

OrientDBはスキーマレスで利用できます。これは各データ値と一緒に、フィールド名も格納されるということです。 だから、フィールド名を"outVertices"ではなく "out"にすると、各レコード毎に8バイトが節約できます。 レコードが100万件レベルになると、数メガバイトが節約できることになります。

<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script> <script> $(function() { $("*").contents().filter(function() { return this.nodeType==8 && this.nodeValue.match(/^original/); }).each(function(i, e) { var tooltips = e.nodeValue.replace(/^original *[\n\r]|[\n\r]$/g, ''); $(this).prev().attr('title', tooltips); }); }); </script>
Clone this wiki locally