SQLite es una base de datos relacional embebida que funciona sin un proceso de servidor independiente y almacena toda la base de datos en un solo archivo en el dispositivo. Gracias a la configuración cero, el tamaño reducido de la biblioteca y el soporte completo de SQL, SQLite se ha convertido en el estándar para el almacenamiento local de datos en aplicaciones móviles. Según el SQLite Consortium (2025), este SGBD se utiliza en más de 4 mil millones de dispositivos, incluidos todos los teléfonos inteligentes con iOS y Android.
Puntos clave
SQLite es una biblioteca en lenguaje C que implementa un SGBD relacional sin servidor dedicado. Se integra directamente en la aplicación, leyendo y escribiendo datos en un archivo ordinario del sistema de archivos del dispositivo. El tamaño de la biblioteca es de aproximadamente 600 KB, lo que convierte a SQLite en la base de datos SQL completa más ligera.
SQLite soporta la mayor parte del estándar SQL:1999, incluyendo JOIN, subconsultas, disparadores, vistas, índices y funciones de ventana. Las limitaciones afectan a ALTER TABLE (soporte limitado) y a los RIGHT/FULL OUTER JOIN completos. No obstante, para aplicaciones móviles, la funcionalidad de SQLite es suficiente en el 99% de los casos de almacenamiento local.
Según la encuesta a desarrolladores de Stack Overflow (2025), SQLite es la base de datos más popular para soluciones embebidas y ocupa el tercer lugar en popularidad entre todos los SGBD, después de MySQL y PostgreSQL. En el desarrollo móvil, SQLite se utiliza en cada aplicación, ya sea directamente o a través de envoltorios.
Configuración cero — SQLite no requiere instalación, configuración de permisos, creación de usuarios ni inicio de servicios. La biblioteca se enlaza al proyecto y la base de datos se crea llamando a una sola función. Esto simplifica radicalmente el despliegue en comparación con los SGBD cliente-servidor, que requieren instalación del servidor, configuración de puertos y creación de usuarios.
El archivo de base de datos SQLite es un archivo multiplataforma ordinario que se puede copiar, analizar, enviar por red o restaurar desde una copia de seguridad. El formato del archivo es estable a nivel de API: los archivos SQLite 3 creados en 2004 se abren con la versión actual de la biblioteca, lo que garantiza la compatibilidad de datos a largo plazo.
La arquitectura de SQLite consta de ocho máquinas virtuales: Tokenizer, Parser, Code Generator, VM, B-Tree, Pager, OS Interface y Utilities. Una consulta SQL pasa por el Tokenizer (división en tokens), el Parser (construcción de un AST), el Code Generator (conversión a bytecode) y se ejecuta en la máquina virtual, que lee las páginas de datos a través de B-Tree y Pager.
SQLite utiliza B-Tree para almacenar tablas e índices. Cada tabla se almacena como un B-Tree independiente, donde los nodos hoja contienen las filas de datos. Los índices también se almacenan como B-Tree, pero con claves en las hojas. Pager gestiona la carga de páginas (por defecto 4096 bytes) desde el archivo a la memoria, proporcionando transacciones ACID mediante un diario o WAL.
WAL (Write-Ahead Logging) es el modo recomendado para aplicaciones móviles. Los cambios se escriben primero en un archivo WAL independiente y luego se trasladan periódicamente a la base de datos principal. WAL permite leer (datos antiguos) y escribir (a través de WAL) simultáneamente en la base de datos, lo que mejora el rendimiento de las aplicaciones multihilo. El diario estándar (rollback journal) bloquea la lectura durante la escritura.
| Parámetro | Rollback Journal | WAL (Write-Ahead Logging) |
|---|---|---|
| Lectura durante escritura | Bloqueada | Permitida (lee datos antiguos) |
| Rendimiento de escritura | Medio | Alto (escritura secuencial en WAL) |
| Consumo de disco | Menor (solo diario de reversión) | Mayor (WAL + base de datos principal) |
| Recuperación ante fallos | Reversión al último punto de control | Recuperación desde WAL (sin pérdida de datos) |
| Recomendación | Para escenarios monohilo | Para aplicaciones móviles típicas |
El cambio entre modos se realiza con una sola consulta SQL: PRAGMA journal_mode=WAL. Para aplicaciones móviles con sincronización en segundo plano y un hilo de UI que lee datos simultáneamente, WAL ofrece un mejor rendimiento y ausencia de bloqueos de interfaz.
SQLite no es la única opción para el almacenamiento local de datos, pero sí la más versátil. Realm ofrece una mayor velocidad de acceso directo a objetos en memoria, pero utiliza su propio formato NoSQL y tiene un tamaño de biblioteca mayor. Core Data en iOS es una capa ORM sobre SQLite que añade gestión de grafos de objetos y deshacer operaciones.
Para la mayoría de las aplicaciones, SQLite sigue siendo la opción óptima gracias a su rendimiento predecible, ausencia de vendor lock-in y estabilidad probada a lo largo del tiempo. Realm y Core Data se justifican en proyectos con grafos de objetos complejos, consultas reactivas o requisitos de sincronización entre dispositivos.
| Característica | SQLite | Realm | Core Data |
|---|---|---|---|
| Tipo de base de datos | Relacional (SQL) | NoSQL (orientada a objetos) | ORM (sobre SQLite) |
| Tamaño de biblioteca | ~600 KB | ~4 MB | Integrado en SDK de Apple |
| Rendimiento | Medio | Alto (objetos en memoria) | Medio (sobrecarga ORM) |
| Plataformas | iOS, Android, Web, Escritorio | iOS, Android, Node.js | iOS, macOS |
| Vendor lock-in | Ninguno (estándar abierto) | Medio (formato propio) | Alto (solo Apple) |
La elección entre SQLite, Realm y Core Data depende de la plataforma, los requisitos del modelo de objetos y la estrategia de sincronización. Para proyectos multiplataforma (KMP, Flutter), SQLite sigue siendo la única opción universal que funciona en todas las plataformas objetivo sin cambios en el modelo de datos.
Room es una biblioteca de Android Jetpack que proporciona una capa ORM sobre SQLite. Room genera automáticamente consultas SQL a partir de interfaces DAO anotadas, verifica la corrección de las consultas en tiempo de compilación y admite migraciones de base de datos cuando cambia el esquema. Room es la forma recomendada de trabajar con SQLite en Android.
SQLiteOpenHelper es una API de bajo nivel para la gestión directa de SQLite sin ORM. La clase gestiona la creación, apertura y actualización de la base de datos. SQLiteOpenHelper es adecuado para proyectos con consultas SQL sencillas o cuando se necesita un control total sobre la lógica SQL sin la abstracción de Room.
Una entidad en Room se anota con @Entity, y un DAO con @Dao. Room traduce los métodos anotados en consultas SQL: @Insert genera INSERT, @Query genera SELECT con el SQL especificado. Las migraciones se añaden mediante Migration con las versiones de esquema antigua y nueva especificadas. Room valida el SQL en tiempo de compilación, eliminando errores de sintaxis en producción.
@Entity
data class User(
@PrimaryKey val id: Long,
val name: String,
@ColumnInfo(name = "created_at")
val createdAt: Long
)
@Dao
interface UserDao {
@Query("SELECT * FROM user ORDER BY name ASC")
suspend fun getAllUsers(): List<User>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUser(user: User)
@Query("DELETE FROM user WHERE id = :id")
suspend fun deleteUser(id: Long)
}
Room genera automáticamente la implementación UserDao_Impl, que contiene consultas SQLite en tiempo de ejecución a través del RoomDatabase interno. Gracias a las corrutinas (suspend), los métodos DAO se ejecutan de forma asíncrona en un hilo en segundo plano sin bloquear la UI. Los tipos de retorno Flow en @Query actualizan automáticamente el resultado cuando la tabla cambia.
FMDB es un envoltorio en Objective-C sobre la API C de SQLite, históricamente la primera biblioteca popular para iOS. Proporciona objetos FMDatabase y FMResultSet para ejecutar consultas y obtener resultados. FMDB es simple y minimalista, pero no soporta construcciones específicas de Swift: opcionales, Codable, async/await.
GRDB es una biblioteca moderna en Swift para trabajar con SQLite. Proporciona una API type-safe, soporte para Codable, Combine Publishers, async/await, migraciones y observación de cambios en tiempo real. GRDB es preferible para proyectos nuevos en Swift gracias a su completa integración con Swift Concurrency y una mejor legibilidad del código.
GRDB define tablas mediante clases Record que cumplen los protocolos FetchableRecord y TableRecord. Las consultas se escriben en Swift con sintaxis type-safe en lugar de SQL puro. GRDB también soporta DatabaseMigrator para el versionado de esquemas y migraciones entre versiones de la aplicación.
struct User: Codable, FetchableRecord, TableRecord {
var id: Int64
var name: String
var createdAt: Date
}
let dbPool = try DatabasePool(path: dbPath)
var migrator = DatabaseMigrator()
migrator.registerMigration("v1") { db in
try db.create(table: "user") { t in
t.autoIncrementedPrimaryKey("id")
t.column("name", .text).notNull()
t.column("createdAt", .datetime).notNull()
}
}
let users = try await dbPool.read { db in
try User.order(Column("name")).fetchAll(db)
}
DatabasePool utiliza el modo WAL de SQLite para lectura concurrente. Múltiples lectores pueden acceder a la base de datos simultáneamente mientras un solo escritor actualiza los datos a través de WAL. GRDB gestiona automáticamente las conexiones y transacciones, proporcionando acceso thread-safe a la base de datos desde cualquier hilo sin sincronización manual.
Los índices son la forma más eficaz de acelerar las consultas SQLite. Se crea un índice en las columnas utilizadas en las cláusulas WHERE, JOIN y ORDER BY. Para una tabla con 100000 registros, la búsqueda por una columna indexada toma milisegundos en lugar de segundos. Sin embargo, los índices ralentizan las operaciones INSERT y UPDATE, por lo que su número debe equilibrarse con la frecuencia de escritura.
La inserción por lotes (batch insert) dentro de una sola transacción acelera radicalmente la carga masiva de datos. Insertar 1000 registros uno por uno genera una sobrecarga de ~1 segundo. Los mismos 1000 registros en una sola transacción tardan ~5-10 milisegundos. La diferencia se explica porque cada INSERT individual crea una nueva transacción con escritura síncrona en disco.
PRAGMA son comandos de SQLite para configurar el comportamiento de la biblioteca. Pragmas de optimización clave: PRAGMA synchronous=NORMAL (reduce la frecuencia de fsync), PRAGMA cache_size=-8000 (asigna 8 MB de caché), PRAGMA temp_store=MEMORY (tablas temporales en memoria). Para aplicaciones móviles con grandes volúmenes de datos, la combinación de estas pragmas acelera las consultas entre 2 y 3 veces.
Otra optimización importante es la precompilación de consultas SQL (prepared statements). Si una consulta se ejecuta repetidamente (por ejemplo, insertando 10000 filas), compilar SQL una vez y reutilizar la sentencia reduce la carga de CPU en un 30-50%. Room y GRDB almacenan en caché automáticamente los prepared statements, pero al usar la API C de SQLite directamente, la compilación debe realizarse manualmente.
class UserRepository(private val db: RoomDatabase) {
suspend fun insertBatch(users: List<User>) {
db.withTransaction {
users.chunked(500).forEach { batch ->
batch.forEach { user ->
insertUser(user)
}
}
}
}
}
La inserción por lotes con withTransaction garantiza que todas las operaciones INSERT se ejecuten dentro de una sola transacción. La división en sublotes (chunked) evita que una sola transacción sea demasiado grande, lo que podría bloquear otros hilos durante un período prolongado. Para la sincronización en segundo plano, un tamaño de sublote de 500 registros proporciona el equilibrio óptimo entre velocidad y capacidad de respuesta de la UI.
Preguntas frecuentes
Sí, SQLite admite acceso multihilo en modo WAL. Varios hilos pueden leer datos simultáneamente, pero solo uno puede escribir. Room y GRDB gestionan la sincronización automáticamente. En modo rollback journal (predeterminado), la base de datos se bloquea completamente durante cualquier operación de escritura.
El límite de SQLite es de 281 TB (máximo teórico). En la práctica, el tamaño de la base de datos está limitado por la memoria disponible del dispositivo. Para aplicaciones móviles, un tamaño cómodo es de hasta 1-2 GB. Las bases de datos de más de 2 GB ralentizan las copias de seguridad, las actualizaciones a través de App Store y aumentan el consumo de RAM.
SQLite no cifra los datos por defecto: cualquier proceso con acceso al archivo puede leerlos. Para el cifrado, utilice SQLCipher (extensión con AES-256), Room con EncryptedDatabase (Android) o Encrypted Core Data en iOS. El cifrado añade un 5-15% de sobrecarga a las operaciones de lectura y escritura de datos.
SQLite es una biblioteca embebida que no requiere un proceso de servidor. MySQL es un SGBD cliente-servidor con un servidor independiente, usuarios, derechos de acceso y un protocolo de red. SQLite almacena la base de datos en un solo archivo; MySQL la almacena en varios archivos gestionados por el servidor. SQLite es más simple y ligero; MySQL es más potente y escalable.
Para la migración de SQLite, use ALTER TABLE (añadir columnas) o cree una nueva tabla con traslado de datos y eliminación de la tabla antigua. Room automatiza este proceso mediante clases Migration: especifique startVersion, endVersion y las consultas SQL para los cambios de esquema. GRDB y FMDB proporcionan DatabaseMigrator similares.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también