sql >> Base de Datos >  >> NoSQL >> Redis

Redis frente a MongoDB:comparación de bases de datos en memoria con el motor de memoria Percona

En esta publicación, estamos comparando dos de las bases de datos NoSQL más populares:Redis (en memoria) y MongoDB (motor de almacenamiento de memoria Percona).

Redis es un almacén de estructura de base de datos en memoria popular y muy rápido que se utiliza principalmente como caché o intermediario de mensajes. Al estar en la memoria, es el almacén de datos elegido cuando los tiempos de respuesta superan todo lo demás.

MongoDB es un almacén de documentos en disco que proporciona una interfaz JSON para los datos y tiene un lenguaje de consulta muy rico. Conocida por su velocidad, eficiencia y escalabilidad, actualmente es la base de datos NoSQL más popular utilizada en la actualidad. Sin embargo, al ser una base de datos en disco, no se puede comparar favorablemente con una base de datos en memoria como Redis en términos de rendimiento absoluto. Pero, con la disponibilidad de los motores de almacenamiento en memoria para MongoDB, se vuelve factible una comparación más directa.

Motor de memoria Percona para MongoDB

A partir de la versión 3.0, MongoDB proporciona una API para conectar el motor de almacenamiento de su elección. Un motor de almacenamiento, del contexto MongoDB, es el componente de la base de datos que se encarga de administrar cómo se almacenan los datos, tanto en la memoria como en el disco. MongoDB admite un motor de almacenamiento en memoria; sin embargo, actualmente está limitado a la edición Enterprise del producto. En 2016, Percona lanzó un motor en memoria de código abierto para MongoDB Community Edition llamado Percona Memory Engine para MongoDB. Al igual que el motor en memoria de MonogDB, también es una variación del motor de almacenamiento WiredTiger, pero sin persistencia en el disco.

Con un motor de almacenamiento MongoDB en memoria, tenemos igualdad de condiciones entre Redis y MongoDB. Entonces, ¿por qué necesitamos comparar los dos? Veamos las ventajas de cada uno de ellos como solución de almacenamiento en caché.

Veamos Redis primero.

Ventajas de Redis como caché

  • Una conocida solución de almacenamiento en caché que sobresale en eso.
  • Redis no es una solución de caché simple: tiene estructuras de datos avanzadas que brindan muchas formas poderosas de guardar y consultar datos que no se pueden lograr con un caché de clave-valor estándar.
  • Redis es bastante simple de configurar, usar y aprender.
  • Redis proporciona persistencia que puede optar por configurar, por lo que el calentamiento de la memoria caché en caso de un bloqueo es sencillo.

Desventajas de Redis:

  • No tiene cifrado incorporado en el cable.
  • Sin control de cuenta basado en roles (RBAC).
  • No existe una solución de agrupación en clústeres perfecta y madura.
  • Puede ser complicado implementarlo en implementaciones en la nube a gran escala.

Ventajas de MongoDB como caché

  •  MongoDB es una base de datos más tradicional con funciones avanzadas de manipulación de datos (piense en agregaciones y reducción de mapas) y un lenguaje de consulta enriquecido.
  • SSL, RBAC y escalabilidad horizontal integrados.
  • Si ya está utilizando MongoDB como su base de datos principal, sus costos operativos y de desarrollo se reducen, ya que solo tiene una base de datos para aprender y administrar.

Mire esta publicación de Peter Zaitsev sobre dónde podría encajar bien el motor en memoria de MongoDB.

Desventaja de MongoDB:

  • Con un motor en memoria, no ofrece persistencia hasta que se implementa como un conjunto de réplicas con la persistencia configurada en las réplicas de lectura.

En esta publicación, nos centraremos en cuantificar las diferencias de rendimiento entre Redis y MongoDB . Una comparación cualitativa y las diferencias operativas se cubrirán en publicaciones posteriores.

Redis frente a MongoDB en memoria

Rendimiento

  • Redis funciona considerablemente mejor para las lecturas para todo tipo de cargas de trabajo y mejor para escrituras a medida que aumentan las cargas de trabajo.
  • Aunque MongoDB utiliza todos los núcleos del sistema, se vincula a la CPU relativamente temprano. Si bien todavía tenía computación disponible, era mejor en escritura que Redis.
  • Ambas bases de datos finalmente están vinculadas a la computación. Aunque Redis es de subproceso único, (principalmente) se ejecuta más en un núcleo que MongoDB al saturar todos los núcleos.
  • Redis , para conjuntos de datos no triviales, usa mucha más RAM en comparación con MongoDB para almacenar la misma cantidad de datos.

Configuración

Usamos YCSB para medir el rendimiento y lo hemos estado usando para comparar y comparar el rendimiento de MongoDB en varios proveedores de nube y configuraciones en el pasado. Asumimos una comprensión básica de las cargas de trabajo y las características de YCSB en la descripción del equipo de prueba.

  • Tipo de instancia de base de datos:   AWS EC2 c4.xlarge con 4 núcleos, 7,5 GB de memoria y redes mejoradas para garantizar que no tengamos cuellos de botella en la red.
  • Máquina cliente:   AWS EC2 c4.xlarge en la misma nube privada virtual (VPC) que los servidores de la base de datos.
  • Redis:  Versión 3.2.8 con AOF y RDB desactivados. Independiente.
  • MongoDB:  Motor de memoria Percona basado en MongoDB versión 3.2.12. Independiente.
  • Rendimiento de red :  Medido a través de iperf según lo recomendado por AWS:
    Test Complete. Summary Results:
    [ ID] Interval           Transfer     Bandwidth       Retr
    [  4]   0.00-60.00  sec  8.99 GBytes  1.29 Gbits/sec  146             sender
    [  4]   0.00-60.00  sec  8.99 GBytes  1.29 Gbits/sec                  receiver
    
  • Detalles de la carga de trabajo
    1. Insertar carga de trabajo: 100 % de escritura:2,5 millones de registros
    2. Carga de trabajo A: Carga de trabajo pesada de actualización:50 %/50 % de lecturas/escrituras:25 millones de operaciones
    3. Carga de trabajo B: Carga de trabajo principalmente de lectura:95 %/5 % de lecturas/escrituras:25 millones de operaciones
  • Carga de clientes: El rendimiento y la latencia se miden en cargas cada vez mayores generadas desde el cliente. Esto se hizo aumentando la cantidad de subprocesos de carga del cliente YCSB, comenzando en 8 y creciendo en múltiplos de 2

Resultados

Rendimiento de la carga de trabajo B

Dado que el caso de uso principal para las bases de datos en memoria es la memoria caché, veamos primero la carga de trabajo B.

Estas son las cifras de rendimiento/latencia de la carga de trabajo de 25 millones de operaciones y la proporción de lecturas/escrituras fue del 95 %/5 %. Esta sería una carga de trabajo de lectura de caché representativa:

Nota:el rendimiento se representa en el eje principal (izquierda), mientras que la latencia se representa en el eje secundario (derecha).

Observaciones durante la ejecución de la carga de trabajo B:

  • Para MongoDB, la CPU estaba saturada por 32 subprocesos en adelante. Más del 300 % de uso con porcentajes de inactividad de un solo dígito.
  • Para Redis, la utilización de la CPU nunca superó el 95 %. Por lo tanto, Redis se desempeñaba constantemente mejor que MongoDB mientras se ejecutaba en un solo subproceso, mientras que MongoDB saturaba todos los núcleos de la máquina.
  • Para Redis, con 128 subprocesos, las ejecuciones fallaron a menudo con excepciones de tiempo de espera de lectura.

Carga de trabajo A Rendimiento

Estas son las cifras de rendimiento/latencia de la carga de trabajo de 25 millones de operaciones. La proporción de lecturas/escrituras fue del 50 %/50 %:

Nota:el rendimiento se representa en el eje principal (izquierda), mientras que la latencia se representa en el eje secundario (derecha).

Observaciones durante la ejecución de la carga de trabajo A:

  • Para MongoDB, la CPU estaba saturada por 32 subprocesos en adelante. Más del 300 % de uso con porcentajes de inactividad de un solo dígito.
  • Para Redis, la utilización de la CPU nunca superó el 95 %.
  • Para Redis, por 64 subprocesos y más, las ejecuciones fallaron a menudo con excepciones de tiempo de espera de lectura.

Insertar rendimiento de la carga de trabajo

Por último, estas son las cifras de rendimiento/latencia de la carga de trabajo de inserción de registros de 2,5 millones. La cantidad de registros se seleccionó para garantizar que se usara la memoria total en el evento Redis que no superó el 80 % (dado que Redis es el acaparador de memoria, consulte el Apéndice B).

Nota:el rendimiento se representa en el eje principal (izquierda), mientras que la latencia se representa en el eje secundario (derecha).

Observaciones durante la ejecución de Insertar carga de trabajo:

  • Para MongoDB, la CPU estaba saturada por 32 subprocesos en adelante. Más del 300 % de uso con porcentajes de inactividad de un solo dígito.
  • Para Redis, la utilización de la CPU nunca superó el 95 %.

Apéndices

A:rendimiento de un solo subproceso

Tuve una fuerte necesidad de averiguar esto, aunque no es muy útil en condiciones del mundo real:quién sería mejor al aplicar la misma carga a cada uno de ellos desde un solo hilo. Es decir, ¿cómo funcionaría una aplicación de subproceso único?

B:Tamaño de la base de datos

El formato por defecto de los registros insertados por YCSB son:cada registro es de 10 campos y cada campo es de 100 bytes. Suponiendo que cada registro tenga alrededor de 1 KB, el tamaño total esperado en la memoria sería superior a 2,4 GB. Hubo un marcado contraste en los tamaños reales como se ve en las bases de datos.

MongoDB

> db.usertable.count()
2500000
> db.usertable.findOne()
{
    "_id" : "user6284781860667377211",
    "field1" : BinData(0,"OUlxLllnPC0sJEovLTpyL18jNjk6ME8vKzF4Kzt2OUEzMSEwMkBvPytyODZ4Plk7KzRmK0FzOiYoNFU1O185KFB/IVF7LykmPkE9NF1pLDFoNih0KiIwJU89K0ElMSAgKCF+Lg=="),
    "field0" : BinData(0,"ODlwIzg0Ll5vK1s7NUV1O0htOVRnMk53JEd3KiwuOFN7Mj5oJ1FpM11nJ1hjK0BvOExhK1Y/LjEiJDByM14zPDtgPlcpKVYzI1kvKEc5PyY6OFs9PUMpLEltNEI/OUgzIFcnNQ=="),
    "field7" : BinData(0,"N155M1ZxPSh4O1B7IFUzJFNzNEB1OiAsM0J/NiMoIj9sP1Y1Kz9mKkJ/OiQsMSk2OCouKU1jOltrMj4iKEUzNCVqIV4lJC0qIFY3MUo9MFQrLUJrITdqOjJ6NVs9LVcjNExxIg=="),
    "field6" : BinData(0,"Njw6JVQnMyVmOiZyPFxrPz08IU1vO1JpIyZ0I1txPC9uN155Ij5iPi5oJSIsKVFhP0JxM1svMkphL0VlNzdsOlQxKUQnJF4xPkk9PUczNiF8MzdkNy9sLjg6NCNwIy1sKTw6MA=="),
    "field9" : BinData(0,"KDRqP1o3KzwgNUlzPjwgJEgtJC44PUUlPkknKU5pLzkuLEAtIlg9JFwpKzBqIzo2MCIoKTxgNU9tIz84OFB/MzJ4PjwoPCYyNj9mOjY+KU09JUk1I0l9O0s/IEUhNU05NShiNg=="),
    "field8" : BinData(0,"NDFiOj9mJyY6KTskO0A/OVg/NkchKEFtJUprIlJrPjYsKT98JyI8KFwzOEE7ICR4LUF9JkU1KyRkKikoK0g3MEMxKChsL10pKkAvPFRxLkxhOlotJFZlM0N/LiR4PjlqJ0FtOw=="),
    "field3" : BinData(0,"OSYoJTR+JEp9K00pKj0iITVuIzVqPkBpJFN9Myk4PDhqOjVuP1YhPSM2MFp/Kz14PTF4Mlk3PkhzKlx3L0xtKjkqPCY4JF0vIic6LEx7PVBzI0U9KEM1KDV4NiEuKFx5MiZyPw=="),
    "field2" : BinData(0,"Njd8LywkPlg9IFl7KlE5LV83ISskPVQpNDYgMEprOkprMy06LlotMUF5LDZ0IldzLl0tJVkjMTdgJkNxITFsNismLDxuIyYoNDgsLTc+OVpzKkBlMDtoLyBgLlctLCxsKzl+Mw=="),
    "field5" : BinData(0,"OCJiNlI1O0djK1BtIyc4LEQzNj9wPyQiPT8iNE1pODI2LShqNDg4JF1jNiZiNjZuNE5lNzA8OCAgMDp2OVkjNVU3MzIuJTgkNDp0IyVkJyk6IEEvKzVyK1s9PEAhKUJvPDxyOw=="),
    "field4" : BinData(0,"OFN1I0B7N1knNSR2LFp7PjUyPlJjP15jIUdlN0AhNEkhMC9+Lkd5P10jO1B3K10/I0orIUI1NzYuME81I0Y1NSYkMCxyI0w/LTc8PCEgJUZvMiQiIkIhPCF4LyN6K14rIUJlJg==")
}
> db.runCommand({ dbStats: 1, scale: 1 })
{
    "db" : "ycsb",
    "collections" : 1,
    "objects" : 2500000,
    "avgObjSize" : 1167.8795252,
    "dataSize" : 2919698813,
    "storageSize" : 2919698813,
    "numExtents" : 0,
    "indexes" : 1,
    "indexSize" : 76717901,
    "ok" : 1
}

Por lo tanto, el espacio ocupado es de ~2,7 GB que se acerca bastante a lo que esperábamos.

Redis

Veamos Redis ahora.

> info keyspace
# Keyspace
db0:keys=2500001,expires=0,avg_ttl=0
127.0.0.1:6379> RANDOMKEY
"user3176318471616059981"
127.0.0.1:6379> hgetall user3176318471616059981
 1) "field1"
 2) "#K/<No\"&l*M{,;f;]\x7f)Ss'+2<D}7^a8I/01&9.:)Q71T7,3r&\\y6:< Gk;6n*]-)*f>:p:O=?<:(;v/)0)Yw.W!8]+4B=8.z+*4!"
 3) "field2"
 4) "(9<9P5**d7<v((2-6*3Zg/.p4G=4Us;N+!C! I50>h=>p\"X9:Qo#C9:;z.Xs=Wy*H3/Fe&0`8)t.Ku0Q3)E#;Sy*C).Sg++t4@7-"
 5) "field5"
 6) "#1 %8x='l?5d38~&U!+/b./b;(6-:v!5h.Ou2R}./(*)4!8>\"B'!I)5U?0\" >Ro.Ru=849Im+Qm/Ai(;:$Z',]q:($%&(=3~5(~?"
 7) "field0"
 8) "+\"(1Pw.>*=807Jc?Y-5Nq#Aw=%*57r7!*=Tm!<j6%t3-45L5%Cs#/h;Mg:Vo690-/>-X}/X#.U) )f9-~;?p4;p*$< D-1_s!0p>"
 9) "field7"
10) ":]o/2p/3&(!b> |#:0>#0-9b>Pe6[}<Z{:S}9Uc*0<)?60]37'~'Jk-Li',x!;.5H'\"'|.!v4Y-!Hk=E\x7f2;8*9((-09*b#)x!Pg2"
11) "field3"
12) " C; ,f6Uq+^i Fi'8&0By\"^##Qg\":$+7$%Y;7Rs'\"d3Km'Es>.|33$ Vo*M%=\"<$&j%/<5]%\".h&Kc'5.46x5D35'0-3l:\"| !l;"
13) "field6"
14) "-5x6!22)j;O=?1&!:&.S=$;|//r'?d!W54(j!$:-H5.*n&Zc!0f;Vu2Cc?E{1)r?M'!Kg'-b<Dc*1d2M-9*d&(l?Uk5=8,>0.B#1"
15) "field9"
16) "(Xa&1t&Xq\"$((Ra/Q9&\": &>4Ua;Q=!T;(Vi2G+)Uu.+|:Ne;Ry3U\x7f!B\x7f>O7!Dc;V7?Eu7E9\"&<-Vi>7\"$Q%%A%1<2/V11: :^c+"
17) "field8"
18) "78(8L9.H#5N+.E5=2`<Wk+Pw?+j'Q=3\"$,Nk3O{+3p4K?0/ 5/r:W)5X}#;p1@\x7f\"+&#Ju+Z97#t:J9$'*(K).7&0/` 125O38O)0"
19) "field4"
20) "$F=)Ke5V15_)-'>=C-/Ka7<$;6r#_u F9)G/?;t& x?D%=Ba Zk+]) ($=I%3P3$<`>?*=*r9M1-Ye:S%%0,(Ns3,0'A\x7f&Y12A/5"
127.0.0.1:6379> info memory
# Memory
used_memory:6137961456
used_memory_human:5.72G
used_memory_rss:6275940352
used_memory_rss_human:5.84G
used_memory_peak:6145349904
used_memory_peak_human:5.72G
total_system_memory:7844429824
total_system_memory_human:7.31G
used_memory_lua:37888
used_memory_lua_human:37.00K
maxmemory:7516192768
maxmemory_human:7.00G
maxmemory_policy:noeviction
mem_fragmentation_ratio:1.02
mem_allocator:jemalloc-3.6.0

En el momento de uso máximo, parece que Redis ocupa alrededor de 5,72 G de memoria, es decir, el doble de memoria que ocupa MongoDB. Ahora, esta comparación puede no ser perfecta debido a las diferencias en las dos bases de datos, pero esta diferencia en el uso de la memoria es demasiado grande para ignorarla. YCSB inserta un registro en un hash en Redis y se mantiene un índice en un conjunto ordenado. Dado que una entrada individual es mayor que 64, el hash se codifica normalmente y no se ahorra espacio. El rendimiento de Redis tiene el precio de una mayor huella de memoria.

Esto, en nuestra opinión, puede ser un punto de datos importante para elegir entre MongoDB y Redis:MongoDB podría ser preferible para los usuarios que se preocupan por reducir sus costos de memoria.

C:rendimiento de la red

Es probable que un servidor de base de datos en memoria esté vinculado a la computación o a la E/S de la red, por lo que fue importante durante todo el conjunto de estas pruebas asegurarnos de que nunca estuviéramos vinculados a la red. Medir el rendimiento de la red mientras se ejecutan las pruebas de rendimiento de la aplicación afecta negativamente a la medición del rendimiento general. Por lo tanto, realizamos mediciones posteriores del rendimiento de la red utilizando iftop en los recuentos de subprocesos donde se observaron los rendimientos de escritura más altos. Se encontró que esto era de alrededor de 440 Mbps tanto para Redis como para MongoDB en su rendimiento máximo respectivo. Dado que nuestra medición inicial del ancho de banda máximo de la red es de alrededor de 1,29 Gbps, estamos seguros de que nunca alcanzamos los límites de la red. De hecho, solo respalda la inferencia de que si Redis fuera multinúcleo, podríamos obtener números mucho mejores.