SQLite es una base de datos relacional popular que usted integra en su aplicación. Con una cantidad cada vez mayor de datos en su base de datos, debe aplicar el ajuste de rendimiento de SQLite. En este artículo se analizan los índices y sus peligros, el uso del planificador de consultas, el modo de diario Write-Ahead-Logging (WAL) y el aumento del tamaño de la memoria caché. También explica la importancia de medir el impacto de tus ajustes mediante pruebas automatizadas.
Introducción
SQLite es un popular sistema de base de datos relacional (DB) . A diferencia de sus hermanos mayores basados en cliente-servidor, como MySQL, SQLite se puede integrar en su aplicación como una biblioteca . SQLite tiene un conjunto de funciones muy similar y también puede manejar millones de filas, dado que conoce algunos consejos y trucos sobre el ajuste del rendimiento. Como se mostrará en las siguientes secciones, hay más que saber sobre el ajuste del rendimiento de SQLite que solo crear índices.
Crear índices, pero con precaución
La idea básica de un índice es acelerar la lectura de datos específicos , es decir, SELECT
declaraciones con WHERE
cláusula. Los índices también aceleran la clasificación datos (ORDER BY
), o JOIN
mesas de trabajo. Lamentablemente, los índices son un arma de doble filo, ya que consumen espacio adicional en el disco y ralentizan la manipulación de datos (INSERT
, UPDATE
, DELETE
).
El consejo general es crear la menor cantidad de índices posible, pero tantos como sea necesario . Además, los índices solo tienen sentido para grandes bases de datos, con miles o millones de filas.
Utilice el planificador de consultas para analizar sus consultas
La forma en que SQLite usa los índices internamente está documentada, pero no es muy fácil de entender. Como se explica más detalladamente en este artículo, es una buena idea analizar una consulta prefijándola con EXPLAIN QUERY PLAN
. Eche un vistazo a cada línea de salida, de las cuales hay tres variantes básicas:
SEARCH table ...
las líneas son una buena señal:¡SQLite usa uno de sus índices!SCAN table ... USING INDEX
es mala señal,SCAN table ...
es aún peor!
Trate de evitar SCAN table [using index]
entradas en la salida de EXPLAIN QUERY PLAN
siempre que sea posible, porque se encontrará con problemas de rendimiento en bases de datos más grandes. Utilice EXPLAIN QUERY PLAN
para agregar iterativamente o modificar sus índices hasta que no haya más SCAN table
aparecen las entradas.
Optimizar consultas que implican IS NOT
Comprobar IS NOT ...
es caro porque SQLite tendrá que escanear todas las filas de la tabla, incluso si la columna afectada tiene un índice . Los índices solo son útiles si busca valores específicos, es decir, comparaciones que involucran < (más pequeño), > (mayor), o = (igual), pero no aplican para !=(desigual).
Un pequeño truco es que puedes reemplazar WHERE column != value
con WHERE column > value OR column < value
. Esto usará el índice de la columna y afectará efectivamente a todas las filas cuyo valor no sea igual a value
. De manera similar, un WHERE stringColumn != ''
puede ser reemplazado por WHERE stringColumn > ''
, porque las cadenas se pueden ordenar. Sin embargo, al aplicar este truco, asegúrese de saber cómo maneja SQLite NULL
comparaciones Por ejemplo, SQLite evalúa NULL > ''
como FALSE
.
Si usa un truco de comparación de este tipo, hay otra advertencia en caso de que su consulta contenga WHERE
y ORDER BY
, cada uno con una columna diferente:esto hará que la consulta vuelva a ser ineficaz. Si es posible, use el mismo columna en WHERE
y ORDER BY
o crea un índice de cobertura que involucra tanto el WHERE
y ORDER BY
columna.
Mejore la velocidad de escritura con Write-Ahead-Log
El registro de escritura anticipada (WAL) el modo diario mejora significativamente el rendimiento de escritura/actualización , en comparación con la reversión predeterminada modo diario. Sin embargo, como se documenta aquí, hay algunas advertencias . Por ejemplo, el modo WAL no está disponible en ciertos sistemas operativos. Además, existen garantías de consistencia de datos reducidas en caso de falla del hardware . Asegúrese de leer las primeras páginas para comprender lo que está haciendo.
Encontré que el comando PRAGMA synchronous = NORMAL
proporciona una aceleración de 3-4x. Configuración de journal_mode
a WAL
luego vuelve a mejorar significativamente el rendimiento (aproximadamente 10 veces o más, según el sistema operativo).
Además de las advertencias que ya mencioné, también debe tener en cuenta lo siguiente:
- Usando el modo diario WAL, habrá dos archivos adicionales al lado del archivo de la base de datos en su sistema de archivos, que tienen el mismo nombre que la base de datos, pero con el sufijo "-shm" y "-wal". Normalmente no necesita preocuparse, pero si enviara la base de datos a otra máquina mientras su aplicación se está ejecutando, no olvide incluir esos dos archivos. SQLite compactará estos dos archivos en el archivo principal cada vez que cierre todas las conexiones de bases de datos abiertas.
- El rendimiento de inserción o actualización disminuirá ocasionalmente, cada vez que la consulta desencadene la fusión del contenido del archivo de registro WAL en el archivo de la base de datos principal. Esto se llama puntos de control , ver aquí.
- Encontré que
PRAGMA
s que cambianjournal_mode
ysynchronous
no parecen estar persistentemente almacenados en la base de datos. Por lo tanto, siempre volver a ejecutarlos cada vez que abra una nueva conexión a la base de datos, en lugar de simplemente ejecutarlos al crear las tablas por primera vez.
Mide todo
Siempre que agregue ajustes de rendimiento, asegúrese de medir el impacto. Las pruebas (unitarias) automatizadas son un gran enfoque para esto. Ayudan a documentar sus hallazgos para su equipo, y ellos descubrirán comportamientos desviados con el tiempo , p.ej. cuando actualiza a una versión más nueva de SQLite. Ejemplos de lo que puede medir:
- ¿Cuál es el efecto de usar el WAL modo diario sobre el rollback ¿modo? ¿Cuál es el efecto de otros
PRAGMA
(supuestamente) que mejoran el rendimiento? s? - Una vez que agrega/cambia/elimina un índice, ¿qué tan rápido
SELECT
declaraciones se convierten? ¿Cuánto más lentoINSERT/DELETE/UPDATE
declaraciones se convierten? - ¿Cuánto espacio de disco adicional consumen los índices?
Para cualquiera de estas pruebas, considere repetirlas con diferentes tamaños de bases de datos. P.ej. ejecútelos en una base de datos vacía y también en una base de datos que ya contiene miles (o millones) de entradas. También debe ejecutar las pruebas en diferentes dispositivos y sistemas operativos, especialmente si su entorno de desarrollo y producción es sustancialmente diferente.
Ajuste el tamaño de caché
SQLite almacena información temporal en un caché (en la RAM), p. mientras construye los resultados de un SELECT
consulta, o al manipular datos que aún no se han confirmado. Por defecto, este tamaño es de unos míseros 2 MB . Las máquinas de escritorio modernas pueden ahorrar mucho más. Ejecute PRAGMA cache_size = -kibibytes
para aumentar este valor (Cuidado con el menos firmar delante del valor!). Consulte aquí para obtener más información. De nuevo, medir ¡Qué impacto tiene esta configuración en el rendimiento!
Utilice REPLACE INTO para crear o actualizar una fila
Esto puede no ser tanto un ajuste de rendimiento como un pequeño truco. Supongamos que necesita actualizar una fila en la tabla t
o crear una fila si aún no existe. En lugar de usar dos consultas (SELECT
seguido de INSERT
o UPDATE
), use el REPLACE INTO
(documentos oficiales).
Para que esto funcione, agregue una columna ficticia adicional (por ejemplo, replacer
) a la tabla t
, que tiene un UNIQUE
constreñir. La declaración de la columna podría, p. ser ... replacer INTEGER UNIQUE ...
eso es parte de tu CREATE TABLE
declaración. Luego use una consulta como
REPLACE INTO t (col1, col2, ..., replacer) VALUES (?,?,...,1)
Code language: SQL (Structured Query Language) (sql)
Cuando esta consulta se ejecuta por primera vez, simplemente realizará un INSERT
. Cuando se ejecuta por segunda vez, UNIQUE
restricción del replacer
se activará y el comportamiento de resolución de conflictos hará que se elimine la fila anterior, creando una nueva automáticamente. También puede encontrar útil el comando UPSERT relacionado.
Conclusión
Una vez que crece el número de filas en su base de datos, los ajustes de rendimiento se vuelven una necesidad. Los índices son la solución más común. Cambian la complejidad del tiempo mejorada por la complejidad del espacio reducida, mejorando las velocidades de lectura, mientras afectan negativamente el rendimiento de la modificación de datos. R He demostrado que debe tener mucho cuidado al comparar desigualdad en SELECT
declaraciones, porque SQLite no puede usar índices para este tipo de comparaciones. En general, recomiendo usar el planificador de consultas eso explica lo que sucede internamente para cada consulta SQL. Cada vez que modifiques algo, ¡mide el impacto!