¿Cuáles son las características de rendimiento de sqlite con archivos de base de datos muy grandes? [cerrado]

325

Sé que sqlite no funciona bien con archivos de bases de datos extremadamente grandes, incluso cuando son compatibles (solía haber un comentario en el sitio web de sqlite que indica que si necesita archivos de más de 1 GB, puede considerar usar un rdbms empresarial. No lo encuentre más, podría estar relacionado con una versión anterior de sqlite).

Sin embargo, para mis propósitos, me gustaría tener una idea de lo malo que es realmente antes de considerar otras soluciones.

Estoy hablando de archivos de datos sqlite en el rango de varios gigabytes, desde 2 GB en adelante. Alguien tiene alguna experiencia con esto? ¿Algún consejo / idea?

Snazzer
fuente
1
El uso de roscar (conexión por hilo) podría ayudar sólo para lectura - stackoverflow.com/a/24029046/743263
Malkia
23
Año 2016: tengo una base de datos de 5 GB que se ejecuta en SQLite sin problemas. Instalé exactamente el mismo conjunto de datos en Postgres. SQLite ejecutó una consulta compleja en 2.7 ms, Postgres en 2.5 ms. Terminé en Postgres para un acceso Regex más fácil y mejores características de índice. Pero SQLite me impresionó y podría haberlo usado también.
Paulb

Respuestas:

246

Así que hice algunas pruebas con sqlite para archivos muy grandes y llegué a algunas conclusiones (al menos para mi aplicación específica).

Las pruebas implican un único archivo sqlite con una sola tabla o varias tablas. Cada tabla tenía aproximadamente 8 columnas, casi todos enteros, y 4 índices.

La idea era insertar suficientes datos hasta que los archivos sqlite tuvieran aproximadamente 50 GB.

Mesa individual

Traté de insertar varias filas en un archivo sqlite con solo una tabla. Cuando el archivo tenía aproximadamente 7 GB (lo siento, no puedo ser específico sobre el recuento de filas) las inserciones tardaban demasiado. Había estimado que mi prueba para insertar todos mis datos tardaría aproximadamente 24 horas, pero no se completó incluso después de 48 horas.

Esto me lleva a concluir que una sola tabla sqlite muy grande tendrá problemas con las inserciones, y probablemente también con otras operaciones.

Supongo que esto no es una sorpresa, ya que la tabla se hace más grande, la inserción y actualización de todos los índices lleva más tiempo.

Tablas Múltiples

Luego intenté dividir los datos por tiempo en varias tablas, una tabla por día. Los datos para la tabla 1 original se dividieron en ~ 700 tablas.

Esta configuración no tuvo problemas con la inserción, no tardó más a medida que avanzaba el tiempo, ya que se creó una nueva tabla para cada día.

Problemas de vacío

Como lo señaló i_like_caffeine, el comando VACUUM es un problema cuanto más grande es el archivo sqlite. A medida que se realizan más inserciones / eliminaciones, la fragmentación del archivo en el disco empeorará, por lo que el objetivo es VACÍO periódicamente para optimizar el archivo y recuperar el espacio de archivo.

Sin embargo, como se señala en la documentación , se realiza una copia completa de la base de datos para hacer un vacío, lo que lleva mucho tiempo completarla. Por lo tanto, cuanto más pequeña sea la base de datos, más rápida será esta operación.

Conclusiones

Para mi aplicación específica, probablemente dividiré los datos en varios archivos db, uno por día, para obtener el mejor rendimiento de vacío y la velocidad de inserción / eliminación.

Esto complica las consultas, pero para mí, es una compensación valiosa poder indexar esta cantidad de datos. Una ventaja adicional es que solo puedo eliminar un archivo db completo para eliminar el valor de un día de datos (una operación común para mi aplicación).

Probablemente también tenga que monitorear el tamaño de la tabla por archivo para ver cuándo la velocidad se convertirá en un problema.

Es una pena que no parece haber un método de vacío incremental que no sea el vacío automático . No puedo usarlo porque mi objetivo para el vacío es desfragmentar el archivo (el espacio de archivo no es un gran problema), lo que el vacío automático no hace. De hecho, la documentación indica que puede empeorar la fragmentación, por lo que tengo que recurrir a hacer un vacío completo en el archivo periódicamente.

Snazzer
fuente
55
Información muy útil Pura especulación, pero me pregunto si la nueva API de copia de seguridad se puede utilizar para crear una versión no fragmentada de su base de datos a diario y evitar la necesidad de ejecutar una VACÍO.
eodonohoe
24
Tengo curiosidad, ¿estaban todos sus INSERTOS en una transacción?
Paul Lefebvre
99
Sí, las inserciones se realizaron en lotes de 10000 mensajes por transacción.
Snazzer
66
¿Qué sistema de archivos usaste? Si ext {2,3,4}, ¿cuál fue la configuración de datos =, fue el diario habilitado? Además de los patrones io, la forma en que sqlite se descarga en el disco puede ser significativa.
Tobu
55
Estaba probando principalmente en Windows, así que no puedo comentar sobre el comportamiento en Linux.
Snazzer
169

Estamos utilizando DBS de 50 GB + en nuestra plataforma. sin quejas funciona muy bien. ¡Asegúrate de estar haciendo todo bien! ¿Estás usando declaraciones predefinidas? * SQLITE 3.7.3

  1. Actas
  2. Declaraciones pre hechas
  3. Aplique esta configuración (justo después de crear la base de datos)

    PRAGMA main.page_size = 4096;
    PRAGMA main.cache_size=10000;
    PRAGMA main.locking_mode=EXCLUSIVE;
    PRAGMA main.synchronous=NORMAL;
    PRAGMA main.journal_mode=WAL;
    PRAGMA main.cache_size=5000;
    

Espero que esto ayude a otros, funciona muy bien aquí

Alex
fuente
22
Recientemente probado con dbs en el rango de 160 GB, funciona muy bien también.
Snazzer
10
También PRAGMA main.temp_store = MEMORY;.
Vikrant Chaudhary
40
@Alex, ¿por qué hay dos PRAGMA main.cache_size = 5000 ;?
Jack
23
No solo aplique ciegamente estas optimizaciones. En particular, synous = NORMAL no es a prueba de choques. Es decir, un bloqueo del proceso en el momento adecuado puede dañar su base de datos incluso en ausencia de fallas de disco. sqlite.org/pragma.html#pragma_synchronous
mpm
22
@Alex, ¿puede explicar esos valores y la diferencia entre ellos y los valores predeterminados?
4m1nh4j1
65

He creado bases de datos SQLite de hasta 3.5 GB de tamaño sin problemas de rendimiento notables. Si no recuerdo mal, creo que SQLite2 podría haber tenido algunos límites inferiores, pero no creo que SQLite3 tenga tales problemas.

Según la página de Límites de SQLite , el tamaño máximo de cada página de base de datos es de 32K. Y el máximo de páginas en una base de datos es 1024 ^ 3. Entonces, según mis cálculos, eso llega a 32 terabytes como el tamaño máximo. ¡Creo que alcanzarás los límites de tu sistema de archivos antes de llegar a los de SQLite!

Paul Lefebvre
fuente
3
Dependiendo de las operaciones que esté realizando, tratando de eliminar 3000 filas en una base de datos de 8G sqlite, le toma suficiente tiempo preparar una buena olla de prensa francesa, jajaja
benjaminz
44
@benjaminz, debes estar haciéndolo mal. Si ajusta la eliminación de 3k filas en una transacción, debería ser casi instantánea. Yo mismo cometí este error: eliminar 10k filas una por una tomó 30 minutos. Pero una vez que envolví todas las declaraciones de eliminación en una transacción, tardé 5 segundos.
mvp
55

Gran parte de la razón por la que tardó más de 48 horas en hacer sus inserciones se debe a sus índices. Es increíblemente más rápido:

1 - Descartar todos los índices 2 - Hacer todas las inserciones 3 - Crear índices nuevamente

usuario352992
fuente
23
Eso es bien sabido ... pero para un proceso de larga duración no va a soltar periódicamente sus índices para reconstruirlos, especialmente cuando los va a consultar para que funcionen. Ese es el enfoque que se está adoptando, sin embargo, cuando el sqlite db tiene que ser reconstruido desde cero, los índices se crean después de que se realizan todas las inserciones.
Snazzer
24
@Snazzer en una situación similar utilizamos una tabla de "acumulador": una vez al día, moveríamos las filas acumuladas de la tabla de acumulador a la tabla principal en una sola transacción. Cuando fue necesario, una vista se encargó de presentar ambas tablas como una sola tabla.
CAFxX
44
Otra opción es mantener los índices, pero ordenar previamente los datos en orden de índice antes de insertarlos.
Steven Kryskalla
1
@StevenKryskalla, ¿cómo se compara eso con soltar los índices y recrearlos? ¿Algún enlace que conozcas que haya sido comparado?
mcmillab
1
@mcmillab Esto fue hace años, así que no recuerdo todos los detalles o las estadísticas de referencia, pero pensar intuitivamente, insertar N elementos ordenados aleatoriamente en un índice llevará O (NlogN) tiempo, mientras que insertar N elementos ordenados llevará O (N ) hora.
Steven Kryskalla
34

Además de la recomendación habitual:

  1. Índice de caída para inserción masiva.
  2. Inserciones / actualizaciones por lotes en grandes transacciones.
  3. Ajuste su caché de búfer / deshabilite journal / w PRAGMAs.
  4. Use una máquina de 64 bits (para poder usar mucho caché ™).
  5. [agregado en julio de 2014] ¡Use la expresión de tabla común (CTE) en lugar de ejecutar múltiples consultas SQL! Requiere el lanzamiento de SQLite 3.8.3.

He aprendido lo siguiente de mi experiencia con SQLite3:

  1. Para obtener la máxima velocidad de inserción, no use el esquema con ninguna restricción de columna. (Altere la mesa más tarde según sea necesario No puede agregar restricciones con ALTER TABLE).
  2. Optimice su esquema para almacenar lo que necesita. A veces esto significa desglosar tablas y / o incluso comprimir / transformar sus datos antes de insertarlos en la base de datos. Un gran ejemplo es almacenar direcciones IP como enteros (largos).
  3. Una tabla por archivo db, para minimizar la contención de bloqueo. (Use ATTACH DATABASE si desea tener un único objeto de conexión.
  4. SQLite puede almacenar diferentes tipos de datos en la misma columna (escritura dinámica), utilícelo para su ventaja.

Pregunta / comentario de bienvenida. ;-)

Lester Cheung
fuente
1
¿Qué impacto obtiene de 'una tabla por archivo db'? Suena interesante. ¿Crees que importaría mucho si tu tabla solo tiene 3 tablas y se está construyendo desde cero?
Martin Velez
44
@martin odio decirlo, pero la respuesta es que depende . La idea es dividir los datos en un tamaño manejable. En mi caso de uso, recopilo datos de diferentes hosts y hago informes sobre los datos después del hecho, por lo que este enfoque funcionó bien. La partición por fecha / hora, según lo sugerido por otros, debería funcionar bien para los datos que abarcan un largo período de tiempo, me imagino.
Lester Cheung
3
@Lester Cheung: Respecto a su segundo n. ° 1: Según los documentos y la experiencia personal, hasta el día de hoy, SQLite3 no admite agregar restricciones con ALTER TABLE después de la creación de la tabla. La única forma de agregar o eliminar restricciones de las filas de la tabla existente es crear una nueva tabla con las características deseadas y copiar todas las filas, lo que probablemente sea mucho más lento que insertar una vez con restricciones.
Mumbleskates
3
@Widdershins tiene toda la razón: ALTER TABLE en SQLite no permite agregar restricciones. No sé qué estaba fumando, actualizaré la respuesta, gracias.
Lester Cheung
Ninguna de esas sugerencias tiene nada que ver con el uso de enormes archivos SQLite db. ¿Se editó la pregunta desde que se envió esta respuesta?
A. Rager
9

Creo que las principales quejas sobre el escalado de sqlite son:

  1. Proceso único de escritura.
  2. Sin reflejo
  3. Sin replicación
Desconocido
fuente
9

Tengo una base de datos SQLite de 7GB. Para realizar una consulta particular con una unión interna se necesitan 2.6s. Para acelerar esto, intenté agregar índices. Dependiendo de qué índice (s) agregué, a veces la consulta bajó a 0.1s y a veces subió a 7s. Creo que el problema en mi caso fue que si una columna está muy duplicada, agregar un índice degrada el rendimiento :(

Mike Oxynormas
fuente
99
¿Por qué una columna con muchos duplicados degradaría el rendimiento (pregunta seria)?
Martin Velez
66
una columna con baja cardinalidad es más difícil de indexar: stackoverflow.com/questions/2113181/…
metrix
9

Solía ​​haber una declaración en la documentación de SQLite de que el límite de tamaño práctico de un archivo de base de datos era unas pocas docenas de GB: s. Esto se debió principalmente a la necesidad de que SQLite "asigne un mapa de bits de páginas sucias" cada vez que inició una transacción. Por lo tanto, se requerían 256 bytes de RAM por cada MB en la base de datos. Insertar en un archivo DB de 50 GB requeriría un fuerte (2 ^ 8) * (2 ^ 10) = 2 ^ 18 = 256 MB de RAM.

Pero a partir de versiones recientes de SQLite, esto ya no es necesario. Lee más aquí .

Alix Axel
fuente
25
Lamento mucho tener que señalar esto, pero 2^18en realidad solo son 256 K.
Gabriel Schreiber
77
@GabrielSchreiber eso, y también el hecho de que 50 GB no son (2 ^ 10) MB, eso es solo 1 GB. Entonces, para una base de datos de 50 GB, necesita 12.5 MB de memoria: (2 ^ 8) * (2 ^ 10) * 50
elipoultorak
8

He experimentado problemas con archivos sqlite grandes cuando uso el comando de vacío.

Todavía no he probado la función auto_vacuum. Si espera actualizar y eliminar datos a menudo, entonces vale la pena ver esto.

eodonohoe
fuente