web-dev-qa-db-ja.com

SQLite Performance Benchmark-なぜ:memory:は非常に遅い...ディスクの1.5倍の速さですか?

Sqliteで:m​​emory:が遅いのはなぜですか?

インメモリsqliteとディスクベースのsqliteを使用することでパフォーマンスが向上するかどうかを確認しようとしました。基本的に、アプリケーションの実行中にディスクをヒットするnotを実行する非常に高速なクエリを取得するために、起動時間とメモリを交換したいと思います。

ただし、次のベンチマークでは、速度が1.5倍しか向上していません。ここでは、1M行のランダムデータを生成し、同じテーブルのディスクベースとメモリベースの両方のバージョンにロードしています。次に、両方のデータベースでランダムクエリを実行し、約300kのサイズのセットを返します。メモリベースのバージョンはかなり高速になると予想していましたが、前述のように1.5倍の高速化しか得られません。

他のいくつかのサイズのデータ​​ベースとクエリセットを試しました。 :memory:doesの利点は、dbの行数が増えると上がるようです。いくつかの仮説がありましたが、なぜ利点がそれほど小さいのかわかりません。

  • 使用されるテーブルは、:memory:が大きな勝者となるほど(行で)大きくない
  • より多くの結合/テーブルは、:memory:の利点をより明確にします
  • 接続またはOSレベルで何らかのキャッシュが行われているため、以前の結果に何らかの方法でアクセスでき、ベンチマークが破損している
  • 表示されていない何らかの種類の隠しディスクアクセスがあります(まだlsofを試していませんが、ジャーナリングのPRAGMAをオフにしました)

ここで何か間違ったことをしていますか?なぜ:memory:がほとんど瞬時のルックアップを生成しないのかについての考えはありますか?ベンチマークは次のとおりです。

==> sqlite_memory_vs_disk_benchmark.py <==

#!/usr/bin/env python
"""Attempt to see whether :memory: offers significant performance benefits.

"""
import os
import time
import sqlite3
import numpy as np

def load_mat(conn,mat):
    c = conn.cursor()

    #Try to avoid hitting disk, trading safety for speed.
    #http://stackoverflow.com/questions/304393
    c.execute('PRAGMA temp_store=MEMORY;')
    c.execute('PRAGMA journal_mode=MEMORY;')

    # Make a demo table
    c.execute('create table if not exists demo (id1 int, id2 int, val real);')
    c.execute('create index id1_index on demo (id1);')
    c.execute('create index id2_index on demo (id2);')
    for row in mat:
        c.execute('insert into demo values(?,?,?);', (row[0],row[1],row[2]))
    conn.commit()

def querytime(conn,query):
    start = time.time()
    foo = conn.execute(query).fetchall()
    diff = time.time() - start
    return diff

#1) Build some fake data with 3 columns: int, int, float
nn   = 1000000 #numrows
cmax = 700    #num uniques in 1st col
gmax = 5000   #num uniques in 2nd col

mat = np.zeros((nn,3),dtype='object')
mat[:,0] = np.random.randint(0,cmax,nn)
mat[:,1] = np.random.randint(0,gmax,nn)
mat[:,2] = np.random.uniform(0,1,nn)

#2) Load it into both dbs & build indices
try: os.unlink('foo.sqlite')
except OSError: pass

conn_mem = sqlite3.connect(":memory:")
conn_disk = sqlite3.connect('foo.sqlite')
load_mat(conn_mem,mat)
load_mat(conn_disk,mat)
del mat

#3) Execute a series of random queries and see how long it takes each of these
numqs = 10
numqrows = 300000 #max number of ids of each kind
results = np.zeros((numqs,3))
for qq in range(numqs):
    qsize = np.random.randint(1,numqrows,1)
    id1a = np.sort(np.random.permutation(np.arange(cmax))[0:qsize]) #ensure uniqueness of ids queried
    id2a = np.sort(np.random.permutation(np.arange(gmax))[0:qsize])
    id1s = ','.join([str(xx) for xx in id1a])
    id2s = ','.join([str(xx) for xx in id2a])
    query = 'select * from demo where id1 in (%s) AND id2 in (%s);' % (id1s,id2s)

    results[qq,0] = round(querytime(conn_disk,query),4)
    results[qq,1] = round(querytime(conn_mem,query),4)
    results[qq,2] = int(qsize)

#4) Now look at the results
print "  disk | memory | qsize"
print "-----------------------"
for row in results:
    print "%.4f | %.4f | %d" % (row[0],row[1],row[2])

結果は次のとおりです。かなり広い範囲のクエリサイズに対して、ディスクはメモリの約1.5倍の時間がかかることに注意してください。

[ramanujan:~]$python -OO sqlite_memory_vs_disk_clean.py
  disk | memory | qsize
-----------------------
9.0332 | 6.8100 | 12630
9.0905 | 6.6953 | 5894
9.0078 | 6.8384 | 17798
9.1179 | 6.7673 | 60850
9.0629 | 6.8355 | 94854
8.9688 | 6.8093 | 17940
9.0785 | 6.6993 | 58003
9.0309 | 6.8257 | 85663
9.1423 | 6.7411 | 66047
9.1814 | 6.9794 | 11345

RAMは、ディスクに対してほぼ瞬時ですか?ここで何が問題になっていますか?

編集

ここでいくつかの良い提案。

私にとっての主なポイントは、**:memory:absolutely fastを作成する方法はおそらくないでしょうが、ディスクアクセスを作成する方法はありますrelatively slow。 **

つまり、ベンチマークはメモリの現実的なパフォーマンスを適切に測定していますが、ディスクの現実的なパフォーマンスは測定していません(たとえば、cache_sizeプラグマが大きすぎるか、書き込みを行っていないため)。これらのパラメーターをいじって、機会があれば見つけた結果を投稿します。

そうは言っても、もし私がインメモリdbからもっと速度を絞ることができると思う人がいれば(cache_sizeとdefault_cache_sizeをジャッキアップすること以外は)、私はすべて耳にしています...

47
ramanujan

SQLiteにはページキャッシュがあるという事実に関係しています。 ドキュメント によると、デフォルトのページキャッシュは2000の1Kページまたは約2Mbです。これはデータの約75%から90%であるため、この2つの数値が非常に似ていることは驚くことではありません。私の推測では、SQLiteページキャッシュに加えて、残りのデータはまだOSディスクキャッシュにあります。ページキャッシュ(およびディスクキャッシュ)をフラッシュするSQLiteを取得した場合、いくつかの本当に大きな違いが表示されます。

39

あなたへの私の質問は、何をベンチマークしようとしているのですか?

すでに述べたように、SQLiteの:memory:DBはディスクベースのものと同じ、つまりページングされたものであり、唯一の違いはページがディスクに書き込まれないことです。したがって、2つの唯一の違いは、ディスク書き込み:memory:を実行する必要がないことです(ディスクページをキャッシュからオフロードする必要がある場合、ディスク読み取りも実行する必要もありません)。

ただし、キャッシュからの読み取り/書き込みは、クエリによってはクエリ処理時間のほんの一部を表す場合があります。クエリには、選択した行がメンバーである必要がある2つの大きなIDセットを持つwhere句があり、これは高価です。

Cary MillsapがOracleの最適化に関するブログで実証しているように(代表的な投稿は次のとおりです: http://carymillsap.blogspot.com/2009/06/profiling-with-my-boy.html )クエリ処理のどの部分に時間がかかるかを理解します。セットメンバーシップテストがクエリ時間の90%を表し、ディスクベースのIO 10%、: memory:に行くと、それらの10%だけが節約されると仮定します。これは代表例ではない極端な例です、しかし、特定のクエリが結果を傾斜させていることを示すことを願っています。より単純なクエリを使用すると、クエリ処理のIO部分が増加するため、:memory :.

最後のメモとして、実際のストレージを担当するSQLiteの仮想テーブルを試し、SQLiteのセル値を保存する方法とは異なるタイプのC++コンテナを使用することで、処理時間に大幅な改善が見られることがあります。 over:memory :、しかしそれは少し話題になっています;)--DD

PS:このスレッドの最も人気のある投稿にコメントするのに十分なカルマがないので、ここでコメントします:)最近のSQLiteバージョンはWindowsでデフォルトで1KBページを使用しないと言っています: http: //www.sqlite.org/changes.html#version_3_6_12

20
ddevienne

SELECTを実行していて、メモリキャッシュを使用しています。 SELECTとUPDATEをインターリーブしてみてください。

7
vartec

SQLiteのメモリデータベースは、実際にはディスクに接触しないページキャッシュです。したがって、パフォーマンスの調整のためにSQLiteでメモリdbを使用することを忘れてください。

ジャーナルをオフにし、同期モードをオフにし、ラージページキャッシュを設定することができ、ほとんどの操作でほぼ同じパフォーマンスが得られますが、耐久性は失われます。

コードから、コマンドを再利用する必要がありますおよびONLY BINDパラメーターであることは、テストパフォーマンスの90%以上を奪ったため、絶対に明らかです。

6
Mash

コードをありがとう。私は192GBで2 x XEON 2690でテストしましたRAM RAID 5の4つのSCSI 15kハードドライブで、結果は次のとおりです。

  disk | memory | qsize
-----------------------
6.3590 | 2.3280 | 15713
6.6250 | 2.3690 | 8914
6.0040 | 2.3260 | 225168
6.0210 | 2.4080 | 132388
6.1400 | 2.4050 | 264038

メモリの速度向上は重要です。

5
jankos

Sqlite3が実際にキャッシュからデータをディスクに書き込んでいない可能性がありますか?数字が似ている理由を説明するかもしれません。

使用可能なメモリが少ないため、OSがページングしている可能性もありますか?

1
Felix

比較的大きなデータセットを返すクエリに焦点を合わせていることに注意してください。小さいデータセットでどのような効果が得られるでしょうか?単一の行を何度も返すには、ディスクが大量にシークする必要がある場合があります-メモリのランダムアクセス時間ははるかに高速です。

1

numpy配列は、シーケンス内の500万以上のオブジェクトを処理するまで、dictやTupleなどのオブジェクトシーケンスよりも低速です。大量のデータを繰り返し処理し、ジェネレーターを使用して一時的な大きなオブジェクトの作成と再作成を回避することで、大量のデータの処理速度を大幅に向上させることができます。

numpyは、線形パフォーマンスを提供するように設計されているため、制限要因になりました。それは、少量または大量のデータを持つ星ではありません。しかし、numpyのパフォーマンスは、データセットが増加しても曲線になりません。直線のままです。

SQLiteのほかに、非常に高速なデータベースがあります。ほとんどのサーバーデータベースよりも高速です。 SQLを使用する軽量で超高速のフォールトトレラントデータベースが存在し、ブラウザから携帯電話まで何年もテストされているのに、なぜ誰もNOSQLデータベースを使用するのかという疑問が生じます。

0
freegnu