Forum |  HardWare.fr | News | Articles | PC | Prix | S'identifier | S'inscrire | Aide Recherche
2512 connectés 

  FORUM HardWare.fr
  Programmation
  SGBD/SQL

  Le NoSQL: MongoDB, Redis, Cassandra, HBase, etc

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

Le NoSQL: MongoDB, Redis, Cassandra, HBase, etc

n°2093610
el muchach​o
Comfortably Numb
Posté le 06-08-2011 à 00:12:52  profilanswer
 

J'ouvre un topic sur un sujet très à la mode ces temps-ci, les bases de données non relationnelles.
C'est le mouvement du NoSQL ("Not only SQL" ).

 

L'idée est que les BdD non relationnelles sont plus simples que les SGBD, et permettent en principe d'atteindre des performances plus élevées. D'autres bénéfices importants:
 - les tables sont simplement représentées comme des couples clés-valeurs, avec éventuellement un ou plusieurs index sur les valeurs
 - comme il n'y a pas de schéma, elles peuvent être scalables horizontalement
 - la réplication permet de rendre l'infrastructure plus résistante.

 

Par ailleurs, certaines n'ont pas les garanties ACID apportées par les SGBD. Evidemment, étant donné leurs limitations, elles ne remplacent pas un SGBD, mais sont plutôt complémentaires, pour des usages spécifiques.
Il n'est pas un secret que nombre de grandes sociétées orientées web (Google en premier) font massivement appel au NoSQL. Mais elles sont de plus en plus utilisées ailleurs pour servir de cache de données en backup de la base principale, qui est généralement un SGBD traditionnel.

 

Venez ici discuter de vos retours d'expérience sur ces outils.

 

NB: [:icon4] ce topic n'est pas un topic "pour ou contre le SQL"

 

Une liste de produits: http://nosql-database.org/


Message édité par el muchacho le 17-05-2012 à 06:07:11
mood
Publicité
Posté le 06-08-2011 à 00:12:52  profilanswer
 

n°2093611
el muchach​o
Comfortably Numb
Posté le 06-08-2011 à 00:17:47  profilanswer
 

J'ouvre le bal avec Kyoto Cabinet, successeur de Tokyo Cabinet.
 
C'est une simple librairie clefs-valeurs aux performances élevées, qui peut être entièrement en RAM, s'appuyer sur un fichier, ou même sur un système de fichiers. Si celui-ci est distribué, la base peut en profiter. De nombreuses structures de données sont proposées, chacune optimisée pour un type d'application. Il suffit de changer le suffixe du nom de la base pour changer de structure. db.kct est un TreeDB fichier par ex, db.kch est un HashDB.
 
Pour les bases en RAM (à la memcache):
    * time efficiency: CacheDB > StashDB > ProtoHashDB > ProtoTreeDB > GrassDB
    * space efficiency: GrassDB > StashDB > CacheDB > ProtoHashDB > ProtoTreeDB
 
Pour les bases fichier:
    * time efficiency: HashDB > TreeDB > DirDB > ForestDB
    * space efficiency: TreeDB > HashDB > ForestDB > DirDB
 
Il y a un binding pour tous les langages majeurs. Le binding Python est simple bien qu'un peu limité, mais il permet de se servir de KC avec la syntaxe des dictionnaires Python. L'usage en est on ne peut plus simple, ce qui est très attractif.
On peut donc l'utiliser comme une sorte de dictionnaire adossé à un disque de façon transparente, c'est simple et très utile.
Un inconvénient majeur, c'est que la clef et la valeur sont des chaînes de caractères ou des valeurs binaires (au moins en Python). Il faut donc parfois marshaller et unmarshaller les objets, ce qui réduit les performances. L'autre inconvénient est que la base est formée d'un seul fichier qui grossit, et non de plusieurs fichiers. Je pense que ça n'est pas optimal avec pas mal de filesystems.  
J'en parle un peu plus ici.
Les usages évidents de cette librairie sont les caches, le logging, l'indexation, le backup de données.
 
Sur un Core2Duo, le TreeDB en Python les perfs sont excellentes jusqu'à 20 millions d'éléments. Pour le test ci-dessous, le fichier fait alors près de 2Go et la durée des inserts semble croitre exponentiellement avec le nombre d'éléments déjà en base, mais sur moins de 10 millions, la durée est dominée par le marshaling. La durée des retraits est constante.
 
Globalement, c'est un produit comme je les aime: une librairie simple, tout le contraire de l'usine à gaz, qui fait donc relativement peu de choses mais qui les fait bien, et peut donc se révéler très utile en étant immédiatement productive.
 
Un petit prog de test qui ne teste que l'insert et le retrait aléatoire.
Il faut savoir que pour le TreeDB, la clef est triée et que l'on peut itérer sur les éléments en base avec un curseur, ce qui est bcp plus rapide.
 

Code :
  1. # Usage: python kctest.py [num of entries] [db name#options]
  2. # Example: python kctest.py 100000 testdb.kct#tune_buckets=10000
  3. #
  4. from kyotocabinet import *
  5. import sys
  6. import os
  7. import re
  8. import time
  9. import random
  10. import marshal
  11.  
  12. def memoryusage():
  13.    for line in open("/proc/self/status" ):
  14.        line = line.rstrip()
  15.        if line.startswith("VmRSS:" ):
  16.            line = re.sub(r".*:\s*(\d+).*", r"\1", line)
  17.            return float(line) / 1024
  18.    return -1
  19.  
  20. musage = memoryusage()
  21. rnum, bulk_size = 1000000, 10000
  22.  
  23. if len(sys.argv) > 1:
  24.    rnum = int(sys.argv[1])
  25.  
  26. if len(sys.argv) > 2:
  27.    hash = DB()
  28.    if not hash.open(sys.argv[2], DB.OWRITER | DB.OCREATE | DB.OTRUNCATE):
  29.        raise RuntimeError(hash.error())
  30. else:
  31.    print "Using Python dict()"
  32.    hash = {}
  33.  
  34. def bulk_insert(rnum, bulk_size):
  35.    print "========== Bulk inserts =========="
  36.    print "Insert %d items" % rnum
  37.    ttime = 0.0
  38.    stime = etime = time.time()
  39.    sum_itimes = 0
  40.    for step in xrange(0, rnum, bulk_size):
  41.  
  42.        steptime = time.time()
  43.        bulk = {}
  44.        for i in xrange(step, step+bulk_size):
  45.            key = "%08d" % i
  46.            #value = (key, 3.1416, i)
  47.            alea = random.randint(1, rnum*10)
  48.            dec = random.random()
  49.            value = {'index': i,
  50.                      'entier': alea,
  51.                      'chaine': "%20.18f %s" % (dec, alea),
  52.                      'float': dec,
  53.                      'datetime': dec * 1000000000
  54.                     }
  55.            bulk[key] = value
  56.        
  57.        stime = time.time()
  58.        hash.set_bulk(bulk)
  59.        ctime = time.time()
  60.        itime = ctime - stime
  61.        print "Time: %.5f ms bulk insert time:  %.5f ms, %.0f/sec" % ((ctime - steptime)*1000, itime*1000, bulk_size/itime), step
  62.        sum_itimes += itime
  63.    print 'Count:', len(hash)
  64.    print "Total time: %.3f sec, total insertion time: %.3f sec" % ((ctime - etime), sum_itimes)
  65.    
  66. def inserts(rnum):
  67.    print "========== Single inserts =========="
  68.    print "Insert %d items" % rnum
  69.    ttime = 0.0
  70.    stime = time.time()
  71.    for i in xrange(0, rnum):  
  72.        key = "%08d" % i
  73.        #value = (key, 3.1416, i)
  74.        alea = random.randint(1, rnum*10)
  75.        dec = random.random()
  76.        value = {'index': i,
  77.                  'entier': alea,
  78.                  'chaine': "%20.18f %s" % (dec, alea),
  79.                  'float': dec,
  80.                  'datetime': dec * 1000000000
  81.                 }
  82.        hash[key] = marshal.dumps(value)
  83.        if i % 100000 == 0:
  84.            ctime = time.time()
  85.            ttime += ctime - stime
  86.            print "Time: %.3f sec." % (ctime - stime), i
  87.            stime = ctime
  88.    print 'Count:', len(hash)
  89.    print "Time: %.3f sec." %  ttime
  90.  
  91. def retrievals(rnum):
  92.    print "========== Single retrievals =========="
  93.    print "Retrieve %d items" % rnum
  94.    stime = time.time()
  95.    ttime = 0.0
  96.    for i in xrange(0, rnum):
  97.        key = "%08d" % i
  98.        value = marshal.loads(hash.get(key))
  99.        #if value != (key, 3.1416, i):
  100.         #raise ValueError("Retrieved and inserted values do not match !" )
  101.        if i % 100000 == 0:
  102.            ctime = time.time()
  103.            ttime += ctime - stime
  104.         print "Time: %.3f sec." % (ctime - stime), i
  105.            stime = ctime
  106.    print "Time: %.3f sec." % ttime
  107.  
  108.    print "Count: %s" % len(hash)
  109.    print "Usage: %.3f MB" % (memoryusage() - musage)
  110.  
  111.  
  112. if __name__ == '__main__':
  113.  
  114.    bulk_insert(rnum, bulk_size)
  115.    inserts(rnum)
  116.    retrievals(rnum)


Message édité par el muchacho le 07-08-2011 à 09:35:07
n°2093612
ratibus
Posté le 06-08-2011 à 00:31:07  profilanswer
 

Ou bien t'es pas obligé de choisir et en utilisant MySQL t'as les 2 :)
http://yoshinorimatsunobu.blogspot [...] y-for.html


---------------
Les histoires de votre enfance : Raconte moi des histoires
n°2093613
mareek
Et de 2 \o/
Posté le 06-08-2011 à 00:36:28  profilanswer
 

ratibus a écrit :

Ou bien t'es pas obligé de choisir et en utilisant MySQL t'as les 2 :)
http://yoshinorimatsunobu.blogspot [...] y-for.html


Les performances d'un SGBDR avec l'intégrité du NoSQL ?


---------------
"I wonder if the internal negative pressure in self pumping toothpaste tubes is adjusted for different market altitudes." John Carmack
n°2093614
el muchach​o
Comfortably Numb
Posté le 06-08-2011 à 00:56:26  profilanswer
 

Sinon, j'évalue MongoDB, version 1.8.2.
 
MongoDB a ceci de particulier qu'il intègre un langage de requêtes. C'est pas du SQL, mais ça émule ce dernier. On peut donc faire des critères de recherche sur n'importe quelle clef ou valeur en base. Les données sont stockées sous forme de BSON, du JSON binaire.
MongoDB intègre des outils pour du sharding et de la réplication, mais par défaut, n'est pas ACID. Il faut spécifiquement activer la journalisation.
 
Mon problème est que les binaires proposés sur le site n'ont pas l'air optimisés. J'ai eu des performances complètement erratiques sous Windows, et sur mon PC perso sous Linux, j'ai des performances très médiocres comparées à celles obtenues sur un serveur Linux  de mon bureau: 6900 inserts/seconde sur mon C2D (32 bits) contre 20 000/sec sur celui du bureau (64 bits). Mais c'est encore bien pire pour les requêtes, j'ai une différence qui tourne entre un facteur 30 et un facteur 50. P-ê que le disque est bcp plus lent, mais je doute un peu que cela suffise à expliquer de telles différences.Il semblerait que les perfs de cette base soient boostées sur un PC récent. En query, sur une machine récente, on atteint des perfs excellentes de l'ordre de 100 000 à plusieurs centaines de milliers d'objets Python/sec sélectionnés avec plusieurs critères (voir prog ci-dessous), ordonnés selon un champs et récupérés.
 
Mais même avec des différences de disque dur, je ne comprends pas comment on peut obtenir des résultats aussi disparates avec la même version, sur des machines différentes, et curieusement, je ne vois rien sur le site qui explique comment "tuner" la base pour en tirer le maximum. Par ailleurs, si l'espace disque est trop limité, le serveur a tendance à non pas s'arrêter de façon propre, mais à crasher violamment (c'est pourtant la version de production compilée en statique du site).
Enfin, quelle que soit la machine cible, dans mes benchmarks, 98 à 100% du temps CPU est utilisé par Python, ce qui semble indiquer que binding Python, entièrement écrit en Python n'est pas très optimisé et est le bottleneck. Je n'ai pas fait de mesures avec Java et C++.
 
Un prog de test inserts et requêtes:
(Si qq a le loisir de passer mon script de test chez lui, ça m'intéresserait de connaitre les résultats obtenus.)

Code :
  1. # Usage: python mongotest.py num_of_entries bulk_size
  2. # Example: python mongotest.py 100000 1000
  3. #
  4.  
  5. import sys
  6. import os
  7. import re
  8. import time
  9. import datetime
  10. import decimal
  11. import random
  12. import json
  13.  
  14.  
  15.  
  16. def memoryusage():
  17.   for line in open("/proc/self/status" ):
  18.       line = line.rstrip()
  19.       if line.startswith("VmRSS:" ):
  20.           line = re.sub(r".*:\s*(\d+).*", r"\1", line)
  21.           return float(line) / 1024
  22.   return -1
  23.  
  24.  
  25.  
  26. rnum = 1000000
  27. fname = 'documents.txt'
  28. if len(sys.argv) > 1:
  29.   rnum = int(sys.argv[1])
  30.   bulk_size = int(sys.argv[2])
  31.  
  32.  
  33. def bulk_insert(rnum, bulk_size):
  34.    random.seed(17)
  35.  
  36.    print "Insert %d items" % rnum
  37.  
  38.    f = open(fname, 'r')
  39.    if 'posts' in db.collection_names():
  40.        print 'cleaning db.posts'
  41.        db.drop_collection('posts')
  42.        print db.posts.count()
  43.  
  44.    posts = db.posts
  45.    ttime = time.time()
  46.    sum_itimes = 0
  47.    
  48.    for step in xrange(0, rnum, bulk_size):
  49.        steptime = time.time()
  50.        bulk = []
  51.        for i in xrange(step, step+bulk_size):
  52.            r = json.loads(f.readline())
  53.            r['datetime'] = datetime.datetime.fromtimestamp(r['datetime'])
  54.            bulk.append(r)      
  55.  
  56.        stime = time.time()
  57.        posts.insert(bulk)
  58.        ctime = time.time()
  59.        itime = ctime - stime
  60.        print "Time: %.3f sec insertion time:  %.3f sec, %.3f/sec" % (ctime - steptime, itime, bulk_size/itime), step
  61.        sum_itimes += itime
  62.    etime = time.time()
  63.  
  64.    print 'Number of docs in posts', posts.count()
  65.    print "Total time: %.3f sec, total insertion time: %.3f sec" % ((etime - ttime), sum_itimes)
  66.  
  67. def retrievals():
  68.    random.seed(17)
  69.    print "==========Indexing==========="
  70.  
  71.    db.posts.create_index('entier')
  72.    stime = time.time()
  73.    sum_itimes = 0
  74.  
  75.    print "==========Sorted Queries==========="
  76.    sum_num = 0
  77.    ttime = time.time()
  78.    for i in xrange(0, 10):      
  79.        dec = random.random()
  80.      
  81.        a_date = datetime.datetime.fromtimestamp(dec * 1000000000)
  82.        query = { 'datetime': {'$gt': a_date}, 'float': {'$gt': dec, '$lt': dec+0.25} }
  83.        num = 0
  84.        stime = time.time()
  85.        db.posts.find(query).count()
  86.        for num, record in enumerate(db.posts.find(query)):
  87.            pass
  88.  
  89.        ctime = time.time()
  90.        itime = ctime - stime
  91.        sum_num += num
  92.        sum_itimes += itime
  93.        form = "Time: %.3f sec. to fetch %s records (datetime > %s, float > x=%f < x+0.25)"
  94.        print form % (itime, num, a_date, dec)
  95.  
  96.    etime = time.time()
  97.    print "%s records retrieved retrieved in %.3f sec." % (sum_num, sum_itimes)
  98.    print "Total time: %.3f sec., total query time: %.2f sec." % ((etime - ttime), sum_itimes)    
  99.  
  100. def generate(rnum):
  101.    f = open(fname, 'w')
  102.    print 'Generating random %s documents' % rnum
  103.  
  104.    random.seed(17)
  105.  
  106.    for i in xrange(0, rnum):
  107.        # a random record
  108.        alea = random.randint(1, rnum*10)
  109.        dec = random.random()
  110.  
  111.        record = {'index': i,
  112.                  'entier': alea,
  113.                  'chaine': "%20.18f %s" % (dec, alea),
  114.                  'float': dec,
  115.                  'datetime': dec * 1000000000
  116.                 }
  117.  
  118.        dump = json.dumps(record, separators=(',',':'))
  119.        f.write(dump + '\n')
  120.  
  121. if __name__ == '__main__':
  122.  
  123.    from pymongo import Connection  
  124.  
  125.    if not os.path.exists(fname):
  126.        generate(rnum)
  127.    
  128.    connection = Connection('localhost', 27017)
  129.    db = connection.test_database
  130.    
  131.    bulk_insert(rnum, bulk_size)
  132.    retrievals()
  133.  
  134.    print "Used memory %d Mb" % memoryusage()


 
Exemple de sortie sur ma machine:


(myenv)nicolas@gromit:~/myenv$ python mongo_test.py 100000 500
Generating random 100000 documents
Insert 100000 items
cleaning db.posts
0
Time: 0.153 sec insertion time:  0.073 sec, 6868.229/sec 0
Time: 0.165 sec insertion time:  0.072 sec, 6922.322/sec 500
Time: 0.161 sec insertion time:  0.072 sec, 6950.910/sec 1000
Time: 0.167 sec insertion time:  0.072 sec, 6947.640/sec 1500
..........
Time: 0.166 sec insertion time:  0.072 sec, 6899.116/sec 99500
Number of docs in posts 100000
Total time: 32.603 sec, total insertion time: 14.283 sec
==========Indexing===========
==========Sorted Queries===========
Time: 288.215 sec. to fetch 96032 records (datetime < 2000-06-08 22:06:14.323877, float > x=0.960495 < x+0.25)
Time: 8.254 sec. to fetch 11024 records (datetime < 1973-06-29 01:34:08.917212, float > x=0.110162 < x+0.25)
Time: 221.906 sec. to fetch 74616 records (datetime < 1993-08-26 21:59:56.166384, float > x=0.746395 < x+0.25)
Time: 285.353 sec. to fetch 94498 records (datetime < 1999-12-14 18:51:54.504889, float > x=0.945194 < x+0.25)
Time: 263.617 sec. to fetch 87489 records (datetime < 1997-09-27 01:28:22.128281, float > x=0.875317 < x+0.25)
Time: 40.043 sec. to fetch 20844 records (datetime < 1976-08-27 18:06:49.490222, float > x=0.210010 < x+0.25)
Time: 195.412 sec. to fetch 63855 records (datetime < 1990-03-30 06:59:41.059796, float > x=0.638773 < x+0.25)
Time: 130.868 sec. to fetch 47521 records (datetime < 1985-01-09 07:01:11.106405, float > x=0.474098 < x+0.25)
Time: 239.179 sec. to fetch 81286 records (datetime < 1995-10-11 03:22:01.749089, float > x=0.813378 < x+0.25)
Time: 239.588 sec. to fetch 75193 records (datetime < 1993-10-31 03:33:53.571964, float > x=0.752035 < x+0.25)
652358 records retrieved retrieved in 1912.435 sec.
Total time: 1912.436 sec., total query time: 1912.44 sec.
Used memory 46 Mb


Ces mêmes queries tournent autour de la fraction de seconde a la seconde sur un autre PC. Je ne comprends pas bien ces résultats.


Message édité par el muchacho le 06-08-2011 à 21:23:29
n°2093623
ratibus
Posté le 06-08-2011 à 09:11:00  profilanswer
 

mareek a écrit :


Les performances d'un SGBDR avec l'intégrité du NoSQL ?


Ca commence à troller sur MySQL :/


---------------
Les histoires de votre enfance : Raconte moi des histoires
n°2093626
el muchach​o
Comfortably Numb
Posté le 06-08-2011 à 09:46:35  profilanswer
 

ratibus a écrit :

Ou bien t'es pas obligé de choisir et en utilisant MySQL t'as les 2 :)
http://yoshinorimatsunobu.blogspot [...] y-for.html


Intéressant.
Ici un post comparant un MySQL "normal" et MongoDB. Pas sûr qu'il ait bien indexé MySQL pour obtenir de telles différences, ou alors sa base était entièrement en RAM ?


Message édité par el muchacho le 06-08-2011 à 10:08:24
n°2093629
verdoux
And I'm still waiting
Posté le 06-08-2011 à 10:19:53  profilanswer
 

Ce comparatif est ridicule.

n°2093630
el muchach​o
Comfortably Numb
Posté le 06-08-2011 à 10:24:48  profilanswer
 

Je pense aussi. Vu qu'on n'a plus accès aux sources, on ne sait pas s'il a indexé la base, ni si ce sont des queries avec un résultat unitaires ou retournant un certains nombre d'éléments, etc.


Message édité par el muchacho le 06-08-2011 à 10:27:41
n°2093635
ratibus
Posté le 06-08-2011 à 11:32:03  profilanswer
 

verdoux a écrit :

Ce comparatif est ridicule.


+1
 
Pour le DELETE : il pourrait faire un TRUNCATE vu qu'il a pas de critère dans son DELETE
Pour le SELECT : c'est indexé vu que la colonne k est PK (mais pour faire du PK lookup faut utiliser ce que j'ai mis + haut). Il utilise même pas de prepared statement.
Pour les INSERT : il utilise pas d'INSERT bulk ou de LOAD DATA donc oui les perfs ça fait mal.
 
Bref, il sait pas utiliser MySQL :o


---------------
Les histoires de votre enfance : Raconte moi des histoires
n°2093649
verdoux
And I'm still waiting
Posté le 06-08-2011 à 13:45:58  profilanswer
 

J'obtiens ça sur un netbook (atom n550 1,5GHz):

Citation :


 
Number of docs in posts 100000
Total time: 19.710 sec, total insertion time: 7.371 sec
==========Indexing===========
==========Sorted Queries===========
Time: 3.432 sec. to fetch 52315 records (datetime < 1986-07-17 13:31:49.712493, float > x=0.521984 < x+0.25)
Time: 4.902 sec. to fetch 80629 records (datetime < 1995-07-25 18:46:17.118679, float > x=0.806691 < x+0.25)
Time: 5.761 sec. to fetch 96032 records (datetime < 2000-06-08 22:06:14.323877, float > x=0.960495 < x+0.25)
Time: 2.113 sec. to fetch 28968 records (datetime < 1979-03-07 04:29:37.764466, float > x=0.289625 < x+0.25)
Time: 4.691 sec. to fetch 76597 records (datetime < 1994-04-12 01:37:17.797953, float > x=0.766107 < x+0.25)
Time: 4.355 sec. to fetch 70374 records (datetime < 1992-04-25 18:37:46.843413, float > x=0.704220 < x+0.25)
Time: 4.143 sec. to fetch 66121 records (datetime < 1990-12-16 22:30:57.223830, float > x=0.661383 < x+0.25)
Time: 1.127 sec. to fetch 11024 records (datetime < 1973-06-29 01:34:08.917212, float > x=0.110162 < x+0.25)
Time: 0.691 sec. to fetch 2699 records (datetime < 1970-11-08 19:26:18.790527, float > x=0.026937 < x+0.25)
Time: 2.605 sec. to fetch 38520 records (datetime < 1982-03-05 11:11:44.544297, float > x=0.384171 < x+0.25)
523279 records retrieved retrieved in 33.819 sec.
Total time: 33.821 sec., total query time: 33.82 sec.
Used memory 17 Mb

n°2093658
el muchach​o
Comfortably Numb
Posté le 06-08-2011 à 14:31:03  profilanswer
 

OK, ça parait cohérent avec les résultats des autres machines. Je ne comprends pas pourquoi ça rame autant sur mon C2D@2GHz. :/

n°2093659
verdoux
And I'm still waiting
Posté le 06-08-2011 à 14:32:24  profilanswer
 

T'as pensé à mettre à jour ta zlib ?
 
 :whistle:

n°2093660
el muchach​o
Comfortably Numb
Posté le 06-08-2011 à 14:34:56  profilanswer
 

:D [:natas]

 

Nan mais y'a un vrai pb sur ma machine, on dirait, même en insertion, c'est plus lent que ton netbook. Ceci dit, sous XP, au boulot, j'ai eu aussi des perfs anormalement mauvaises, du genre 1 record/seconde (pas d'antivirus). Mais si ça se trouve, les données passaient par le réseau...

 

edit:

 

Bon, mongostat m'indique que la base ne fait rien la plupart du temps, et les commandes:

db.setProfilingLevel(2);
db.system.profile.find( { millis : { $gt : 50 } } )

 

m'indiquent qu'aucune requête n'a pris plus de 150 ms. On dirait qu'iIl y a de mon coté un pb de communication entre la base et le driver Python. D'ailleurs, mongod occupe 0% du CPU tandis que Python mouline comme un coureur du tour de France...

 

edit: j'avais oublié que j'avais le driver pur Python et non le driver écrit en C...


Message édité par el muchacho le 06-08-2011 à 20:48:26
n°2093678
el muchach​o
Comfortably Numb
Posté le 06-08-2011 à 17:25:06  profilanswer
 

Pour comparer, même programme mais avec SQLite 3 (qui est inclus par défaut dans la distrib Python). Vous avez quoi comme perfs ?

 
Code :
  1. import sys
  2. import os
  3. import re
  4. import time
  5. import datetime
  6. import decimal
  7. import random
  8. import json
  9. import sqlite3
  10.  
  11. def memoryusage():
  12.   for line in open("/proc/self/status" ):
  13.       line = line.rstrip()
  14.       if line.startswith("VmRSS:" ):
  15.           line = re.sub(r".*:\s*(\d+).*", r"\1", line)
  16.           return float(line) / 1024
  17.   return -1
  18.  
  19. rnum = 1000000
  20. fname = 'documents.txt'
  21. if len(sys.argv) > 1:
  22.   rnum = int(sys.argv[1])
  23.   bulk_size = int(sys.argv[2])
  24.  
  25. def bulk_insert(conn, rnum, bulk_size):
  26.    random.seed(17)
  27.    
  28.    c = conn.cursor()
  29.    c.execute("drop table if exists record" )
  30.    c.execute('''create table if not exists record (idx integer,
  31.                                      chaine text,
  32.                                      float real,
  33.                                      entier integer,
  34.                                      datetime real)''')
  35.  
  36.    print "Insert %d items" % rnum
  37.    
  38.    f = open(fname, 'r')
  39.    ttime = time.time()
  40.    sum_itimes = 0
  41.    
  42.    for step in xrange(0, rnum, bulk_size):
  43.  
  44.        def tuple_gen():
  45.            for i in xrange(step, step+bulk_size):
  46.                r = json.loads(f.readline())
  47.                #r['datetime'] = datetime.datetime.fromtimestamp(r['datetime'])
  48.                yield tuple(r.values())
  49.  
  50.        steptime = time.time()        
  51.        stime = time.time()
  52.        c.executemany("insert into record values (?,?,?,?,?)", tuple_gen())
  53.        conn.commit()
  54.        ctime = time.time()
  55.        itime = ctime - stime
  56.        print "Time: %.3f sec insertion time:  %.3f sec, %.3f/sec" % (ctime - steptime, itime, bulk_size/itime), step
  57.        sum_itimes += itime
  58.    etime = time.time()
  59.    print 'Number of docs in record table:', c.execute("select count(*) from record" ).fetchone()[0]
  60.    print "Total time: %.3f sec, total insertion time: %.3f sec" % ((etime - ttime), sum_itimes)
  61.    
  62. def retrievals(conn):
  63.    random.seed(17)
  64.    c = conn.cursor()
  65.  
  66.    print "==========Indexing================"
  67.    c.execute("create index if not exists entier_idx on record(entier)" )
  68.    c.execute("analyze" )
  69.    #c.execute("create index if not exists datetime_idx on record(datetime)" )
  70.    #c.execute("create index if not exists float_idx on record(float)" )
  71.    stime = time.time()
  72.    sum_itimes = 0
  73.  
  74.    print "==========Sorted Queries==========="
  75.    sum_num = 0
  76.    ttime = time.time()
  77.    for i in xrange(0, 10):      
  78.        dec = random.random()
  79.        
  80.        a_date = datetime.datetime.fromtimestamp(dec * 1E9)
  81.        num = 0
  82.        stime = time.time()
  83.        query = "select * from record where datetime > %s and (float > %s and float < %s)" % (dec*1E9, dec, dec+0.25)
  84.        c.execute(query)
  85.        for num, record in enumerate(c):
  86.            pass
  87.        ctime = time.time()
  88.        itime = ctime - stime
  89.        sum_num += num
  90.        sum_itimes += itime
  91.        form = "Time: %.3f sec. to fetch %s records (datetime > %s, float > x=%f < x+0.25)"
  92.        print form % (itime, num, a_date, dec)
  93.    etime = time.time()
  94.    print "%s records retrieved retrieved in %.3f sec." % (sum_num, sum_itimes)
  95.    print "Total time: %.3f sec., total query time: %.2f sec." % ((etime - ttime), sum_itimes)
  96.  
  97. def generate(rnum):
  98.    f = open(fname, 'w')
  99.    print 'Generating random %s documents' % rnum
  100.    random.seed(17)
  101.    for i in xrange(0, rnum):
  102.        # a random record
  103.        alea = random.randint(1, rnum*10)
  104.        dec = random.random()
  105.        record = {'index': i,
  106.                  'entier': alea,
  107.                  'chaine': "%20.18f %s" % (dec, alea),
  108.                  'float': dec,
  109.                  'datetime': dec * 1000000000
  110.                 }
  111.        dump = json.dumps(record, separators=(',',':'))
  112.        f.write(dump + '\n')
  113.    
  114. if __name__ == '__main__':
  115.    
  116.    if not os.path.exists(fname):
  117.        generate(rnum)
  118.    
  119.    conn = sqlite3.connect('sqlite.db')
  120.    
  121.    bulk_insert(conn, rnum, bulk_size)
  122.    retrievals(conn)
  123.    print "Used memory %d Mb" % memoryusage()



> python sqlite_test.py 1000000 5000

 

...
Number of docs in record table: 1000000
Total time: 164.432 sec, total insertion time: 164.421 sec
==========Sorted Queries===========
Time: 1.990 sec. to fetch 249503 records (datetime > 1986-07-17 13:31:49.712493, float > x=0.521984 < x+0.25)
Time: 1.626 sec. to fetch 193353 records (datetime > 1995-07-25 18:46:17.118679, float > x=0.806691 < x+0.25)
Time: 0.849 sec. to fetch 39576 records (datetime > 2000-06-08 22:06:14.323877, float > x=0.960495 < x+0.25)
Time: 2.105 sec. to fetch 250627 records (datetime > 1979-03-07 04:29:37.764466, float > x=0.289625 < x+0.25)
Time: 1.817 sec. to fetch 233786 records (datetime > 1994-04-12 01:37:17.797953, float > x=0.766107 < x+0.25)
Time: 1.921 sec. to fetch 249910 records (datetime > 1992-04-25 18:37:46.843413, float > x=0.704220 < x+0.25)
Time: 1.946 sec. to fetch 250174 records (datetime > 1990-12-16 22:30:57.223830, float > x=0.661383 < x+0.25)
Time: 2.156 sec. to fetch 249436 records (datetime > 1973-06-29 01:34:08.917212, float > x=0.110162 < x+0.25)
Time: 2.182 sec. to fetch 249905 records (datetime > 1970-11-08 19:26:18.790527, float > x=0.026937 < x+0.25)
Time: 2.080 sec. to fetch 250116 records (datetime > 1982-03-05 11:11:44.544297, float > x=0.384171 < x+0.25)
2216386 records retrieved retrieved in 18.671 sec.
Total time: 18.672 sec., total query time: 18.67 sec.
Used memory 7 Mb

 


Les perfs sont excellentes. A noter que sans la clause ORDER BY, les requêtes s'exécutent en 9,6 sec au lieu de 37,5 sec.
Curieusement, le fait d'ajouter un index sur cette colonne à la création de la table fait que l'insertion ralentit considérablement. On passe de 6000 à moins de 1000 insertions/sec.
verdoux, tu as quoi, comme résultats ?
En outre, il faut savoir que SQLite ne vérifie pas les types de données (typage dynamique), contrairement aux SGBDR traditionnels. Les données peuvent être corrompues.


Message édité par el muchacho le 07-08-2011 à 12:24:04
n°2093711
el muchach​o
Comfortably Numb
Posté le 06-08-2011 à 20:46:46  profilanswer
 

OK, problem solved.
Via easy_install, j'avais installé le driver pur Python et non le driver C. Maintenant, les insertions via Pymongo se font à 48 000/sec (sauf qu'elles ne sont pas forcément écrites sur disque instantanément). Les requêtes ne sont pas extrêmement rapides, mais prennent autour de 7 secondes pour 250 000 lignes sur une base d'un million. C'est pas mal, mais c'est plus lent que SQLite. Dans ces tests, MongoDB est 8 fois plus rapide en insertion, et 4 fois plus lent en query, via leurs drivers Python respectifs. Reste que la vitesse du disque est primordiale et les tests en query sur un SSD donnent des vitesses très élevées. Globalement, les perfs sur un seul node sont satisfaisantes au regard des possibilités offertes par la base. Je n'ai pas fait de tests en cluster.

 

Comme la base est memory mapped, MongoDB ne fonctionnera pas sur des bases de plusieurs millions de lignes en 32 bits. Il faut obligatoirement un OS 64 bits.


Message édité par el muchacho le 06-11-2011 à 16:54:54
n°2093748
el muchach​o
Comfortably Numb
Posté le 07-08-2011 à 00:28:21  profilanswer
 
n°2093770
verdoux
And I'm still waiting
Posté le 07-08-2011 à 10:50:01  profilanswer
 
n°2093946
el muchach​o
Comfortably Numb
Posté le 08-08-2011 à 14:54:39  profilanswer
 

Quelques liens utiles:

 

http://nosql.mypopescu.com/post/61 [...] experience
http://kkovacs.eu/cassandra-vs-mon [...] b-vs-redis
http://code.google.com/p/scalable-frameworks/
http://www.slideshare.net/ksankar/nosql-4559402

 

Le diagramme ci-dessous fait référence au "théorème PAC" qui dit qu'un système distribué ne peut pas satisfaire plus de deux propriétés simultanément parmi les 3 que sont Partition tolerance, Availibility et Consistency.
Les différentes bases garantissent donc seulement deux de ces propriétés. L'application envisagée doit impérativement tenir compte de ces limitations.

 

Sinon, d'un point de vue conceptuel, il me semble que les bases orientées "tables" façon BigTable sont plus complexes à appréhender que les bases orientées documents comme MongoDB, qui se prêtent bien à la programmation OO, chaque document représentant une hiérarchie d'objets; tandis que les bases orientées clefs-valeurs sont les plus simples, et peuvent être utilisées comme des dictionnaires améliorés.

 

http://posterous.com/getfile/files.posterous.com/nahurst/IAHBjwAxcmieJzvtqqyIgriyIzqquwwxumguABujzlzHEbEeJgvhCFcriika/media_httpfarm5static_mevIk.png.scaled1000.png

 

http://pl.atyp.us/images/visual_guide.png


Message édité par el muchacho le 08-08-2011 à 23:33:29
n°2094210
el muchach​o
Comfortably Numb
Posté le 09-08-2011 à 15:55:57  profilanswer
 

Cassandra, ou le NoSQL qui rajoute une couche... de SQL... :D

 

Rien de très étonnant. Cassandra/HBase/BigTable sont des bases SQL avec schéma.

 

Les concepts de Cassandra/HBase/BigTable sont bcp plus clairs si on fait la correspondance:


Cassandra       SQL
--------------------
Column          colonne
SuperColumn    ligne
ColumnFamily   table


Il n'y a pas de jointure dans Cassandra, parce que que les ColumnFamilys peuvent contenir d'autres ColumnFamilys, ce qui revient effectivement à faire l'équivalent d'une jointure.
Dans Cassandra, les supercolumns et les columns sont des dictionnaires clefs-valeurs. L'absence d'une clef correspond à un NULL dans une table SQL.
Donc les concepts sont équivalents, ce qui explique le fait qu'on puisse faire un SQL par dessus les tables Cassandra.
Il y a cependant une différence majeure, qui est que dans Cassandra, les columns et les Supercolumns sont triées par défaut, contrairement aux RDBMS. L'avantage est un gain certains de perfs. Il est possible d'obtenir des données en sens inverse via SliceRange tant que le nombre de données requêté tient en RAM, ça n'est donc pas aussi performant qu'un ORDER BY d'un RDBMS.


Message édité par el muchacho le 09-08-2011 à 16:24:52
n°2110023
el muchach​o
Comfortably Numb
Posté le 06-11-2011 à 14:35:24  profilanswer
 

Bon, MongoDB a mauvaise presse ces temp-ci:
http://news.ycombinator.com/item?id=3202081
http://news.ycombinator.com/item?id=3200683
Réponse du boss de 10gen:http://news.ycombinator.com/item?id=3202959
En particulier, les multiples posts indiquant des pertes de données (même avec une version 2.0),sont particulièrement inquiétants, donnant l'impression que ce produit n'est pas mature.


Message édité par el muchacho le 06-11-2011 à 18:21:54
n°2110336
___alt
(╯°□°)╯︵ ┻━┻
Posté le 08-11-2011 à 14:43:04  profilanswer
 

Fais ressortir la réponse du boss quand même, ça met en lumière que les posts précédents étaient de la merde : http://news.ycombinator.com/item?id=3202959


---------------
┬──┬◡ノ(° -°ノ)
n°2136532
el muchach​o
Comfortably Numb
Posté le 14-04-2012 à 11:48:50  profilanswer
 

Non, ça n'était pas de la merde.
http://blog.engineering.kiip.me/po [...] th-mongodb
 
MongoDB n'est manifestement pas un outil fiable.

n°2136798
___alt
(╯°□°)╯︵ ┻━┻
Posté le 16-04-2012 à 10:19:38  profilanswer
 

el muchacho a écrit :

Non, ça n'était pas de la merde.
http://blog.engineering.kiip.me/po [...] th-mongodb
 
MongoDB n'est manifestement pas un outil fiable.


 
Si, c'était un peu de la merde. Y'a quand même un monde entre : "l'intégrité des données n'est pas garantie" et "l'exploitation de MongoDB est délicate du fait de certains problèmes liés aux performances".


---------------
┬──┬◡ノ(° -°ノ)
n°2142780
el muchach​o
Comfortably Numb
Posté le 17-05-2012 à 05:50:19  profilanswer
 

Et encore des gens qui lâchent mongodb:
http://www.korokithakis.net/posts/ [...] 3-i-guess/
http://www.zopyx.de/blog/goodbye-mongodb
Toujours les mêmes raisons.


Message édité par el muchacho le 17-05-2012 à 05:57:29
mood
Publicité
Posté le   profilanswer
 


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  SGBD/SQL

  Le NoSQL: MongoDB, Redis, Cassandra, HBase, etc

 

Sujets relatifs
php + mongoDBConférence MongoDB Paris le 23 Mars
[NoSQL] MongoDB et l'aggrégation 
Plus de sujets relatifs à : Le NoSQL: MongoDB, Redis, Cassandra, HBase, etc


Hit-Parade
Copyright © 1997-2012 Hardware.fr SARL / Groupe LDLC / LesNumeriques.com / Version anglaise du site: BeHardware