In diesem Beitrag vergleichen wir zwei der beliebtesten NoSQL-Datenbanken:Redis (In-Memory) und MongoDB (Percona Memory Storage Engine).
Redis ist ein beliebter und sehr schneller In-Memory-Datenbankstrukturspeicher, der hauptsächlich als Cache oder Nachrichtenbroker verwendet wird. Da es In-Memory ist, ist es der Datenspeicher der Wahl, wenn Antwortzeiten alles andere übertrumpfen.
MongoDB ist ein Dokumentenspeicher auf der Festplatte, der eine JSON-Schnittstelle zu Daten bereitstellt und über eine sehr reichhaltige Abfragesprache verfügt. Bekannt für seine Geschwindigkeit, Effizienz und Skalierbarkeit, ist es derzeit die beliebteste NoSQL-Datenbank, die heute verwendet wird. Da es sich jedoch um eine On-Disk-Datenbank handelt, kann sie in Bezug auf die absolute Leistung nicht mit einer In-Memory-Datenbank wie Redis mithalten. Aber mit der Verfügbarkeit der In-Memory-Speicher-Engines für MongoDB wird ein direkterer Vergleich möglich.
Percona-Speicher-Engine für MongoDB
Ab Version 3.0 bietet MongoDB eine API zum Einbinden der Speicher-Engine Ihrer Wahl. Eine Speicher-Engine aus dem MongoDB-Kontext ist die Komponente der Datenbank, die für die Verwaltung der Datenspeicherung verantwortlich ist, sowohl im Arbeitsspeicher als auch auf der Festplatte. MongoDB unterstützt eine In-Memory-Speicher-Engine, ist jedoch derzeit auf die Enterprise-Edition des Produkts beschränkt. Im Jahr 2016 veröffentlichte Percona eine Open-Source-In-Memory-Engine für die MongoDB Community Edition namens Percona Memory Engine for MongoDB. Wie die In-Memory-Engine von MonogDB ist es auch eine Variation der WiredTiger-Speicher-Engine, jedoch ohne Persistenz auf der Festplatte.
Mit einer In-Memory-MongoDB-Speicher-Engine haben wir gleiche Wettbewerbsbedingungen zwischen Redis und MongoDB. Also, warum müssen wir die beiden vergleichen? Sehen wir uns die Vorteile von jedem von ihnen als Caching-Lösung an.
Schauen wir uns zuerst Redis an.
Vorteile von Redis als Cache
- Eine bekannte Caching-Lösung, die sich darin auszeichnet.
- Redis ist keine einfache Cache-Lösung – es verfügt über fortschrittliche Datenstrukturen, die viele leistungsstarke Möglichkeiten zum Speichern und Abfragen von Daten bieten, die mit einem Standard-Schlüsselwert-Cache nicht erreicht werden können.
- Redis ist ziemlich einfach einzurichten, zu verwenden und zu erlernen.
- Redis bietet Persistenz, die Sie einrichten können, sodass das Aufwärmen des Caches im Falle eines Absturzes problemlos möglich ist.
Nachteile von Redis:
- Es hat keine eingebaute Verschlüsselung auf der Leitung.
- Keine rollenbasierte Kontosteuerung (RBAC).
- Es gibt keine nahtlose, ausgereifte Clustering-Lösung.
- Die Bereitstellung in groß angelegten Cloud-Bereitstellungen kann mühsam sein.
Vorteile von MongoDB als Cache
- MongoDB ist eine traditionellere Datenbank mit erweiterten Datenbearbeitungsfunktionen (denken Sie an Aggregationen und Map-Reduce) und einer reichhaltigen Abfragesprache.
- SSL, RBAC und Scale-out integriert.
- Wenn Sie MongoDB bereits als primäre Datenbank verwenden, sinken Ihre Betriebs- und Entwicklungskosten, da Sie nur eine Datenbank erlernen und verwalten müssen.
Schauen Sie sich diesen Beitrag von Peter Zaitsev an, wo die In-Memory-Engine von MongoDB gut passen könnte.
Nachteil von MongoDB:
- Mit einer In-Memory-Engine bietet sie keine Persistenz, bis sie als Replikatsatz bereitgestellt wird, wobei die Persistenz für die Read Replica(s) konfiguriert ist.
In diesem Beitrag konzentrieren wir uns auf die Quantifizierung der Leistungsunterschiede zwischen Redis und MongoDB . Ein qualitativer Vergleich und operative Unterschiede werden in nachfolgenden Beiträgen behandelt.
Redis vs. In-Memory-MongoDB
Leistung
- Redis schneidet bei Lesevorgängen erheblich besser ab für alle Arten von Workloads und besser für Schreibvorgänge, wenn die Workloads zunehmen.
- Obwohl MongoDB alle Kerne des Systems nutzt, wird die CPU vergleichsweise früh gebunden. Obwohl es immer noch Rechenleistung zur Verfügung hatte, war es beim Schreiben besser als Redis.
- Beide Datenbanken sind letztendlich rechengebunden. Obwohl Redis Single-Threaded ist, schafft es (meistens) mehr, wenn es auf einem Kern ausgeführt wird, als MongoDB, während es alle Kerne auslastet.
- Redis verwendet für nicht-triviale Datensätze im Vergleich zu MongoDB viel mehr RAM, um die gleiche Datenmenge zu speichern.
Konfiguration
Wir haben YCSB verwendet, um die Leistung zu messen, und haben es in der Vergangenheit verwendet, um die Leistung von MongoDB auf verschiedenen Cloud-Anbietern und -Konfigurationen zu vergleichen und zu bewerten. Wir setzen ein grundlegendes Verständnis der YCSB-Arbeitslasten und -Funktionen in der Prüfstandsbeschreibung voraus.
- Datenbankinstanztyp: AWS EC2 c4.xlarge mit 4 Kernen, 7,5 GB Speicher und verbessertem Netzwerk, um sicherzustellen, dass wir keine Netzwerkengpässe haben.
- Client-Rechner: AWS EC2 c4.xlarge in derselben Virtual Private Cloud (VPC) wie die Datenbankserver.
- Redis: Version 3.2.8 mit deaktiviertem AOF und RDB. Eigenständig.
- MongoDB: Percona Memory Engine basierend auf MongoDB Version 3.2.12. Eigenständig.
- Netzwerkdurchsatz : Gemessen über iperf wie von AWS empfohlen:
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
- Workload-Details
- Arbeitslast einfügen: 100 % Schreiben – 2,5 Millionen Datensätze
- Arbeitslast A: Hohe Workload aktualisieren – 50 %/50 % Lese-/Schreibvorgänge – 25 Millionen Vorgänge
- Arbeitslast B: Hauptsächlich gelesene Workload – 95 %/5 % Lese-/Schreibvorgänge – 25 Millionen Vorgänge
- Clientlast: Durchsatz und Latenz werden über schrittweise zunehmende Lasten gemessen, die vom Client generiert werden. Dies wurde erreicht, indem die Anzahl der YCSB-Client-Lade-Threads erhöht wurde, beginnend bei 8 und in Vielfachen von 2 steigend
Ergebnisse
Arbeitslast B-Leistung
Da der primäre Anwendungsfall für In-Memory-Datenbanken der Cache ist, sehen wir uns zuerst Workload B an.
Hier sind die Durchsatz-/Latenzzahlen aus der Workload mit 25 Millionen Vorgängen und das Verhältnis von Lese-/Schreibvorgängen betrug 95 %/5 %. Dies wäre eine repräsentative Cache-Leselast:
Hinweis:Der Durchsatz wird gegen die primäre Achse (links) aufgetragen, während die Latenz gegen die sekundäre Achse (rechts) aufgetragen wird.
Beobachtungen während des Workload B-Laufs:
- Für MongoDB war die CPU ab 32 Threads gesättigt. Mehr als 300 % Nutzung mit einstelligen Leerlaufprozentsätzen.
- Für Redis hat die CPU-Auslastung nie 95 % überschritten. Also war Redis durchgängig besser als MongoDB, während es auf einem einzelnen Thread lief, während MongoDB alle Kerne der Maschine auslastete.
- Für Redis schlugen die Ausführungen bei 128 Threads häufig mit Lese-Timeout-Ausnahmen fehl.
Workload-A-Leistung
Hier sind die Durchsatz-/Latenzzahlen aus der Workload mit 25 Millionen Vorgängen. Das Verhältnis von Lesevorgängen/Schreibvorgängen betrug 50 %/50 %:
Hinweis:Der Durchsatz wird gegen die primäre Achse (links) aufgetragen, während die Latenz gegen die sekundäre Achse (rechts) aufgetragen wird.
Beobachtungen während der Ausführung von Workload A:
- Für MongoDB war die CPU ab 32 Threads gesättigt. Mehr als 300 % Nutzung mit einstelligen Leerlaufprozentsätzen.
- Für Redis hat die CPU-Auslastung nie 95 % überschritten.
- Bei Redis mit 64 Threads und höher schlugen die Ausführungen häufig mit Lese-Timeout-Ausnahmen fehl.
Workload-Leistung einfügen
Schließlich sind hier die Durchsatz-/Latenzzahlen aus der Workload mit 2,5 Millionen Datensätzen. Die Anzahl der Datensätze wurde ausgewählt, um sicherzustellen, dass der Gesamtspeicher im Fall von Redis nicht mehr als 80 % verwendet wurde (da Redis der Speicherfresser ist, siehe Anhang B).
Hinweis:Der Durchsatz wird gegen die primäre Achse (links) aufgetragen, während die Latenz gegen die sekundäre Achse (rechts) aufgetragen wird.
Beobachtungen während des Insert Workload-Laufs:
- Für MongoDB war die CPU ab 32 Threads gesättigt. Mehr als 300 % Nutzung mit einstelligen Leerlaufprozentsätzen.
- Für Redis hat die CPU-Auslastung nie 95 % überschritten.
Anhänge
A:Single-Thread-Leistung
Ich hatte den starken Drang, das herauszufinden – auch wenn es unter realen Bedingungen nicht sehr nützlich ist:Wer wäre besser, wenn er von einem einzigen Thread aus die gleiche Last auf jeden von ihnen anwenden würde? Das heißt, wie würde eine Singlethread-Anwendung funktionieren?
B:Datenbankgröße
Das Standardformat der von YCSB eingefügten Datensätze ist:Jeder Datensatz besteht aus 10 Feldern und jedes Feld aus 100 Bytes. Unter der Annahme, dass jeder Datensatz etwa 1 KB groß ist, würde die erwartete Gesamtgröße im Arbeitsspeicher über 2,4 GB liegen. Es gab einen starken Kontrast zwischen den tatsächlichen Größen, wie sie in den Datenbanken zu sehen waren.
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 }
Der benötigte Speicherplatz beträgt also etwa 2,7 GB, was ziemlich nahe an unseren Erwartungen liegt.
Redis
Sehen wir uns jetzt Redis an.
> 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
Bei Spitzenauslastung scheint Redis etwa 5,72 GB Speicher zu beanspruchen, d. h. doppelt so viel Speicher wie MongoDB. Dieser Vergleich ist aufgrund der Unterschiede in den beiden Datenbanken möglicherweise nicht perfekt, aber dieser Unterschied in der Speichernutzung ist zu groß, um ihn zu ignorieren. YCSB fügt einen Datensatz in einen Hash in Redis ein, und ein Index wird in einem sortierten Satz verwaltet. Da ein einzelner Eintrag größer als 64 ist, wird der Hash normal codiert und es gibt keine Platzersparnis. Die Leistung von Redis wird mit einem erhöhten Speicherbedarf erkauft.
Dies kann unserer Meinung nach ein wichtiger Datenpunkt bei der Wahl zwischen MongoDB und Redis sein – MongoDB ist möglicherweise für Benutzer vorzuziehen, denen es wichtig ist, ihre Speicherkosten zu senken.
C:Netzwerkdurchsatz
Ein In-Memory-Datenbankserver ist wahrscheinlich entweder rechen- oder netzwerk-I/O-gebunden, daher war es während der gesamten Testreihe wichtig sicherzustellen, dass wir niemals netzwerkgebunden werden. Das Messen des Netzwerkdurchsatzes während der Ausführung von Anwendungsdurchsatztests wirkt sich nachteilig auf die Gesamtdurchsatzmessung aus. Daher haben wir nachfolgende Netzwerkdurchsatzmessungen mit iftop bei den Threadzahlen durchgeführt, bei denen die höchsten Schreibdurchsätze beobachtet wurden. Es wurde festgestellt, dass dies sowohl für Redis als auch für MongoDB bei ihrem jeweiligen Spitzendurchsatz bei etwa 440 Mbit / s liegt. Angesichts unserer anfänglichen Messung der maximalen Netzwerkbandbreite von etwa 1,29 Gbit/s sind wir sicher, dass wir niemals an die Netzwerkgrenzen stoßen. Tatsächlich unterstützt es nur die Schlussfolgerung, dass wir viel bessere Zahlen erhalten könnten, wenn Redis Multi-Core wäre.