Antes de continuar, considere darnos una estrella de GitHub ️. ¡Gracias!
Otros idiomas: 简体中文 日本語 한국어
Sitio web • Documentos • Inicio rápido • Discord de la comunidad • Foro Dragonfly • Únase a la comunidad Dragonfly
Discusiones de GitHub • Problemas de GitHub • Contribuciones • Dragonfly Cloud
Dragonfly es un almacén de datos en memoria creado para cargas de trabajo de aplicaciones modernas.
Totalmente compatible con las API de Redis y Memcached, Dragonfly no requiere cambios de código para su adopción. En comparación con los almacenes de datos en memoria heredados, Dragonfly ofrece 25 veces más rendimiento, mayores tasas de aciertos de caché con menor latencia de cola y puede ejecutarse con hasta un 80 % menos de recursos para la carga de trabajo del mismo tamaño.
Primero comparamos Dragonfly con Redis en la instancia m5.large
que se usa comúnmente para ejecutar Redis debido a su arquitectura de subproceso único. El programa de referencia se ejecuta desde otra instancia de prueba de carga (c5n) en la misma AZ usando memtier_benchmark -c 20 --test-time 100 -t 4 -d 256 --distinct-client-seed
Dragonfly muestra un rendimiento comparable:
--ratio 1:0
):Redis | DF |
---|---|
QPS: 159K, P99,9: 1,16 ms, P99: 0,82 ms | QPS: 173K, P99,9: 1,26 ms, P99: 0,9 ms |
--ratio 0:1
):Redis | DF |
---|---|
QPS: 194K, P99,9: 0,8 ms, P99: 0,65 ms | QPS: 191K, P99,9: 0,95 ms, P99: 0,8 ms |
El punto de referencia anterior muestra que la capa algorítmica dentro de DF que le permite escalar verticalmente no cobra un gran costo cuando se ejecuta con un solo subproceso.
Sin embargo, si tomamos un ejemplo un poco más fuerte (m5.xlarge), la brecha entre DF y Redis comienza a crecer. ( memtier_benchmark -c 20 --test-time 100 -t 6 -d 256 --distinct-client-seed
):
--ratio 1:0
):Redis | DF |
---|---|
QPS: 190K, P99,9: 2,45 ms, P99: 0,97 ms | QPS: 279K, P99.9: 1.95ms, P99: 1.48ms |
--ratio 0:1
):Redis | DF |
---|---|
QPS: 220K, P99,9: 0,98 ms, P99: 0,8 ms | QPS: 305K, P99,9: 1,03 ms, P99: 0,87 ms |
La capacidad de rendimiento de Dragonfly continúa creciendo con el tamaño de la instancia, mientras que Redis de un solo subproceso tiene un cuello de botella en la CPU y alcanza máximos locales en términos de rendimiento.
Si comparamos Dragonfly y Redis en la instancia c6gn.16xlarge con mayor capacidad de red, Dragonfly mostró un aumento de 25 veces en el rendimiento en comparación con el proceso único de Redis, superando los 3,8 millones de QPS.
Métricas de latencia del percentil 99 de Dragonfly en su rendimiento máximo:
op | r6g | c6gn | c7g |
---|---|---|---|
colocar | 0,8 ms | 1ms | 1ms |
conseguir | 0,9 ms | 0,9 ms | 0,8 ms |
setex | 0,9 ms | 1,1 ms | 1,3 ms |
Todas las pruebas comparativas se realizaron utilizando memtier_benchmark
(ver más abajo) con la cantidad de subprocesos ajustados por servidor y tipo de instancia. memtier
se ejecutó en una máquina c6gn.16xlarge separada. Establecimos el tiempo de vencimiento en 500 para el punto de referencia SETEX para garantizar que sobreviva al final de la prueba.
memtier_benchmark --ratio ... -t < threads > -c 30 -n 200000 --distinct-client-seed -d 256
--expiry-range=...
En el modo de canalización --pipeline=30
, Dragonfly alcanza 10 millones de QPS para operaciones SET y 15 millones de QPS para operaciones GET.
Comparamos Dragonfly con Memcached en una instancia c6gn.16xlarge en AWS.
Con una latencia comparable, el rendimiento de Dragonfly superó el rendimiento de Memcached tanto en cargas de trabajo de escritura como de lectura. Dragonfly demostró una mejor latencia en cargas de trabajo de escritura debido a la contención en la ruta de escritura en Memcached.
Servidor | QPS(miles qps) | latencia 99% | 99,9% |
---|---|---|---|
Libélula | ? 3844 | ? 0,9 ms | ? 2,4 ms |
Memcached | 806 | 1,6 ms | 3,2 ms |
Servidor | QPS(miles qps) | latencia 99% | 99,9% |
---|---|---|---|
Libélula | ? 3717 | 1ms | 2,4 ms |
Memcached | 2100 | ? 0,34 ms | ? 0,6 ms |
Memcached mostró una latencia más baja para el punto de referencia de lectura, pero también un rendimiento más bajo.
Para probar la eficiencia de la memoria, llenamos Dragonfly y Redis con ~5 GB de datos usando el comando debug populate 5000000 key 1024
, enviamos tráfico de actualización con memtier
e iniciamos la toma de instantáneas con el comando bgsave
.
Esta figura demuestra cómo se comportó cada servidor en términos de eficiencia de la memoria.
Dragonfly fue un 30% más eficiente en memoria que Redis en el estado inactivo y no mostró ningún aumento visible en el uso de memoria durante la fase de instantánea. En su punto máximo, el uso de memoria de Redis aumentó a casi 3 veces el de Dragonfly.
Dragonfly terminó la instantánea más rápido, en unos pocos segundos.
Para obtener más información sobre la eficiencia de la memoria en Dragonfly, consulte nuestro documento Dashtable.
Dragonfly admite argumentos comunes de Redis cuando corresponda. Por ejemplo, puedes ejecutar: dragonfly --requirepass=foo --bind localhost
.
Dragonfly actualmente admite los siguientes argumentos específicos de Redis:
port
: puerto de conexión de Redis ( default: 6379
).bind
: use localhost
para permitir solo conexiones de localhost o una dirección IP pública para permitir conexiones a esa dirección IP (es decir, también desde afuera). Utilice 0.0.0.0
para permitir todo IPv4.requirepass
: la contraseña para la autenticación AUTH ( default: ""
).maxmemory
: límite de memoria máxima (en bytes legibles por humanos) utilizada por la base de datos ( default: 0
). Un valor maxmemory
de 0
significa que el programa determinará automáticamente su uso máximo de memoria.dir
: Dragonfly Docker usa la carpeta /data
para tomar instantáneas de forma predeterminada, la CLI usa ""
. Puede usar la opción -v
Docker para asignarlo a su carpeta de host.dbfilename
: el nombre del archivo para guardar y cargar la base de datos ( default: dump
).También hay algunos argumentos específicos de Dragonfly:
memcached_port
: el puerto para habilitar la API compatible con Memcached ( default: disabled
).
keys_output_limit
: Número máximo de claves devueltas en el comando keys
( default: 8192
). Tenga en cuenta que keys
son un comando peligroso. Truncamos su resultado para evitar un aumento en el uso de la memoria al recuperar demasiadas claves.
dbnum
: número máximo de bases de datos admitidas para select
.
cache_mode
: consulte la novedosa sección de diseño de caché a continuación.
hz
: frecuencia de evaluación de caducidad de clave ( default: 100
). Una frecuencia más baja utiliza menos CPU cuando está inactivo a expensas de una tasa de desalojo más lenta.
snapshot_cron
: Expresión de programación cron para instantáneas de respaldo automático usando sintaxis cron estándar con granularidad de minutos ( default: ""
). A continuación se muestran algunos ejemplos de expresiones de programación cron y no dude en leer más sobre este argumento en nuestra documentación.
Expresión de programación cron | Descripción |
---|---|
* * * * * | en cada minuto |
*/5 * * * * | Cada 5 minutos |
5 */2 * * * | En el minuto 5 y cada 2 horas |
0 0 * * * | A las 00:00 (medianoche) todos los días |
0 6 * * 1-5 | A las 06:00 (amanecer) de lunes a viernes |
primary_port_http_enabled
: permite acceder a la consola HTTP en el puerto TCP principal si es true
( default: true
).
admin_port
: para habilitar el acceso de administrador a la consola en el puerto asignado ( default: disabled
). Admite los protocolos HTTP y RESP.
admin_bind
: para vincular la conexión TCP de la consola de administración a una dirección determinada ( default: any
). Admite los protocolos HTTP y RESP.
admin_nopass
: para habilitar el acceso de administrador abierto a la consola en el puerto asignado, sin necesidad de token de autenticación ( default: false
). Admite los protocolos HTTP y RESP.
cluster_mode
: modo de clúster admitido ( default: ""
). Actualmente sólo admite emulated
.
cluster_announce_ip
: la IP que los comandos del clúster anuncian al cliente.
announce_port
: el puerto que los comandos del clúster anuncian al cliente y al maestro de replicación.
./dragonfly-x86_64 --logtostderr --requirepass=youshallnotpass --cache_mode=true -dbnum 1 --bind localhost --port 6379 --maxmemory=12gb --keys_output_limit=12288 --dbfilename dump.rdb
Los argumentos también se pueden proporcionar a través de:
--flagfile <filename>
>: el archivo debe enumerar un indicador por línea, con signos iguales en lugar de espacios para indicadores clave-valor. No se necesitan comillas para los valores de las banderas.DFLY_x
, donde x
es el nombre exacto de la bandera, distingue entre mayúsculas y minúsculas. Para obtener más opciones, como administración de registros o compatibilidad con TLS, ejecute dragonfly --help
.
Dragonfly actualmente admite ~185 comandos de Redis y todos los comandos de Memcached además de cas
. Casi a la par con la API de Redis 5, el próximo hito de Dragonfly será estabilizar la funcionalidad básica e implementar la API de replicación. Si hay un comando que necesita y que aún no está implementado, abra un problema.
Para la replicación nativa de Dragonfly, estamos diseñando un formato de registro distribuido que admitirá velocidades de orden de magnitud más altas.
Después de la función de replicación, continuaremos agregando los comandos que faltan para las API de las versiones 3 a 6 de Redis.
Consulte nuestra Referencia de comandos para conocer los comandos actuales admitidos por Dragonfly.
Dragonfly tiene un algoritmo de almacenamiento en caché adaptable, unificado y único que es simple y eficiente en memoria.
Puede habilitar el modo de almacenamiento en caché pasando el indicador --cache_mode=true
. Una vez que este modo está activado, Dragonfly expulsará los elementos con los que sea menos probable que te topes en el futuro, pero solo cuando esté cerca del límite maxmemory
.
Los rangos de vencimiento están limitados a ~8 años.
Los plazos de vencimiento con precisión de milisegundos (PEXPIRE, PSETEX, etc.) se redondean al segundo más cercano para plazos superiores a 2^28 ms , lo que tiene un error inferior al 0,001 % y debería ser aceptable para rangos grandes. Si esto no es adecuado para su caso de uso, póngase en contacto o abra un problema explicando su caso.
Para conocer las diferencias más detalladas entre los plazos de vencimiento de Dragonfly y las implementaciones de Redis, consulte aquí.
De forma predeterminada, Dragonfly permite el acceso HTTP a través de su puerto TCP principal (6379). Así es, puede conectarse a Dragonfly a través del protocolo Redis y del protocolo HTTP; el servidor reconoce el protocolo automáticamente durante el inicio de la conexión. Continúe y pruébelo con su navegador. El acceso HTTP actualmente no tiene mucha información, pero incluirá información útil de depuración y administración en el futuro.
Vaya a la URL :6379/metrics
para ver las métricas compatibles con Prometheus.
Las métricas exportadas de Prometheus son compatibles con el panel de Grafana, consulte aquí.
¡Importante! Se debe acceder a la consola HTTP dentro de una red segura. Si expone el puerto TCP de Dragonfly externamente, le recomendamos que deshabilite la consola con --http_admin_console=false
o --nohttp_admin_console
.
Dragonfly comenzó como un experimento para ver cómo se vería un almacén de datos en memoria si se diseñara en 2022. Según las lecciones aprendidas de nuestra experiencia como usuarios de almacenes de memoria e ingenieros que trabajaron para empresas de la nube, sabíamos que necesitábamos preservar dos Propiedades clave de Dragonfly: garantías de atomicidad para todas las operaciones y latencia baja de menos de un milisegundo en un rendimiento muy alto.
Nuestro primer desafío fue cómo utilizar completamente la CPU, la memoria y los recursos de E/S utilizando servidores que están disponibles hoy en las nubes públicas. Para resolver esto, utilizamos una arquitectura de nada compartido, que nos permite dividir el espacio de claves del almacén de memoria entre subprocesos para que cada subproceso pueda administrar su propia porción de datos del diccionario. A estos trozos los llamamos "fragmentos". La biblioteca que impulsa la gestión de subprocesos y E/S para una arquitectura sin compartir es de código abierto aquí.
Para proporcionar garantías de atomicidad para operaciones de múltiples claves, utilizamos los avances de investigaciones académicas recientes. Elegimos el artículo "VLL: un rediseño del administrador de bloqueos para sistemas de bases de datos de memoria principal" para desarrollar el marco transaccional para Dragonfly. La elección de la arquitectura de nada compartido y VLL nos permitió componer operaciones atómicas de múltiples claves sin usar mutexes o spinlocks. fue un hito importante para nuestra PoC y su desempeño se destacó de otras soluciones comerciales y de código abierto.
Nuestro segundo desafío fue diseñar estructuras de datos más eficientes para la nueva tienda. Para lograr este objetivo, basamos nuestra estructura central de tabla hash en el documento "Dash: Scalable Hashing on Persistent Memory". El artículo en sí se centra en el dominio de la memoria persistente y no está directamente relacionado con los almacenes de memoria principal, pero sigue siendo más aplicable a nuestro problema. El diseño de tabla hash sugerido en el artículo nos permitió mantener dos propiedades especiales que están presentes en el diccionario de Redis: la capacidad de hash incremental durante el crecimiento del almacén de datos y la capacidad de recorrer el diccionario bajo cambios mediante una operación de escaneo sin estado. Además de estas dos propiedades, Dash es más eficiente en el uso de CPU y memoria. Al aprovechar el diseño de Dash, pudimos innovar aún más con las siguientes características:
Una vez que habíamos construido las bases para Dragonfly y estábamos satisfechos con su rendimiento, implementamos la funcionalidad de Redis y Memcached. Hasta la fecha, hemos implementado ~185 comandos de Redis (aproximadamente equivalente a la API de Redis 5.0) y 13 comandos de Memcached.
Y finalmente,
Nuestra misión es crear un almacén de datos en memoria bien diseñado, ultrarrápido y rentable para cargas de trabajo en la nube que aproveche los últimos avances de hardware. Tenemos la intención de abordar los puntos débiles de las soluciones actuales y al mismo tiempo preservar las API y las propuestas de sus productos.