Lightning Memory-Mapped Database ( LMDB ) es una base de datos transaccional integrada en forma de un almacén de clave-valor . LMDB está escrita en C con enlaces API para varios lenguajes de programación . LMDB almacena pares de clave/datos arbitrarios como matrices de bytes, tiene una capacidad de búsqueda basada en rangos, admite múltiples elementos de datos para una sola clave y tiene un modo especial para agregar registros (MDB_APPEND) sin verificar la consistencia. [1] LMDB no es una base de datos relacional , es estrictamente un almacén de clave-valor como Berkeley DB y DBM.
LMDB también se puede utilizar simultáneamente en un entorno multiproceso o multiprocesamiento, con un rendimiento de lectura que escala linealmente por diseño. Las bases de datos LMDB pueden tener solo un escritor a la vez, sin embargo, a diferencia de muchas bases de datos de clave-valor similares, las transacciones de escritura no bloquean a los lectores, ni los lectores bloquean a los escritores. LMDB también es inusual en el sentido de que varias aplicaciones en el mismo sistema pueden abrir y usar simultáneamente el mismo almacén LMDB, como un medio para aumentar el rendimiento. Además, LMDB no requiere un registro de transacciones (lo que aumenta el rendimiento de escritura al no tener que escribir datos dos veces) porque mantiene la integridad de los datos de manera inherente por diseño.
El diseño de LMDB se analizó por primera vez en una publicación de 2009 en la lista de correo para desarrolladores de OpenLDAP [2] , en el contexto de la exploración de soluciones a la dificultad de gestión de caché causada por la dependencia del proyecto de Berkeley DB . Un objetivo específico era reemplazar las múltiples capas de configuración y almacenamiento en caché inherentes al diseño de Berkeley DB con una única caché administrada automáticamente bajo el control del sistema operativo host .
Posteriormente comenzó el desarrollo, inicialmente como una bifurcación de una implementación similar del proyecto ldapd de OpenBSD. [3] La primera versión disponible públicamente apareció en el repositorio de código fuente de OpenLDAP en junio de 2011. [4]
El proyecto se conoció como MDB hasta noviembre de 2012, después de lo cual se le cambió el nombre para evitar conflictos con el software existente. [5]
Internamente, LMDB utiliza estructuras de datos de árbol B+ . La eficiencia de su diseño y su pequeño tamaño tuvieron el efecto secundario no deseado de proporcionar también un buen rendimiento de escritura. LMDB tiene una API similar a Berkeley DB y dbm . LMDB trata la memoria de la computadora como un único espacio de direcciones, compartido entre múltiples procesos o subprocesos utilizando memoria compartida con semántica de copia en escritura (conocida históricamente como un almacenamiento de un solo nivel ). La mayoría de las arquitecturas informáticas modernas anteriores tenían un espacio de direcciones de memoria de 32 bits, lo que imponía un límite estricto de 4 GB al tamaño de cualquier base de datos que se mapeara directamente en un almacenamiento de un solo nivel. Sin embargo, los procesadores de 64 bits actuales ahora implementan principalmente espacios de direcciones de 48 bits, lo que brinda acceso a direcciones de 47 bits o 128 TB de tamaño de base de datos, [6] lo que hace que las bases de datos que usan memoria compartida sean útiles una vez más en aplicaciones del mundo real.
Las características técnicas específicas destacables de LMDB son:
El formato de archivo de LMDB, a diferencia del de Berkeley DB , depende de la arquitectura. Esto significa que se debe realizar una conversión antes de mover una base de datos de una máquina de 32 bits a una de 64 bits, [8] o entre computadoras con diferente orden de bytes . [9]
LMDB emplea el control de concurrencia de múltiples versiones (MVCC) y permite que múltiples subprocesos dentro de múltiples procesos coordinen el acceso simultáneo a una base de datos. Los lectores escalan linealmente por diseño. [10] [11] Mientras que las transacciones de escritura se serializan globalmente a través de un mutex , las transacciones de solo lectura operan en paralelo, incluso en presencia de una transacción de escritura. No requieren espera alguna, excepto la primera transacción de solo lectura en un subproceso. Cada subproceso que lee de una base de datos obtiene la propiedad de un elemento en una matriz de memoria compartida, que puede actualizar para indicar cuándo está dentro de una transacción. Los escritores escanean la matriz para determinar la versión de base de datos más antigua que la transacción debe preservar sin requerir sincronización directa con lectores activos.
En 2011, Google publicó un software que permitía a los usuarios generar micro-benchmarks comparando el rendimiento de LevelDB con SQLite y Kyoto Cabinet en diferentes escenarios. [12] En 2012, Symas agregó soporte para LMDB y Berkeley DB e hizo que el software de evaluación comparativa actualizado estuviera disponible públicamente. [13] Los puntos de referencia resultantes mostraron que LMDB superó a todas las demás bases de datos en operaciones de lectura y escritura por lotes. SQLite con LMDB se destacó en operaciones de escritura, y particularmente en escrituras sincrónicas/transaccionales.
Los puntos de referencia mostraron que el sistema de archivos subyacente tiene una gran influencia en el rendimiento. JFS con un diario externo funciona bien, especialmente en comparación con otros sistemas modernos como Btrfs y ZFS . [14] [15] Zimbra ha probado el rendimiento de back-mdb vs back-hdb en OpenLDAP, con LMDB superando claramente al back-hdb basado en BDB. [16] Muchos otros usuarios de OpenLDAP han observado beneficios similares. [17]
Desde el trabajo de evaluación comparativa inicial realizado en 2012, se han realizado múltiples pruebas de seguimiento con motores de base de datos adicionales para cargas de trabajo en memoria [18] y en disco [19] que caracterizan el rendimiento en múltiples CPU y tamaños de registro. Estas pruebas muestran que el rendimiento de LMDB es inigualable en todas las cargas de trabajo en memoria y se destaca en todas las cargas de trabajo de lectura y escritura en disco que utilizan tamaños de registro grandes. El código del controlador de evaluación comparativa se publicó posteriormente en GitHub [20] y se amplió aún más en la cobertura de la base de datos.
LMDB fue diseñado para resistir la pérdida de datos en caso de fallas del sistema y de las aplicaciones. Su método de copia en escritura nunca sobrescribe los datos que se encuentran en uso. Evitar las sobrescrituras significa que la estructura en el disco/almacenamiento siempre es válida, por lo que las fallas de la aplicación o del sistema nunca pueden dejar la base de datos en un estado corrupto. En su modo predeterminado, en el peor de los casos, una falla puede perder datos de la última transacción de escritura aún no confirmada. Incluso con todos los modos asincrónicos habilitados, es solo un evento de falla catastrófica del sistema operativo o pérdida de energía del hardware [21] en lugar de simplemente una falla de la aplicación que podría potencialmente resultar en una corrupción de datos.
Dos artículos académicos del Simposio USENIX OSDI [22] cubrieron los modos de falla de los motores de bases de datos (incluido LMDB) bajo una pérdida repentina de energía o un bloqueo del sistema. [23] [24] El artículo de Pillai et al., no encontró ninguna falla en LMDB que pudiera ocurrir en los sistemas de archivos del mundo real considerados; la única falla identificada por el estudio en LMDB solo se relaciona con sistemas de archivos hipotéticos. [25] El artículo de Mai Zheng et al. afirma señalar fallas en LMDB, pero la conclusión depende de si se utiliza fsync o fdatasync. El uso de fsync mejora el problema. La selección de fsync sobre fdatasync es un cambio de tiempo de compilación que no es el comportamiento predeterminado en las compilaciones actuales de Linux de LMDB, pero es el predeterminado en macOS, *BSD, Android y Windows. Por lo tanto, las compilaciones predeterminadas de LMDB para Linux son las únicas vulnerables al problema descubierto por los investigadores de zhengmai; sin embargo, los usuarios de Linux pueden simplemente reconstruir LMDB para utilizar fsync en su lugar. [26]
Cuando se proporciona una base de datos corrupta, como una producida por fuzzing , LMDB puede bloquearse. El autor de LMDB considera que es poco probable que el caso sea preocupante, pero ha producido una solución parcial en una rama separada. [27]
En junio de 2013, Oracle cambió la licencia de Berkeley DB (un proyecto relacionado) de la licencia Sleepycat a la Licencia Pública General Affero , [28] restringiendo así su uso en una amplia variedad de aplicaciones. Esto provocó que el proyecto Debian excluyera la biblioteca a partir de la versión 6.0. También se criticó que esta licencia no es amigable con los redistribuidores comerciales. Se desató la discusión sobre si el mismo cambio de licencia podría suceder con LMDB. El autor Howard Chu aclaró que LMDB es parte del proyecto OpenLDAP, que tenía su licencia de estilo BSD antes de que él se uniera, y seguirá siendo así. No se transfiere ningún copyright a nadie al registrarse, lo que haría imposible un movimiento similar al de Oracle. [29] [30] [31] [32] [33] [34] [35] [36] [37]
El problema de la licencia de Berkeley DB ha provocado que las principales distribuciones de Linux, como Debian, hayan eliminado por completo el uso de Berkeley DB y hayan optado por LMDB. [38]
Existen wrappers para varios lenguajes de programación, como C++, [39] [40] Java, [41] Python, [42] [43] Lua, [44] Rust, [45] [46] Go, [47] Ruby, [48] Objective C, [49] Javascript, [50] C#, [51] Perl, [52] PHP, [53] Tcl [54] y Common Lisp. [55] Se puede encontrar una lista completa de wrappers en el sitio web principal. [56]
Howard Chu adaptó SQLite 3.7.7.1 para utilizar LMDB en lugar de su código B-tree original , llamando al resultado SQLightning. [57] Una prueba de inserción citada de 1000 registros fue 20 veces más rápida (que el SQLite original con su implementación B-Tree). [58] LMDB está disponible como un almacén de respaldo para otros proyectos de código abierto, incluidos Cyrus SASL, [59] Heimdal Kerberos, [60] y OpenDKIM. [61] También está disponible en algunos otros proyectos NoSQL como MemcacheDB [62] y Mapkeeper. [63] LMDB se utilizó para hacer que el almacén en memoria Redis persistiera los datos en el disco. El back-end existente en Redis mostró un comportamiento patológico en casos raros, y se buscó un reemplazo. Sin embargo, la API barroca de LMDB fue criticada, ya que obligaba a mucha codificación para hacer cosas simples. Sin embargo, su rendimiento y confiabilidad durante las pruebas fueron considerablemente mejores que los almacenes de back-end alternativos que se probaron. [64]
Un desarrollador de software independiente utilizó los enlaces de Python a LMDB [65] en un entorno de alto rendimiento y publicó, en el sitio de noticias tecnológicas Slashdot , cómo el sistema logró sostener con éxito 200.000 operaciones simultáneas de lectura, escritura y eliminación por segundo (un total de 600.000 operaciones de base de datos por segundo). [66] [67]
En el sitio web principal se mantiene una lista actualizada de aplicaciones que utilizan LMDB. [68]
Muchos proyectos de software libre populares distribuyen o incluyen soporte para LMDB, a menudo como el mecanismo de almacenamiento principal o único.
LMDB hace un uso novedoso de técnicas informáticas bien conocidas, como la semántica de copia en escritura y los árboles B+, para proporcionar garantías de atomicidad y fiabilidad, así como un rendimiento que puede resultar difícil de aceptar, dada la relativa simplicidad de la biblioteca y que ninguna otra base de datos de almacenamiento de clave-valor similar ofrece las mismas garantías o el mismo rendimiento general, aunque los autores afirman explícitamente en las presentaciones que LMDB está optimizada para lectura, no para escritura. Además, como LMDB se desarrolló principalmente para su uso en OpenLDAP , sus desarrolladores se centran principalmente en el desarrollo y mantenimiento de OpenLDAP, no en LMDB en sí. Por tanto, el tiempo limitado que dedicaron los desarrolladores a presentar los primeros resultados de las pruebas comparativas fue criticado por no indicar limitaciones y por dar una "impresión de solución milagrosa" que no era adecuada para abordar la actitud de un ingeniero [79] (hay que señalar que, sin embargo, las preocupaciones planteadas se abordaron posteriormente de forma adecuada, a satisfacción del revisor, por el desarrollador clave detrás de LMDB. [80] )
La presentación motivó a otros desarrolladores de bases de datos a analizar el código en profundidad para entender cómo y por qué funciona. Las revisiones van desde breves [81] hasta profundas. El desarrollador de bases de datos Oren Eini escribió una serie de 12 artículos sobre su análisis de LMDB, comenzando el 9 de julio de 2013. La conclusión fue algo así como "una base de código impresionante... necesita mucho cariño", principalmente debido a métodos demasiado largos y a la duplicación de código. [82] Esta revisión, realizada por un desarrollador de .NET sin experiencia previa en C , concluyó el 22 de agosto de 2013 con "más allá de mis problemas con el código, la implementación es realmente brillante. La forma en que LMDB logra incluir tanta funcionalidad sin hacer las cosas es bastante impresionante... Aprendí mucho del proyecto, y ha sido una experiencia frustrante, molesta y fascinante". [83]
Varias otras revisiones cubren LMDB [84] [85] en varios idiomas, incluido el chino. [86]
A partir de las versiones 6.0/12c, todos los productos Berkeley DB cuentan con la licencia GNU AFFERO GENERAL PUBLIC LICENSE (AGPL), versión 3. Esta licencia es publicada por la Free Software Foundation (FSF) (1) y aprobada por la Open Source Initiative (2). Revise los términos de la licencia para asegurarse de que cumple con los requisitos antes de actualizar a la versión 12c. Las versiones anteriores del software Berkeley DB se seguirán distribuyendo con la licencia Sleepycat.
Proponemos la estandarización de una capacidad de almacenamiento de clave-valor simple, basada en LMDB, que sea rápida, compacta, con capacidad para múltiples procesos y que se pueda usar igualmente desde JS, Java, Rust, Swift y C++.