中年engineerの独り言 - crumbjp

LinuxとApacheの憂鬱

MongoDB 2.4 の新クエリーcapped arrayを試した

capped arrayの機能と性能を検証をした。

結論

capped arrayでは配列数が多い場合、明らかにsortが性能劣化する
(使い難いが)sortを使わない場合は殆ど気にしなくて良いレベルになる。
LRUキャッシュに使うにはちょっと厳しいかもしれない。。

capped arrayの機能

ドキュメント内配列を容量固定にする。

>use testdb
>db.cappedArray.ensureIndex({key:1},{unique:1});
>db.cappedArray.save({
 key:10,
 value:[
  {s:10},
  {s:20},
  {s:30},
  {s:40},
  {s:50}
]});
>db.cappedArray.find({key:10});
{ "_id" : ObjectId("514fb518f63edd201d14595f"),
  "key":10,
  "value":[{"s":10},{"s":20 },{"s":30 },{"s":40},{"s":50} ] }
// ここまでは同じ。

// capped array クエリー
//  $each => {s:9999} , {s:0} レコードを追加する
//  $sort => sフィールドで正順ソート
//  $slice=> 最後5レコードだけ抽出して保存。
db.cappedArray.update({key:10},{
    $push: {
      value: {
          $each : [{s:9999},{s:0}],
          $sort : {s:1},
          $slice:-5
      }
    }
});
// 確認:valueフィールドに注目。今指定した{s:0}すら入らない。
{ "_id" : ObjectId("514fb518f63edd201d14595f"),
  "key":10,
  "value":[{"s":20 },{"s":30 },{"s":40},{"s":50},{"s":9999} ] }

やはり上記リンクのパフォーマンス用途でもある。

でもあまり配列が大きくなるようだと計算量的にどうなんだろう??

性能検証

プロファイルの取り方
目的のDBに移ってdb.setProfilingLevel(level)
0 : 無効
1 : slowsオペレーション(遅い奴)だけ
2 : 全部オペレーション
use testdb
db.setProfilingLevel(2)

プロファイルはdb.system.profile(capped)コレクションに溜まる。

capped array

capped array更新性能と配列数の関係を調べる。

capped arrayクエリー
db.cappedArray.update({key:99},{
    $push: {
      value: {
          $each : [{s:9999},{s:0}],
          $sort : {s:1},
          $slice:-5
      }
    }
});
更新対象ドキュメント
配列数=20
{ key:99,
  value:[
         {s:10, v:'11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'}
                 :<中略>
         ,{s:200,v:'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'}
         ] }
配列数=20のcapped arrayクエリーのプロファイル
ロック時間は0.56ms程度。
{
    "op" : "update",
    "ns" : "testdb.cappedArray",
    "query" : { "key" : 99 },
    "updateobj" : { "$push" : { "value" : { "$each" : [ { "s" : 9999 }, { "s" : 0 } ], "$sort" : { "s" : 1 }, "$slice" : -5 } } },
    "nscanned" : 1,
    "nupdated" : 1,
    "keyUpdates" : 0,
    "numYield" : 0,
    "lockStats" : {
        "timeLockedMicros" : {
            "r" : NumberLong(0),
            "w" : NumberLong(560) },
        "timeAcquiringMicros" : {
            "r" : NumberLong(0),
            "w" : NumberLong(7) } },
    "millis" : 0,
    "ts" : ISODate("2013-03-26T05:30:07.891Z"),
    "client" : "127.0.0.1",
    "allUsers" : [ { "user" : "crumb",
        "userSource" : "admin" } ],
    "user" : "crumb@admin" }
更新対象ドキュメント
配列数=100。配列を5倍にしてみる。ちなみにこのsaveのlock期間は596 = 0.596msだった。小さいドキュメントとあまり変わらない。
ドキュメントサイズが3.5kb程度で1ページに収まるので差が出ないのだろう。)
{  key:99,
   value:[
         {s:10, v:'11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'}
                 :<中略>
        ,{s:1000,v:'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'}
        ] }
配列数=100のcapped arrayクエリーのプロファイル
ロック時間は1.439ms。明らかに増えた。
上記の通りデータサイズがI/Oに与える影響は軽微のはずなので、ソート処理時間が影響したと思われる。
{
    "op" : "update",
    "ns" : "testdb.cappedArray",
    "query" : { "key" : 99 },
    "updateobj" : { "$push" : { "value" : { "$each" : [ { "s" : 9999 }, { "s" : 0 } ],"$sort" : { "s" : 1 },"$slice" : -5 } } },
    "nscanned" : 1,
    "nupdated" : 1,
    "keyUpdates" : 0,
    "numYield" : 0,
    "lockStats" : {
        "timeLockedMicros" : {
            "r" : NumberLong(0),
            "w" : NumberLong(1439) },
        "timeAcquiringMicros" : {
            "r" : NumberLong(0),
            "w" : NumberLong(8) } },
    "millis" : 1,
    "ts" : ISODate("2013-03-26T05:39:01.145Z"),
    "client" : "127.0.0.1",
    "allUsers" : [ {
        "user" : "crumb",
        "userSource" : "admin" } ],
    "user" : "crumb@admin" }
配列数=100のcapped arrayクエリーでsortをしなかった場合のプロファイル
予想通り高速になった。
{
    "op" : "update",
    "ns" : "testdb.cappedArray",
    "query" : { "key" : 99 },
    "updateobj" : { "$push" : { "value" : { "$each" : [ { "s" : 9999 }, { "s" : 0 } ], "$slice" : -5 } } },
    "nscanned" : 1,
    "nupdated" : 1,
    "keyUpdates" : 0,
    "numYield" : 0,
    "lockStats" : {
        "timeLockedMicros" : {
            "r" : NumberLong(0),
            "w" : NumberLong(369) },
        "timeAcquiringMicros" : {
            "r" : NumberLong(0),
            "w" : NumberLong(9) } },
    "millis" : 0,
    "ts" : ISODate("2013-03-26T06:02:56.837Z"),
    "client" : "127.0.0.1",
    "allUsers" : [ {
        "user" : "crumb",
        "userSource" : "admin" } ],
    "user" : "crumb@admin" }