53 votes

SQLite Performance Benchmark - pourquoi: la mémoire: si lente ... que 1,5 fois plus vite que le disque?

Pourquoi :la mémoire: en sqlite-elle si lente?

J'ai essayé de voir s'il y a des améliorations des performances obtenues en utilisant sqlite en mémoire vs disque de base sqlite. Fondamentalement, je voudrais échanger les temps de démarrage et de mémoire pour obtenir extrêmement rapide des requêtes qui ne sont pas touchés de disque au cours de l'application.

Toutefois, la référence ci-après donne-moi seulement un facteur de 1,5 X à l'amélioration de la vitesse. Ici, je suis de la génération 1M de lignes de données aléatoires et de le charger dans un disque et de la mémoire en fonction de la version de la même table. Je puis exécutez aléatoire des requêtes sur les deux dbs, le retour des ensembles de taille environ 300k. Je m'attendais à la mémoire en fonction de la version à être beaucoup plus rapide, mais comme je le disais, je suis en obtenant seulement 1,5 X la vitesse.

J'ai expérimenté avec plusieurs autres dimensions de la bd et de la requête ensembles; l'avantage de :mémoire: ne semble aller comme le nombre de lignes dans la base de données augmente. Je ne suis pas sûr pourquoi, l'avantage est si petit, si j'ai eu quelques hypothèses:

  • la table utilisée n'est pas assez grand (en lignes) à faire :mémoire: le grand vainqueur
  • plus les jointures de tables et rendrait le :mémoire: avantage plus apparente
  • il y a une sorte de mise en cache de passe à la connexion ou au niveau de l'OS, tels que les résultats précédents sont accessibles en quelque sorte, la corruption de l'indice de référence
  • il y a une sorte de caché l'accès au disque passe que je ne vois pas (je n'ai pas essayé de lsof encore, mais je n'ai désactiver les PRAGMAs pour la journalisation)

Suis-je en train de faire quelque chose de mal ici? Toute réflexion sur la raison d'être :la mémoire n'est pas la production de quasi-instantané des recherches? Voici la référence:

==> 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])

Voici les résultats. Notez que le disque prend environ 1,5 X aussi longtemps que la mémoire d'un éventail assez large de requête tailles.

[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

Ne devrait pas RAM être presque instantanée par rapport à un disque? Ce qui ne va pas ici?

Modifier

Quelques bonnes suggestions ici.

Je suppose que le principal revenu net point pour moi, c'est que **il n'y a probablement pas de façon de faire :mémoire: absolument plus rapide, mais il y a une façon de faire de l'accès au disque relativement plus lente. **

En d'autres termes, l'indice de référence est suffisamment mesurer de façon réaliste les performances de la mémoire, mais pas de façon réaliste les performances de disque (par exemple, parce que le cache_size pragma est trop grand, ou parce que je ne fais pas écrit). Je vais déconner avec ces paramètres et d'après mes constatations, quand je reçois une chance.

Cela dit, si il y a quelqu'un qui pense que je peux le serrer quelques plus de vitesse de la mémoire db (autres que par le calage de la cache_size et default_cache_size, ce que je vais faire), je suis tout ouïe...

42voto

Thomas Jones-Low Points 4987

Il a à voir avec le fait que SQLite a un cache de la page. Selon la Documentation, la page par défaut du cache est de 2000 1K pages ou environ 2 mo. Depuis cette est d'environ 75% à 90% de vos données, il n'est pas surprenant que le nombre deux sont très similaires. Ma conjecture est que, en plus de l'SQLite cache de la page, le reste des données est encore dans l'OS de cache disque. Si vous avez SQLite pour vider le cache de la page (et le cache disque), vous devriez voir quelques vraiment de différences significatives.

22voto

ddevienne Points 557

Ma question pour vous est-Ce que vous essayez de référence?

Comme déjà mentionné, SQLite est :mémoire: DB est juste le même que sur disque, c'est à dire paginée, et la seule différence est que les pages ne sont jamais écrites sur le disque. Donc, la seule différence entre les deux sont les écritures sur disque :mémoire: n'a pas besoin de le faire (elle aussi n'a pas besoin de faire la lecture du disque, soit, lorsqu'un disque page devait être déchargé de la mémoire cache).

Mais de lecture/écriture de la mémoire cache peut représenter qu'une fraction du traitement des requêtes, en fonction de la requête. Votre requête a une clause where avec deux grands ensembles de id les lignes sélectionnées doivent être membres de, ce qui est coûteux.

Comme Cary Millsap démontre dans son blog sur l'optimisation Oracle (ici un représentant de la poste: http://carymillsap.blogspot.com/2009/06/profiling-with-my-boy.html), vous avez besoin de comprendre quelles sont les parties de la requête de traitement de prendre du temps. En supposant que l'ensemble de l'adhésion les tests représentent 90% de la durée de la requête, et le disque IO 10%, passant de :mémoire: enregistre uniquement les 10%. C'est un exemple extrême rare pour être représentatif, mais j'espère qu'il illustre que votre requête est incliné des résultats. Utilisez une requête plus simple, et le IO parties du traitement de la requête va augmenter, et donc le bénéfice de la mémoire:.

Comme note finale, nous avons expérimenté avec SQLite virtuelle de tableaux, où vous êtes en charge de la réelle de stockage, et à l'aide de C++ conteneurs, qui sont tapés à la différence de SQLite de la façon de stocker les valeurs des cellules, nous avons pu voir importante improment dans les temps de traitement plus :mémoire: mémoire, mais qui commence à faire du sujet un peu ;) --DD

PS: je n'ai pas assez de Karma de commenter les plus populaires post de ce thread, je suis donc commenter ici :) pour dire que les récentes SQLite version n'utilisez pas de 1KB les pages par défaut sur Windows: http://www.sqlite.org/changes.html#version_3_6_12

7voto

vartec Points 53382

Vous faites des sélections, vous utilisez le cache mémoire. Essayez d’entrelacer les SELECT avec les UPDATE.

7voto

Mash Points 456

La mémoire de base de données SQLite est en fait le cache de la page qui ne touche jamais le disque. Donc, vous devez l'oublier à l'aide de la mémoire db SQLite pour les réglages de performance

Il est possible de désactiver le journal, désactivez le mode de synchronisation, grand cache de la page et vous aurez presque les mêmes performances sur la plupart des opérations, mais la durabilité seront perdues.

À partir de votre code, il est absolument clair que vous DEVEZ RÉUTILISER la commande et UNIQUEMENT LIER des paramètres, car cela prenait plus de 90% de votre test de performance de suite.

6voto

jankos Points 165

Merci pour le code. J'ai testé sur 2 x XEON 2690 avec 192 Go de RAM avec 4 disques durs SCSI 15k en RAID 5 et les résultats sont les suivants:

   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
 

L'augmentation de la vitesse en mémoire est significative.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X