Comment PostgreSQL effectue-t-il les écritures beaucoup plus rapidement que SQLite?
J'ai fait un simple entier mise à jour de test de performance. SQLite n'a seulement 15 mises à jour par seconde alors que PostgreSQL n'a 1500 mises à jour par seconde.
Le nombre avec le SQLite cas semble être normal.
La FAQ dans l'SQLite site explique que si c'est une limitation fondamentale d'un mouvement de rotation de disque.
En fait, SQLite fera facilement 50 000 habitants ou plus les états d'INSERTION par
deuxième sur un ordinateur de bureau moyen. Mais il ne suffit de faire quelques dizaines de
les transactions par seconde. La vitesse des transactions est limité par la
la vitesse de rotation de votre lecteur de disque. Une transaction exige normalement
deux rotations complètes du plateau de disque, qui, sur un disque 7200 tr /min
en voiture vous limite à environ 60 transactions par seconde. Transaction
la vitesse est limitée par le lecteur de disque de la vitesse (par défaut) SQLite
en fait attend jusqu'à ce que les données sont stockées en toute sécurité sur le disque
la surface avant de la transaction est terminée. De cette façon, si vous avez soudainement
perdre de la puissance, ou si votre système d'exploitation se bloque, vos données sont toujours en sécurité. Pour
détails, lire à ce sujet atomique s'engager dans SQLite..Par défaut, chaque instruction INSERT est sa propre transaction. Mais si vous
surround plusieurs instructions INSERT avec COMMENCER...COMMIT, puis tous les
les inserts sont regroupées en une seule transaction. Le temps nécessaire à l'
valider la transaction est amorti sur toute l'encart ci-joint
états et donc le temps par instruction insert est considérablement réduit.Une autre option consiste à exécuter PRAGMA synchrone=OFF. Cette commande
cause SQLite ne pas attendre sur les données atteignent la surface du disque, qui sera
effectuer les opérations d'écriture semblent être beaucoup plus rapide. Mais si vous perdez la puissance
dans le milieu d'une transaction, votre fichier de base de données peut aller corrompus.
Est cette description de vrai? Alors, comment pouvez-PostgreSQL effectuer beaucoup plus rapide que SQLite?
(J'ai mis les deux fsync
et synchronous_commit
option pour on
dans PostgreSQL)
Mise à JOUR:
Voici le test complet de code écrit en Clojure:
(defproject foo "0.1.0-SNAPSHOT"
:repositories {"sonatype-oss-public" "https://oss.sonatype.org/content/groups/public/"}
:dependencies [[org.clojure/clojure "1.5.1"]
[org.clojure/java.jdbc "0.3.0-SNAPSHOT"]
[com.mchange/c3p0 "0.9.2.1"]
[org.xerial/sqlite-jdbc "3.7.2"]
[postgresql "9.1-901.jdbc4"]])
(ns foo.core
(:require [clojure.java.jdbc :as jdbc]
[clojure.java.jdbc.ddl :as ddl])
(:import [com.mchange.v2.c3p0 ComboPooledDataSource]))
(def sqlite
(let [spec {:classname "org.sqlite.JDBC"
:subprotocol "sqlite"
:subname "test.db"}]
{:datasource (doto (ComboPooledDataSource.)
(.setDriverClass (:classname spec))
(.setJdbcUrl (str "jdbc:" (:subprotocol spec) ":" (:subname spec)))
(.setMaxIdleTimeExcessConnections (* 30 60))
(.setMaxIdleTime (* 3 60 60)))}))
(def postgres
(let [spec {:classname "org.postgresql.Driver"
:subprotocol "postgresql"
:subname "//localhost:5432/testdb"
:user "postgres"
:password "uiop"}]
{:datasource (doto (ComboPooledDataSource.)
(.setDriverClass (:classname spec))
(.setJdbcUrl (str "jdbc:" (:subprotocol spec) ":" (:subname spec)))
(.setUser (:user spec))
(.setPassword (:password spec))
(.setMaxIdleTimeExcessConnections (* 30 60))
(.setMaxIdleTime (* 3 60 60)))}))
(doseq [x [sqlite postgres]]
(jdbc/db-do-commands x
(ddl/create-table :foo [:id :int "PRIMARY KEY"] [:bar :int])))
(doseq [x [sqlite postgres]]
(jdbc/insert! x :foo {:id 1 :bar 1}))
(defmacro bench
[expr n]
`(dotimes [_# 3]
(let [start# (. System (nanoTime))]
(dotimes [_# ~n]
~expr)
(let [end# (. System (nanoTime))
elapsed# (/(double (- end# start#)) 1000000.0)
operation-per-sec# (long (/(double ~n) (/(double (- end# start#)) 1000000000)))]
(prn (str "Elapsed time: " elapsed# " ms (" (format "%,d" operation-per-sec#) " ops)"))))))
(bench (jdbc/query sqlite ["select * from foo"]) 20000)
(bench (jdbc/execute! sqlite ["update foo set bar=bar+1 where id=?" 1]) 100)
(bench (jdbc/query postgres ["select * from foo"]) 20000)
(bench (jdbc/execute! postgres ["update foo set bar=bar+1 where id=?" 1]) 5000)
Et la sortie est:
; Running "select * from foo" 20000 times in SQLite
"Elapsed time: 1802.426963 ms (11,096 ops)"
"Elapsed time: 1731.118831 ms (11,553 ops)"
"Elapsed time: 1749.842658 ms (11,429 ops)"
; Running "update foo set bar=bar+1 where id=1" 100 times in SQLite
"Elapsed time: 6362.829057 ms (15 ops)"
"Elapsed time: 6405.25075 ms (15 ops)"
"Elapsed time: 6352.943553 ms (15 ops)"
; Running "select * from foo" 20000 times in PostgreSQL
"Elapsed time: 2898.636079 ms (6,899 ops)"
"Elapsed time: 2824.77372 ms (7,080 ops)"
"Elapsed time: 2837.622659 ms (7,048 ops)"
; Running "update foo set bar=bar+1 where id=1" 5000 times in PostgreSQL
"Elapsed time: 3213.120219 ms (1,556 ops)"
"Elapsed time: 3564.249492 ms (1,402 ops)"
"Elapsed time: 3280.128708 ms (1,524 ops)"
pg_fsync_test résultat:
C:\temp>"C:\Program Files\PostgreSQL.3\bin\pg_test_fsync"
5 seconds per test
O_DIRECT supported on this platform for open_datasync and open_sync.
Compare file sync methods using one 8kB write:
(in wal_sync_method preference order, except fdatasync
is Linux's default)
open_datasync 81199.920 ops/sec 12 usecs/op
fdatasync n/a
fsync 45.337 ops/sec 22057 usecs/op
fsync_writethrough 46.470 ops/sec 21519 usecs/op
open_sync n/a
Compare file sync methods using two 8kB writes:
(in wal_sync_method preference order, except fdatasync
is Linux's default)
open_datasync 41093.981 ops/sec 24 usecs/op
fdatasync n/a
fsync 38.569 ops/sec 25927 usecs/op
fsync_writethrough 36.970 ops/sec 27049 usecs/op
open_sync n/a
Compare open_sync with different write sizes:
(This is designed to compare the cost of writing 16kB
in different write open_sync sizes.)
1 * 16kB open_sync write n/a
2 * 8kB open_sync writes n/a
4 * 4kB open_sync writes n/a
8 * 2kB open_sync writes n/a
16 * 1kB open_sync writes n/a
Test if fsync on non-write file descriptor is honored:
(If the times are similar, fsync() can sync data written
on a different descriptor.)
write, fsync, close 45.564 ops/sec 21947 usecs/op
write, close, fsync 33.373 ops/sec 29964 usecs/op
Non-Sync'ed 8kB writes:
write 889.800 ops/sec 1124 usecs/op
source d'informationauteur alice
Vous devez vous connecter pour publier un commentaire.
Vous avez raison d'être méfiant. PostgreSQL avec les paramètres que vous indiquez ne devrait pas être en mesure d'effectuer quelque chose près de 1500 mises à jour séparées séquentielle de transactions par seconde de support en rotation.
Probablement quelque chose dans votre pile e /s est couché ou buggy sur la façon dont il met en œuvre la synchronisation. Ce qui signifie que vos données sont à risque de corruption graves après une panne de courant inattendue ou OS échec.
En regardant le résultat de pg_test_fsync, c'est effectivement le cas. open_datasync, qui est la valeur par défaut sous Windows, semble être trop rapide et qui doit donc être dangereux. Je vois la même chose quand je lance pg_test_fsync sur mon Windows7 machine.
Il se décompose de la façon dont ils mettent en œuvre l'isolement d'instantané.
SQLite utilise le verrouillage de fichier comme un moyen d'isoler les transactions, permettant écrit à frapper qu'une seule fois, toutes les lectures sont faites.
Postgres, en revanche, utilise une approche plus sophistiquée appelée multiconcurrency de contrôle de version (mvcc), qui permet à de multiples écritures en parallèle, avec plusieurs lectures.
http://www.sqliteconcepts.org/SI_index.html
http://www.postgresql.org/docs/current/static/mvcc-intro.html
http://wiki.postgresql.org/wiki/MVCC
Denis répondre a tous les liens dont vous avez besoin. Je vais passer pour un moins détaillée mais possbly plus compréhensible réponse.
Sqlite ne pas utiliser sophistiqué gestionnaire de transactions, il n'y a pas avancé multitâche logique cachée en elle. Il exécute ce que vous dites, ce qu'il exécute, exactement dans cet ordre. En d'autres termes: c'est exactement ce que vous lui dites de faire. Si vous avez essayé d'utiliser la même base de données à partir de deux processus - vous rencontrez des problèmes.
PostgreSQL, d'autre part, est très complexe de la base de données: il soutient efficacement simultané de plusieurs lectures et les écritures. Pensez-y comme un système asynchrone - vous planifier les travaux à faire, vous n'avez pas à contrôler dans ses détails - Postgres-t-il pour vous.
Quoi faire avec votre efficacité? Joignez-vous à plusieurs - plusieurs dizaines - des centaines de mises à jour/insère dans une seule transaction. Pour un tableau simple, vous obtiendrez une très bonne performance.
En fait, tout écrire sur un disque en rotation est de l'ordre de grandeur de 10 ms (typique nombre est de 8 ms).
Cela signifie un peu plus de 100 écritures par seconde, SI vous écrivez la même position dans le disque, ce qui est très étrange cas pour une base de données. Voir "you don't know jack sur les disques" de l'ACM, en général, un disque peut l'annexe 10 lit ou écrit en une seule rotation.
http://queue.acm.org/detail.cfm?id=864058
Ainsi, une base de données peut s'effectuer de 1 000 écritures par seconde, et même plus. J'ai vu des applications performantes de 1 500 transactions par seconde il y a 10 ans dans les ordinateurs de bureau.
En supposant que vous utilisez un disque dur normal (pas de ssd), vous pouvez s'attendre à un maximum de 50 à 100 écritures par seconde. Il semble que de 15 écrit par seconde est un peu faible, mais pas impossible.
Donc si Postgres est en train de faire 1500 mises à jour par seconde, ils sont écrits à la mémoire tampon/cache ou regroupées en une seule mise à jour. Sans en savoir plus sur le test, il est difficile de dire quelle est la vraie raison mais si vous ouvrez une opération, mettre à jour une ligne unique de 1500 fois et de s'engager après ce que Postgres devrait être assez intelligent pour exécuter uniquement une seule "vraie" écrire sur le disque.