W tym poście porównujemy dwie najpopularniejsze bazy danych NoSQL:Redis (w pamięci) i MongoDB (silnik pamięci Percona).
Redis to popularny i bardzo szybki magazyn struktury bazy danych w pamięci, używany głównie jako pamięć podręczna lub broker wiadomości. Będąc w pamięci, jest to magazyn danych z wyboru, gdy czasy odpowiedzi są ważniejsze niż wszystko inne.
MongoDB to magazyn dokumentów na dysku, który zapewnia interfejs JSON do danych i ma bardzo bogaty język zapytań. Znana ze swojej szybkości, wydajności i skalowalności, jest obecnie najpopularniejszą obecnie używaną bazą danych NoSQL. Jednak będąc bazą danych na dysku, nie może być korzystnie w porównaniu z bazą danych w pamięci, taką jak Redis, pod względem bezwzględnej wydajności. Jednak dzięki dostępności silników pamięci masowej w pamięci dla MongoDB możliwe staje się bardziej bezpośrednie porównanie.
Silnik pamięci Percona dla MongoDB
Począwszy od wersji 3.0, MongoDB udostępnia interfejs API do podłączenia wybranego silnika pamięci. Silnik pamięci, z kontekstu MongoDB, jest składnikiem bazy danych odpowiedzialnym za zarządzanie sposobem przechowywania danych, zarówno w pamięci, jak i na dysku. MongoDB obsługuje silnik pamięci masowej, jednak obecnie jest ograniczony do wersji Enterprise produktu. W 2016 r. firma Percona wypuściła silnik in-memory o otwartym kodzie źródłowym dla MongoDB Community Edition o nazwie Percona Memory Engine dla MongoDB. Podobnie jak silnik pamięci wewnętrznej MonogDB, jest to również odmiana silnika pamięci masowej WiredTiger, ale bez utrwalania na dysku.
Dzięki wbudowanemu silnikowi pamięci masowej MongoDB mamy równe szanse między Redis i MongoDB. Dlaczego więc musimy je porównywać? Przyjrzyjmy się zaletom każdego z nich jako rozwiązania do buforowania.
Spójrzmy najpierw na Redis.
Zalety Redisa jako pamięci podręcznej
- Dobrze znane rozwiązanie do buforowania, które się w nim wyróżnia.
- Redis nie jest zwykłym rozwiązaniem pamięci podręcznej – ma zaawansowane struktury danych, które zapewniają wiele skutecznych sposobów zapisywania i sprawdzania danych, których nie można osiągnąć za pomocą waniliowej pamięci podręcznej klucz-wartość.
- Redis jest dość prosty w konfiguracji, obsłudze i nauce.
- Redis zapewnia trwałość, którą możesz skonfigurować, więc podgrzewanie pamięci podręcznej w przypadku awarii jest bezproblemowe.
Wady Redisa:
- Nie ma wbudowanego szyfrowania w przewodzie.
- Brak kontroli konta opartej na rolach (RBAC).
- Nie ma jednolitego, dojrzałego rozwiązania do klastrowania.
- Wdrożenie w chmurze na dużą skalę może być trudne.
Zalety MongoDB jako pamięci podręcznej
- MongoDB to bardziej tradycyjna baza danych z zaawansowanymi funkcjami manipulacji danymi (agregacja myśli i redukcja map) oraz bogatym językiem zapytań.
- Wbudowane SSL, RBAC i skalowanie w poziomie.
- Jeśli korzystasz już z MongoDB jako podstawowej bazy danych, koszty operacyjne i programistyczne spadają, ponieważ masz tylko jedną bazę danych do nauki i zarządzania.
Spójrz na ten post Petera Zajcewa, w którym dowiesz się, gdzie silnik MongoDB w pamięci może być dobrym rozwiązaniem.
Wady MongoDB:
- Z silnikiem w pamięci nie zapewnia trwałości, dopóki nie zostanie wdrożony jako zestaw replik z trwałością skonfigurowaną na replikach do odczytu.
W tym poście skupimy się na ilościowym określeniu różnic w wydajności między Redis i MongoDB . Porównanie jakościowe i różnice operacyjne zostaną omówione w kolejnych postach.
Redis a MongoDB w pamięci
Wydajność
- Redis działa znacznie lepiej w przypadku odczytów dla wszelkiego rodzaju obciążeń i lepiej dla zapisów w miarę wzrostu obciążenia.
- Mimo że MongoDB wykorzystuje wszystkie rdzenie systemu, stosunkowo wcześnie wiąże się z CPU. Chociaż nadal miał dostępne obliczenia, był lepszy w zapisie niż Redis.
- Obie bazy danych są ostatecznie powiązane z obliczeniami. Mimo że Redis jest jednowątkowy, (przeważnie) wykonuje więcej pracy na jednym rdzeniu niż MongoDB podczas nasycania wszystkich rdzeni.
- Ponownie , w przypadku nietrywialnych zestawów danych, używa znacznie więcej pamięci RAM w porównaniu do MongoDB do przechowywania tej samej ilości danych.
Konfiguracja
Wykorzystaliśmy YCSB do pomiaru wydajności i używaliśmy go do porównywania i testowania wydajności MongoDB z różnymi dostawcami chmury i konfiguracjami w przeszłości. Zakładamy podstawową wiedzę na temat obciążeń i funkcji YCSB w opisie stanowiska testowego.
- Typ instancji bazy danych: AWS EC2 c4.xlarge z 4 rdzeniami, 7,5 GB pamięci i ulepszoną obsługą sieci, dzięki czemu nie mamy żadnych wąskich gardeł w sieci.
- Maszyna klienta: AWS EC2 c4.xlarge w tej samej wirtualnej chmurze prywatnej (VPC) co serwery baz danych.
- Ponownie: Wersja 3.2.8 z wyłączonymi AOF i RDB. Samodzielny.
- MongoDB: Percona Memory Engine oparty na MongoDB w wersji 3.2.12. Samodzielny.
- Przepustowość sieci : Mierzone przez iperf zgodnie z zaleceniami 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
- Szczegóły zadania
- Wstaw zadanie: 100% zapisu – 2,5 miliona rekordów
- Zadanie A: Aktualizacja dużego obciążenia – 50%/50% odczytów/zapisów – 25 milionów operacji
- Zadanie B: Czytaj głównie obciążenie – 95%/5% odczytów/zapisów – 25 milionów operacji
- Ładowanie klienta: Przepustowość i opóźnienia mierzone na przyrostowo rosnących obciążeniach generowanych przez klienta. Dokonano tego poprzez zwiększenie liczby wątków ładowania klienta YCSB, zaczynając od 8 i rosnąc w wielokrotności 2
Wyniki
Wydajność obciążenia B
Ponieważ podstawowym przypadkiem użycia baz danych w pamięci jest pamięć podręczna, spójrzmy najpierw na obciążenie B.
Oto liczby dotyczące przepustowości/opóźnień z 25 milionów operacji obciążenia, a stosunek odczytów/zapisów wyniósł 95%/5%. Byłoby to reprezentatywne obciążenie odczytu pamięci podręcznej:
Uwaga:przepustowość jest kreślona względem osi głównej (po lewej), a opóźnienie względem osi pomocniczej (po prawej).
Obserwacje podczas wykonywania zadania B:
- W przypadku MongoDB procesor został nasycony przez 32 wątki wzwyż. Wykorzystanie ponad 300% z jednocyfrowymi wartościami procentowymi bezczynności.
- W przypadku Redis wykorzystanie procesora nigdy nie przekroczyło 95%. Tak więc Redis konsekwentnie działał lepiej niż MongoDB podczas działania w jednym wątku, podczas gdy MongoDB nasycał wszystkie rdzenie maszyny.
- W przypadku Redis, przy 128 wątkach, uruchomienia często kończyły się niepowodzeniem z wyjątkami dotyczącymi limitu czasu odczytu.
Wydajność obciążenia A
Oto liczby dotyczące przepustowości/opóźnień z obciążenia 25 milionami operacji. Stosunek odczytów/zapisów wyniósł 50%/50%:
Uwaga:przepustowość jest kreślona względem osi głównej (po lewej), a opóźnienie względem osi pomocniczej (po prawej).
Obserwacje podczas przebiegu Obciążenia A:
- W przypadku MongoDB procesor został nasycony przez 32 wątki wzwyż. Wykorzystanie ponad 300% z jednocyfrowymi wartościami procentowymi bezczynności.
- W przypadku Redis wykorzystanie procesora nigdy nie przekroczyło 95%.
- W przypadku Redis, przez 64 wątki i więcej, uruchomienia często kończyły się niepowodzeniem z wyjątkami dotyczącymi limitu czasu odczytu.
Wstaw wydajność obciążenia
Na koniec podajemy dane dotyczące przepustowości/opóźnień z 2,5 miliona obciążenia wstawiania rekordów. Wybrano liczbę rekordów, aby zapewnić wykorzystanie całkowitej pamięci w zdarzeniu Redis, która nie przekroczyła 80% (ponieważ Redis jest świnią pamięci, patrz Dodatek B).
Uwaga:przepustowość jest kreślona względem osi głównej (po lewej), a opóźnienie względem osi pomocniczej (po prawej).
Obserwacje podczas uruchamiania wstawiania obciążenia:
- W przypadku MongoDB procesor został nasycony przez 32 wątki wzwyż. Wykorzystanie ponad 300% z jednocyfrowymi wartościami procentowymi bezczynności.
- W przypadku Redis wykorzystanie procesora nigdy nie przekroczyło 95%.
Załączniki
O:Wydajność w jednym wątku
Miałem wielką ochotę się tego dowiedzieć – mimo że nie jest to zbyt przydatne w rzeczywistych warunkach:kto byłby lepszy, gdyby przykładał to samo obciążenie do każdego z nich z jednego wątku. To znaczy, jak działałaby aplikacja jednowątkowa?
B:Rozmiar bazy danych
Domyślny format rekordów wstawianych przez YCSB to:każdy rekord składa się z 10 pól, a każde pole ma 100 bajtów. Zakładając, że każdy rekord ma około 1 KB, całkowity oczekiwany rozmiar w pamięci wyniesie ponad 2,4 GB. Rzeczywiste rozmiary widoczne w bazach danych były wyraźne.
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 }
Tak więc zajęte miejsce wynosi ~2,7 GB, co jest dość zbliżone do tego, czego się spodziewaliśmy.
Ponownie
Spójrzmy teraz na Redis.
> 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
Wydaje się, że przy szczytowym wykorzystaniu Redis zajmuje około 5,72 GB pamięci, tj. dwa razy więcej pamięci niż zajmuje MongoDB. Teraz to porównanie może nie być idealne ze względu na różnice w dwóch bazach danych, ale ta różnica w użyciu pamięci jest zbyt duża, aby ją zignorować. YCSB wstawia rekord w hashu w Redis, a indeks jest utrzymywany w posortowanym zestawie. Ponieważ pojedynczy wpis jest większy niż 64, skrót jest kodowany normalnie i nie ma oszczędności miejsca. Wydajność Redis ma cenę zwiększonego zużycia pamięci.
Naszym zdaniem może to być ważny punkt danych przy wyborze między MongoDB a Redis – MongoDB może być preferowany dla użytkowników, którym zależy na zmniejszeniu kosztów pamięci.
C:Przepustowość sieci
Serwer bazy danych w pamięci może być powiązany z obliczeniami lub sieciowym wejściem/wyjściem, dlatego podczas całego zestawu tych testów ważne było upewnienie się, że nigdy nie zostaniemy powiązani z siecią. Pomiar przepustowości sieci podczas wykonywania testów przepustowości aplikacji niekorzystnie wpływa na ogólny pomiar przepustowości. Dlatego przeprowadziliśmy kolejne pomiary przepustowości sieci przy użyciu iftop przy liczbie wątków, gdzie zaobserwowano najwyższą przepustowość zapisu. Stwierdzono, że wynosi ona około 440 Mb/s zarówno dla Redis, jak i MongoDB przy ich szczytowej przepustowości. Biorąc pod uwagę nasz początkowy pomiar maksymalnej przepustowości sieci, który wynosi około 1,29 Gb/s, jesteśmy pewni, że nigdy nie osiągniemy granic sieci. W rzeczywistości potwierdza tylko wniosek, że gdyby Redis był wielordzeniowy, moglibyśmy uzyskać znacznie lepsze liczby.