В тази публикация сравняваме две от най-популярните NoSQL бази данни:Redis (в паметта) и MongoDB (механизъм за съхранение на памет Percona).
Redis е популярно и много бързо хранилище за структура на база данни в паметта, използвано предимно като кеш или посредник на съобщения. Тъй като е в паметта, това е предпочитано хранилище за данни, когато времето за отговор превъзхожда всичко останало.
MongoDB е хранилище за документи на диск, което предоставя JSON интерфейс за данни и има много богат език за заявки. Известна със своята скорост, ефективност и мащабируемост, в момента това е най-популярната база данни NoSQL, използвана днес. Въпреки това, тъй като е дискова база данни, тя не може да се сравни благоприятно с база данни в паметта като Redis по отношение на абсолютната производителност. Но с наличието на механизмите за съхранение в паметта за MongoDB става възможно по-пряко сравнение.
Percona Memory Engine за MongoDB
Започвайки от версия 3.0, MongoDB предоставя API за включване на движката за съхранение по ваш избор. Машината за съхранение, от контекста на MongoDB, е компонентът на базата данни, който е отговорен за управлението на това как се съхраняват данните, както в паметта, така и на диска. MongoDB поддържа двигател за съхранение в паметта, но в момента е ограничен до Enterprise изданието на продукта. През 2016 г. Percona пусна двигател с отворен код в паметта за MongoDB Community Edition, наречен Percona Memory Engine за MongoDB. Подобно на механизма в паметта на MonogDB, той също е вариант на механизма за съхранение на WiredTiger, но без постоянство на диска.
С инсталиран двигател за съхранение на MongoDB в паметта, ние имаме равни условия между Redis и MongoDB. И така, защо трябва да сравняваме двете? Нека разгледаме предимствата на всеки от тях като решение за кеширане.
Нека първо да разгледаме Redis.
Предимства на Redis като кеш
- Добре познато решение за кеширане, което се отличава с него.
- Redis не е обикновено решение за кеширане – той има разширени структури от данни, които предоставят много мощни начини за запазване и запитване на данни които не могат да бъдат постигнати с ванилен кеш ключ-стойност.
- Redis е сравнително лесен за настройка, използване и научаване.
- Redis осигурява постоянство, което можете да изберете да настроите, така че затоплянето на кеша в случай на срив е безпроблемно.
Недостатъци на Redis:
- Няма вградено криптиране на проводника.
- Без контрол на акаунта, базиран на роли (RBAC).
- Няма безпроблемно, зряло решение за клъстериране.
- Може да бъде проблем за внедряване при широкомащабни облачни внедрявания.
Предимства на MongoDB като кеш
- MongoDB е по-традиционна база данни с разширени функции за манипулиране на данни (мислете за агрегиране и намаляване на картата) и богат език за заявки.
- Вградени SSL, RBAC и мащабиране.
- Ако вече използвате MongoDB като основна база данни, тогава вашите оперативни и развойни разходи намаляват, тъй като имате само една база данни за изучаване и управление.
Вижте тази публикация от Петър Зайцев за това къде може да е подходящ механизмът на MongoDB в паметта.
Недостатък на MongoDB:
- С механизъм в паметта той не предлага постоянство, докато не бъде разгърнат като набор от реплики с конфигурирана постоянство на репликата за четене.
В тази публикация ще се съсредоточим върху количественото определяне на разликите в производителността между Redis и MongoDB . Качественото сравнение и оперативните разлики ще бъдат разгледани в следващите публикации.
Redis срещу MongoDB в паметта
Ефективност
- Redis работи значително по-добре при четене за всякакви видове натоварвания и по-добре за записвания, тъй като натоварванията се увеличават.
- Въпреки че MongoDB използва всички ядра на системата, тя се свързва с процесора сравнително рано. Въпреки че все още имаше достъпни изчисления, беше по-добър в записването от Redis.
- В крайна сметка и двете бази данни са обвързани с изчисления. Въпреки че Redis е еднонишков, той (предимно) се справя повече с изпълнение на едно ядро, отколкото MongoDB, докато насища всички ядра.
- Redis , за нетривиални набори от данни, използва много повече RAM в сравнение с MongoDB, за да съхранява същото количество данни.
Конфигурация
Използвахме YCSB за измерване на ефективността и го използвахме за сравняване и сравнение на ефективността на MongoDB при различни доставчици на облак и конфигурации в миналото. Предполагаме основно разбиране на работните натоварвания и функции на YCSB в описанието на тестовата платформа.
- Тип екземпляр на базата данни: AWS EC2 c4.xlarge с 4 ядра, 7,5 GB памет и подобрена работа в мрежа, за да гарантираме, че нямаме проблеми с мрежата.
- Клиентска машина: AWS EC2 c4.xlarge в същия виртуален частен облак (VPC) като сървърите на база данни.
- Redis: Версия 3.2.8 с изключени AOF и RDB. Самостоятелен.
- MongoDB: Percona Memory Engine, базиран на MongoDB версия 3.2.12. Самостоятелен.
- Пропускателна способност на мрежата : Измерено чрез iperf, както е препоръчано от 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
- Подробности за работното натоварване
- Вмъкване на натоварване: 100 % запис – 2,5 милиона записа
- Работно натоварване A: Актуализиране на голямо натоварване – 50%/50% четене/записване – 25 милиона операции
- Работно натоварване Б: Четене предимно натовареност – 95%/5% четене/записване – 25 милиона операции
- Зареждане на клиента: Пропускателна способност и латентност, измерени при постепенно нарастващи натоварвания, генерирани от клиента. Това беше направено чрез увеличаване на броя на нишките за зареждане на клиента YCSB, като се започне от 8 и нараства кратно на 2
Резултати
Ефективност на работното натоварване B
Тъй като основният случай на използване на бази данни в паметта е кешът, нека първо да разгледаме работното натоварване B.
Ето числата за пропускателна способност/закъснение от работното натоварване от 25 милиона операции и съотношението на четене/записване е 95%/5%. Това би било представително работно натоварване за четене на кеш:
Забележка:Пропускателната способност е нанесена спрямо основната ос (вляво), докато латентността е нанесена спрямо вторичната ос (вдясно).
Наблюдения по време на изпълнение на работното натоварване B:
- За MongoDB процесорът беше наситен с 32 нишки нагоре. По-голямо от 300% използване с едноцифрени проценти на празен ход.
- За Redis използването на процесора никога не е надхвърлило 95%. И така, Redis постоянно постигаше по-добри резултати от MongoDB, докато работеше в една нишка, докато MongoDB насищаше всички ядра на машината.
- За Redis, при 128 нишки, изпълняването често е неуспешно с изключения за изчакване на изчакване.
Ефективност на работното натоварване
Ето числата за пропускателна способност/закъснение от работното натоварване от 25 милиона операции. Съотношението на четене/записване беше 50%/50%:
Забележка:Пропускателната способност е нанесена спрямо основната ос (вляво), докато латентността е нанесена спрямо вторичната ос (вдясно).
Наблюдения по време на изпълнение на работното натоварване A:
- За MongoDB процесорът беше наситен с 32 нишки нагоре. По-голямо от 300% използване с едноцифрени проценти на празен ход.
- За Redis използването на процесора никога не надхвърля 95%.
- За Redis, с 64 нишки и повече, изпълняването често е неуспешно с изключения за изчакване на изчакване.
Вмъкване на производителност на натоварването
И накрая, тук са числата за пропускателна способност/закъснение от 2,5 милиона работно натоварване за вмъкване на записи. Броят на записите е избран, за да се гарантира, че общата памет е била използвана в събитие Redis, което не надвишава 80% (тъй като Redis е основната памет, вижте Приложение Б).
Забележка:Пропускателната способност е нанесена спрямо основната ос (вляво), докато латентността е нанесена спрямо вторичната ос (вдясно).
Наблюдения по време на изпълнение на вмъкване на работно натоварване:
- За MongoDB процесорът беше наситен с 32 нишки нагоре. По-голямо от 300% използване с едноцифрени проценти на празен ход.
- За Redis използването на процесора никога не надхвърля 95%.
Приложения
A:Еднонишкова производителност
Имах силно желание да разбера това – въпреки че не е много полезно в реални условия:кой би бил по-добър, когато прилага едно и също натоварване към всеки от тях от една нишка. Тоест как би се представило еднонишково приложение?
B:Размер на базата данни
Форматът по подразбиране на записите, вмъкнати от YCSB, е:всеки запис е от 10 полета и всяко поле е 100 байта. Ако приемем, че всеки запис е около 1KB, общият очакван размер в паметта ще бъде над 2,4GB. Имаше ярък контраст в действителните размери, както се вижда в базите данни.
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 }
И така, заетото пространство е ~2,7 GB което е доста близо до това, което очаквахме.
Redis
Нека сега да разгледаме 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
При пикова употреба изглежда, че Redis отнема около 5,72G памет, т.е. два пъти повече памет, отколкото заема MongoDB. Сега това сравнение може да не е перфектно поради разликите в двете бази данни, но тази разлика в използването на паметта е твърде голяма, за да се игнорира. YCSB вмъква запис в хеш в Redis и индексът се поддържа в сортиран набор. Тъй като отделният запис е по-голям от 64, хешът се кодира нормално и няма спестяване на пространство. Ефективността на Redis идва на цената на увеличената памет.
Според нас това може да бъде важен момент при избора между MongoDB и Redis – MongoDB може да е за предпочитане за потребители, които се грижат за намаляване на разходите за памет.
C:Пропускателна способност на мрежата
Сървърът на базата данни в паметта може да бъде или свързан към изчисления, или към мрежов I/O, така че беше важно през целия набор от тези тестове да се гарантира, че никога няма да бъдем обвързани с мрежа. Измерването на пропускателната способност на мрежата по време на провеждане на тестове за пропускателна способност на приложенията се отразява неблагоприятно върху цялостното измерване на пропускателната способност. И така, проведохме последващи измервания на пропускателната способност на мрежата, използвайки iftop при броя на нишките, където се наблюдава най-високата пропускателна способност на запис. Установено е, че това е около 440 Mbps както за Redis, така и за MongoDB при съответната им пикова пропускателна способност. Като се има предвид, че първоначалното ни измерване на максималната мрежова честотна лента е около 1,29 Gbps, ние сме сигурни, че никога не достигаме границите на мрежата. Всъщност това само подкрепя извода, че ако Redis беше многоядрен, може да получим много по-добри числа.