6
votes

SQLite vs Serializing sur le disque

Je fais une comparaison de performance si vous souhaitez utiliser des données sérialisées ou pour les stocker dans une DB. L'application reçoit l'enfer de nombreuses données (X Go) qui doivent être persistées avec une vitesse de vitesse minimale de 18 Mo / s (comme pour l'instant)

Stockage dans DB offre une fonctionnalité plus facile en termes de recherche et d'accès aux données à une heure ultérieure, instantanés de données, migration de données et etc., mais mes tests jusqu'à présent indiquent une énorme différence de temps de performance.

Le test enregistre 1000 objets (d'environ 7 cent cent autres KB). Soit à leurs colonnes respectives dans le tableau ou sur le disque en les sauvant comme une liste générique. (La SQLite finit par un peu plus de données)

  1. Sauvegarde de SQLite V3, taille totale 745MB: 30.7Secondes (~ Vitesse: 24,3 Mo / s)
  2. Serializing sur le disque, taille totale 741 Mo: 0.33 secondes (~ Vitesse: 2245 Mo / s)

    Je n'ai effectué aucune performance Tweaks à SQLite, il suffit de l'utiliser hors de la boîte avec NHibernate fluide et l'adaptateur SQLITE.DATA (pas de transaction), mais à la première pensée qui est une énorme différence.

    Évidemment, je sais que passer par une ormission mappeuse et DB pour écrire sur le disque donne une surcharge par rapport à la sérialisation, mais c'était beaucoup.

    aussi dans les considérations consiste à persister les données tout de suite que je les reçois. S'il y a une panne de courant, j'ai besoin des dernières données reçues.

    Toutes les pensées?

    ------ Mises à jour (comme je continue à enquêter sur des solutions) ------

    • Emballage des 1000 inserts dans une transaction L'heure était maintenant ~ 14s = 53mb / s, cependant si je jette une exception à mi-chemin, je perds toutes mes données.
    • L'utilisation d'une éditeur semble améliorer le temps de 0,5-1s
    • n'a vu aucun gain de performance en attribuant l'ID à l'entité au lieu de l'avoir attribué automatiquement dans le tableau et de se débarrasser de (sélectionnez Row_GenerateID ()) pour chaque insert SQL. -> ID (x => x.Id) .Génerated.Assigné ();
    • L'alternative NOSYNC () dans SQLite n'est pas une alternative car la DB pourrait être corrompue en cas de panne de courant.

10 commentaires

Avez-vous essayé dapper avec SQLite? code.google.com/p/dapper-dot-net - aussi Assurez-vous de démarrer une transaction sur SQLite avant de commencer à insérer, si chaque insert s'exécute dans sa propre transaction implicite, vous tuerez des performances.


En ce qui concerne la solution hors de la boîte, il fonctionne implicite oui. Je ne sais pas si je peux l'exécuter différemment, les données doivent être persistées tout de suite que celles-ci me sont nourries et si je perds tout le résultat qui attend le commit lors d'une défaillance de puissance qui serait mauvaise. Va le tester cependant.


@IslandWind: envisagez d'utiliser insert ou [Ignorer] puis.


@Alixaxel ne vous suit pas tout à fait à ce sujet?


@Islandwind: L'option Ignore entraînera l'insertion de ne pas échouer, même lorsqu'il ne parvient pas à insérer - peut être une bonne solution à utiliser conjointement avec des transactions, en fonction de votre stratégie de traitement des erreurs. Néanmoins, 14 secondes pour 1000 inserts sont toujours une performance de merde ... Êtes-vous sûr que le goulot d'étranglement n'est pas l'orm?


@Islandwind: Aussi, avez-vous essayé de benchmarking insert ou ignorez dans "Table" (1, 'Row-1'), (2, 'Row-2'), (3, 'Row-3'), ...; ?


@Alixaxel Vous n'êtes pas sûr de savoir si l'ORM est le goulot d'étranglement. Pensait que NHibernate était assez bon. J'ai essayé de modifier tous les paramètres possibles dans le cadre sans chance. Je n'ai pas essayé d'ignorer ou de multiples inserts autant que je sache (joué avec des paramètres de Batch_Size ..). Peut-être devra peut-être envisager d'utiliser quelque chose d'autre oui.


Je recommande vivement la réponse d'Alix ci-dessous. Lorsque vous commettez une modification à la DB, SQLITE SYNC () TO DISK - A REAL transaction - et cela prend généralement deux rotations du disque physique. Avec des vitesses de disque typiques, cela signifie que vous ne pouvez obtenir que cent des transactions par seconde. Il signifiera que vous payez le coût de la synchronisation Sync () une seule fois.


@Donalfellows merci pour votre point de vue à ce sujet. Si je comprends bien Alixaxel, je peux toujours avoir mes données intactes avec une ignore, même s'il y a une panne de courant pendant le temps que je reçois des données et la persiste en transaction. Je vais examiner les inserts en une seule transaction, mais car les données sont alimentées d'un robot en temps réel, je peux voir des difficultés avec cela aussi. Je pourrais simplement rechercher la persistance des données lourdes sur le disque et enregistrer des métadonnées à SQLite pour obtenir les performances nécessaires.


Si les risques de rejet sont trop élevés, cela pourrait valoir la peine de regarder pourquoi c'est. Pour des insertions simples de données où il n'y a pas de contraintes ni de déclencheurs, il devrait être possible de pouvoir simplement insérer plusieurs fois dans une transaction. Traitez les problèmes de puissance en fixant l'alimentation électrique avec une batterie. :-)


3 Réponses :


2
votes

Vous devez envisager d'utiliser des déclarations compilées pour SQLite.

vérifier Ceci

sur les requêtes d'insertion / mise à jour Il y a une énorme augmentation de performance, j'ai réussi à obtenir de 2x à 10 fois plus de temps d'exécution plus rapide à l'aide de déclarations compilées, bien que de 33 secondes à 0,3 seconde est longue.

D'autre part, la vitesse d'exécution SQLite dépend du schéma de la table que vous utilisez, ex: Si vous avez un index sur une énorme donnée, cela résulterait d'un insert lent.


1 commentaires

merci pour votre lien. Je vais vérifier cela. La table est créée à partir du modèle de domaine avant que le test soit exécuté (à l'aide de Nibernate Constroit de la construction ()) et autant que je ne connaisse aucun index n'est créé.



6
votes

J'ai eu un Un problème similaire une fois et je vous suggère de faire la route SQLite.

Quant à vos problèmes de performance, je suis sûr que vous obtiendrez un coup de pouce très important si vous: p>

  1. Exécutez tous les inserts dans une seule transaction - les requêtes d'écriture doivent acquérir (et libérer) une serrure dans le fichier SQLite, ceci est très coûteux en termes d'E / S du disque et vous devez remarquer un Boost énorme *** LI>
  2. envisagez d'utiliser des multi-inserts (cela ne fonctionnera probablement pas pour vous puisque vous comptez sur une orm) li>
  3. comme @ user896756 mentionné Vous devez également préparer vos déclarations li> OL>

    Test 1: 1000 inserts h2> xxx pré>
    • PostgreSQL: 4.373 LI>
    • MYSQL: 0.114 LI>
    • SQLite 2.7.6: 13.061 strong> li>
    • SQLite 2.7.6 (NOSYNC): 0,223 strong> li> ul>

      Test 2: 25000 Inserts dans une transaction H2>
      BEGIN;
      CREATE TABLE t2(a INTEGER, b INTEGER, c VARCHAR(100));
      INSERT INTO t2 VALUES(1,59672,'fifty nine thousand six hundred seventy two');
      ... 24997 lines omitted
      INSERT INTO t2 VALUES(24999,89569,'eighty nine thousand five hundred sixty nine');
      INSERT INTO t2 VALUES(25000,94666,'ninety four thousand six hundred sixty six');
      COMMIT;
      


1 commentaires

+1: De plus, même, même en train de grouper les inserts en groupes de 10 ou 20 obtiendra une bonne vitesse.



0
votes

Après avoir enquêté plus loin, la réponse pose un peu une confusion des résultats intiaux.

Tout en testant le résultat avec des données plus grandes, j'ai eu un autre résultat.

Le taux de transfert de disque est limité à 126 Mo / s par le fabricant et comment puis-je écrire 750 Mo dans une fraction de seconde? Pas certain de pourquoi. Mais lorsque j'ai augmenté le montant des données, le taux de transfert est rapide à ~ 136 Mo / s.

comme pour la base de données, en utilisant une transaction, j'ai obtenu des vitesses jusqu'à 90 Mo / s à l'aide de l'absence d'absence avec de grandes quantités de données (5-10 Go). C'est assez bon pour notre objectif et je suis sûr que cela peut toujours être modifié avec des déclarations SQL compilées et autres si nécessaire.


0 commentaires