company_banner

KeyDB как [потенциальная] замена Redis

    На хабре не нашлось обзоров «более быстрой альтернативы Redis» — KeyDB. Получив достаточно свежий опыт его использования, хочется восполнить этот пробел.



    Предыстория достаточно банальна: однажды с большим наплывом трафика была зафиксирована значительная деградация производительности приложения (а именно — времени ответа). На тот момент, к сожалению, не удалось провести нормальную диагностику происходящего, поэтому впоследствии запланировали ряд нагрузочных тестирований. После их проведения удалось обнаружить узкое место, коим стал кэш базы данных в Redis. Как это часто бывает, проблему нельзя было решить сию секунду и правильным путём — силами разработчиков (изменением логики работы). Поэтому включилось любопытство и желание побороть ситуацию обходным путём. Так и появилась эта статья.

    Проблематика


    Про Redis в целом


    Как многим известно, Redis — однопотоковая база данных. Если быть точнее, то она такова в контексте работы с пользовательскими данными. Ведь с четвертой версии служебные, внутренние операции Redis перевели на параллельное исполнение. Тем не менее, это изменение затронуло лишь малую часть нагрузки, поскольку основная работа приходится на пользовательские данные.

    На эту тему сломано бесчисленное множество копий, но разработчики Redis упорно не хотят внедрять полноценную параллельность, упоминая, насколько это усложнит приложение и повысит накладные расходы, а также добавит количество багов. Их позиция такова: если вы столкнулись с проблемой одноядерности — у вас проблемы с архитектурой приложения и надо что-то менять в ней. Среди пользователей, впрочем, есть и «другой лагерь» — из тех, кто упёрся в одно ядро и утверждает, что Redis сами себе создают бутылочное горлышко. В случае реально больших нагрузок — рано или поздно — неизбежно столкновение с этой проблемой, что накладывает значительные ограничения на архитектуру и/или вынужденные усложнения в ней.

    Не буду давать оценку тому или иному мнению. Вместо этого поделюсь нашим конкретным случаем и тем, как мы его решили.

    Наш кейс


    У одного из проектов мы столкнулись с тем, что команда разработки настроила крайне агрессивное кэширование данных из БД (PostgreSQL) через Redis. Это был единственный путь, который во время резких наплывов трафика спасал от смерти саму PostgreSQL и, как следствие, приложение.

    После серии нагрузочных тестов мы провели анализ ситуации и обнаружили, что Redis упирался в одно ядро (что называется, «в полку»), после чего следовала довольно быстрая деградация приложения. «Захлебывание» имело геометрическую прогрессию: как только достигался лимит производительности у Redis, всё переставало работать.

    Выглядело это примерно так:



    Со стороны New Relic однозначно идентифицировалась проблема:



    А вот статистика по операции get в Redis:



    После того, как проблему донесли во всех подробностях разработке, выяснилось, что «прямо сейчас решить проблему нельзя». Так начались поиски решения на стороне эксплуатации, и ответом стала уже упомянутая KeyDB.

    Однако перед тем, как приступить к её обзору, стоит упомянуть, что в проекте используется standalone Redis, поскольку кластерное решение на основе Sentinel сильно уступает по задержкам (latency). Одним из очевидных решений было создание нескольких реплик кэша: и пусть приложение ходит повсюду с балансировкой! Однако, посовещавшись с разработчиками, мы были вынуждены отбросить этот вариант ввиду активного и сложного механизма инвалидации кэша у приложения. Та же проблема распространялась и на шардирование кэша.

    Беглый обзор KeyDB


    В поисках возможного решения проблемы мы обнаружили приложение под названием KeyDB. Это форк Redis, разработанный канадской компанией и распространяемый под свободной лицензией BSD. Проект весьма молод: он существует с начала 2019 года. История его такова, что авторы тоже однажды столкнулись в свое время с ограничениями Redis… и решили сделать свой форк. Причём он не только решил известные проблемы, но и получил дополнительные возможности, которые доступны только в enterpise-версии Redis.

    Для желающих подробнее ознакомиться с KeyDB есть хорошая вводная статья на Medium, которая представляет СУБД и краткие benchmark’и, сравнивающие её со своим «родителем» — Redis.

    Прежде всего, нас привлекло в KeyDB потенциальное решение наших проблем, а заодно были интересны и некоторые дополнительные фичи. Использование KeyDB обещало следующие плюсы:

    • получение полноценной многопоточности;
    • полная и абсолютная совместимость с Redis (для нас это было особенно важно, т.к. выполнять какие-либо доработки со стороны приложения не представлялось возможным), что также сулило беспроблемную миграцию;
    • встроенный механизм бэкапа в S3-хранилище;
    • простая во внедрении active-репликация;
    • простая кластеризация и шардирование без Sentinel и прочего вспомогательного ПО.

    Более 3 тысяч звезд и множество контрибьюторов на GitHub также выглядели обнадеживающе. Приложение достаточно активно развивается и поддерживается, что хорошо заметно по коммитам, общению в issues, а также закрытым (принятым) PR. Отклик от основного мейнтейнера по всем фронтам всегда доброжелательный и оперативный. В общем, аргументов оказалось предостаточно.

    Миграция и результаты


    Даже несмотря на то, что проект миграции был своеобразной авантюрой (ввиду новизны KeyDB), терять было особо нечего. Ведь откатить изменения достаточно быстро и просто — благо, вся инфраструктура развёрнута в Kubernetes, а встроенные механизмы Rolling Update отлично решают такие задачи.

    В общем, мы подготовили Helm-шаблоны, переключили приложение в тестовом окружении на новую БД и выкатили это всё, отдав в QA-отдел клиента.

    Началось тестирование, которое продолжалось около недели и в детали которого мы не погружались. Нам известно лишь о том, что заказчик проверил стандартные функции работы с Redis с помощью PHP-драйвера phpredis, а также провёл QA-тестирование пользовательского интерфейса. После этого нам дали зеленый свет: никаких побочных эффектов в использовании нового софта не обнаружилось. То есть с точки зрения приложения вообще ничего не изменилось.

    Стоит отметить, что и в конфиге мы ничего не изменили: буквально — просто заменили используемый образ. То же самое касается мониторинга и экспорта метрик в Prometheus: самый распространенный из них отлично работает с KeyDB и без каких-либо доработок. Таким образом, можно смело сказать, что и с точки зрения эксплуатации это просто идеальный переезд.

    Благодаря всему этому, после переключения приложения на новую СУБД можно ничего не менять, а в качестве «стабилизационной меры» — оставить его в таком виде поработать в бою на какое-то время. Однако, если вы хотите увидеть прирост производительности (либо вообще хоть какие-то изменения), надо не забывать, что по умолчанию параметр KeyDB, отвечающий за многопоточность (server-threads), равен единице, то есть СУБД работает ровно так же, как Redis.

    После переключения, тестирования и некоторого времени жизни на новом приложении (с KeyDB) мы решили повторить нагрузочное тестирование с теми же параметрами, что использовались для Redis. Каковы были его результаты?..

    По графику потребления CPU сразу стало заметно устранение проблем с «потолком» в одно ядро: процесс начал использовать доступные ресурсы:



    А впоследствии я пробовал достаточно сильно «истязать» приложение и увидел потребление вплоть до трёх ядер…

    По показаниям New Relic, веб-приложение в целом, имея такую же нагрузку, стало вести себя заметно адекватнее. Некоторая деградация производительности всё равно наблюдалась, однако, сравнивая с аналогичным графиком выше, можете сами оценить существенный прогресс:



    Показатель задержки у новой базы данных (KeyDB) тоже ухудшился, однако оставался в пределах допустимых значений:



    По следующему графику хорошо видно, что количество запросов в саму KeyDB аналогично:



    Подводя итог по этим синтетическим тестам, можно сказать, что и Redis, и KeyDB показывают значительную деградацию производительности в latency (40 мс+) при существенном росте количества параллельных подключений (1000+). В нашем же случае веб-приложению удавалось «просаживать» latency Redis’а и при более низком количестве подключений (400+), хотя для KeyDB такая нагрузка оставалась приемлемой.

    Выводы


    На данном примере прекрасна видна сила Open Source-сообщества в вопросах развития проектов, в которых оно заинтересовано. На просторах интернета мне встречалось отличное высказывание, общий смысл которого сводился к следующему: «Какая-то крупная компания создаёт интересный продукт, делает часть его функций открытой, но самую важную часть оставляет платной. Сообщество пользуется-пользуется, а потом кто-то махнёт рукой и сделает форк, реализовав в нём те самые платные фичи и открыв их для всех». Вот KeyDB — тот самый случай.

    Говоря же о самой миграции, которая прошла на удивление просто, мы не получили настолько существенный прирост в производительности, какого можно ожидать, глядя на графики авторов KeyDB… Однако это лишь наш частный случай, в котором может быть множество отклонений, касающихся в том числе и пресловутой архитектуры приложения (например, огромное количество команд get в Redis вместо более производительного варианта агрегированных запросов mget…). Тем не менее, положительных результатов удалось добиться, а вместе с ними — множество полезных функций, которые мы ещё только будем внедрять в ближайшее время.

    В целом, KeyDB выглядит перспективно: по мере получения практического опыта работы с этой СУБД (а его ещё только предстоит набраться!) и развития самого проекта мы рассмотрим возможность его применения и в других ситуациях.

    Однако не стоит рассматривать эту статью как руководство (и тем более — призыв) к действию по повсеместному отказу от Redis в пользу KeyDB. Несмотря на наш позитивный опыт, очевидно, что это не серебряная пуля. Случай был весьма специфичным: конкретно для решения сиюминутной проблемы в ситуации, когда нужно было сделать это быстро и с минимальными затратами, такое решение себя оправдало. Будет ли KeyDB полезен в вашем случае? По крайней мере, теперь вы знаете, что такая потенциальная возможность существует.

    P.S.


    Читайте также в нашем блоге:

    Флант
    Специалисты по DevOps и Kubernetes

    Комментарии 39

      0
      А Helm chart где-нибудь можно увидеть? Хотелось бы нормальный master-master — насколько я понимаю ни в Redis, ни в Redis-HA чартах такого нет сейчас.
        0
        Мы не публиковали его нигде, там в целом ничего хитрого. Если есть реальная потребность — могу соорудить.
          0
          Больше — не меньше. Я сам сейчас буду пилить (или воровать у китайцев) redis-cluster, поэтому неплохо было бы посмотреть на уже имеющиеся варианты. Буду признателен.
            +2
            Кстати, скоро (постараемся на следующей неделе) у нас будет статья про наш опыт с одним K8s-оператором для Redis.
          +1
          Я собрал свой велосипед на коленке масштабируемый active-active, но не верен в стабильности)
          github.com/Negashev/keydb-cluster
            0
            Видел сегодня, когда пытался найти решение на гитхабе.
          +2
          Уточните, как кластерный редис (с сентинелом [который давно уже не актуален], либо нативный кластер) добавляет дополнительных задержек?
            +1
            К сожалению у нас нет адекватного решения для redis cluster в кубах, поэтому пока используется sentinel.
            В случае с sentinel существует дополнительный сетевой хоп, т.к. приложение сначала идет в sentinel-proxy, после чего уже попадает в сам редис, конечно же это касается тех приложений, который не поддерживает нативную работу с sentinel.
            В таком случае по идее проблем не должно быть, т.к. там ассинхронные операции и напрямую влиять на клиентские get'ы и mget'ы не должны.
              +1

              Думаю, либо ваш коннектор не умеет сам redis-sentinel, либо вы не догадались его приготовить.


              Если коннектор умеет redis-sentinel, он будет ходить напрямую, без proxy.

            +7
            разработчики Redis упорно не хотят внедрять полноценную параллельность, упоминая, насколько это усложнит приложение и повысит накладные расходы

            Ну вы наверное упорно не понимаете философию редис. Обвинять редис в том что он однопоточный, это всё равно что обвинять Си, в том что он компилируемый, Go в том что статически типизированный, а mysql в том что таблицы нужно декларировать, прежде чем использовать.

            У одного из проектов мы столкнулись с тем, что команда разработки настроила крайне агрессивное кэширование данных из БД (PostgreSQL) через Redis

            Могли бы запустить по одному редису на каждое ядро. При кэшировании, по ключу кэша, вычислять (например взять последние 3 бита от md5 ключа) в каком из редисов это будет храниться. Тогда бы ваша система выполняла свои функции вообще без межпроцессного/межпотокового взаимодействия, что было бы быстрее. Но вы взяли другой инструмент и теперь ваша система синхронизируется между потоками и переключает контекст там, где это не нужно.
              +1
              Ну вы наверное упорно не понимаете философию редис. Обвинять редис в том что он однопоточный…

              А вы точно дочитали до:

              Не буду давать оценку тому или иному мнению.

              ? :-)
                +1
                Посыпались минусы, но я лишь хотел указать, что в статье нет обвинений. Есть указания на разные мнения и пути. И нет утверждений, что этот путь — правильный. Он просто есть, и раз для его реализации появился полноценный продукт, на него есть объективный спрос.
                  0
                  Т.е. вы героически воткнули костыль, вместо того, чтобы бороться с невежеством разработки на проекте? И эти люди говорят про DevOps как методологию и отсутствие «колодцев» между командами, ха.
                    +5
                    Вы же понимаете насколько это упрощенно и однобоко звучит? :)
                    К сожалению мир не делится на белый и черный. Есть моменты когда мы можем бороться, есть — когда нет. Есть моменты когда бизнес может идти на встречу, а есть — когда физически нет таких возможностей. Мы стараемся идти на встречу при любом раскладе дел.
                      +5
                      Эти люди всё-таки живут в реальном мире. Обратите внимание, что статья сознательно не добавлена в хаб «DevOps». Нас даже иногда критикуют за то, что мы в своих статьях/докладах часто рассказываем про какую-то мифическую идеальную инфраструктуру и сопутствующие процессы. Мол, всё это круто и красиво, но в жизни обычно по-другому.

                      Вот эта статья — это не best practice [о чём явно в ней написано]. Это иллюстрация опытного эксперимента, какие тоже имеют место в жизни (в определённых случаях), даже если мы стремимся к лучшему. Реализуя его, стоит учитывать проблемы, помнить, что не это идеал, к которому надо идти, понимать, как всё-таки должно быть в перспективе.
                  +2

                  Вот я сейчас использую redis-cluster. 16 физических шардов по три процесса на сервер по две реплики. Т.е. 32 сервера, 48 редисных шардов, 96 редисных процессов. И я в серьез подумываю про KeyDB, т.к. это позволит мне сэкономить гору процессорного времени. Почему? Да потому что на 48 шардов очень плохо работает пайплайнинг запросов. А он экономит цпу как на клиенте, так и в самом редисе. А с keydb у меня будет всего 16 шардов.


                  Останавливает меня только репликация: сейчас каждый редисный шард по 6GB, и мне приходится применять не тривиальную конфигурацию, чтобы редис физически был способен отреплицировать после рестарта реплики без отвала по достижению своих внутренних лимитов. Я сильно сомневаюсь, что KeyDB сможет отреплицировать 18GB.

                    +3
                    Про пайплайнинг замечание здравое. Но что-то у меня сомнения что KeyDB с оверхедом от многопоточности будет быстрее чем редис с оверхедом от отсутствия пайплайнинга. Или будет?
                    И что за кейс где нужен пайплайнинг — это же не кэширование?
                      0
                      > KeyDB с оверхедом от многопоточности будет быстрее чем редис с оверхедом от отсутствия пайплайнинга. Или будет?

                      Будет. Оверхед от многопоточности в KeyDB — это взять мьютекс перед походом в хэш-мап, и отпустить после.

                      > И что за кейс где нужен пайплайнинг — это же не кэширование?

                      Как это «не кэширование»? У нас весь бэкенд шлёт до 2М запросов в секунду в редис именно за кешем. «не кэш» использования редиса у нас очень мало.

                      Просто наш коннектор умеет делать «прозрачный пайплайнинг», когда запросы из параллельных горутин в один сокет пишутся.
                        0
                        Ага, у вас какой-то более другой кейс пайплайнинга, чем в языках, где взаимодействие разных потоков не так легко достигается.

                        В них обычно условие для пайплайнинга — есть N команд, но на первые N-1 ответ не нужен срочно. А нужен только в конце.

                        Т.е. вот 4 команды
                        SET X 1
                        SET Y 2
                        GET W
                        GET Z

                        и нам ответ нужен только в конце — это W и Z. Нам не нужен W до этого момента и тем более результаты установки X, Y. В этом случае пайплайнинг позволяет послать 4 команды, а потом ждать ответа.

                        У вас же такого нет, просто из разных процессов всё идёт через один сокет. И в клиенте у вас конкурентность и на redis-server. А могло бы всё идти по разным сокетам и без конкурентности… В чём профит, непонятно. Разве что какие-то внутренние проблемы редиса с
                        большим кол-вом сокетов.

                        Да потому что на 48 шардов очень плохо работает пайплайнинг запросов. А он экономит цпу как на клиенте, так и в самом редисе


                        Т.е. 8 клиентов на 8 редис процессов могли бы идти по 8 сокетам. Все на разных ядрах.
                        А у вас 8 клиентов идут на 8 редис тредов через 1 сокет все в перемешку.
                        И профит в том что и клиент и сервер жрут меньше CPU. Непонятно…

                          0
                          > А могло бы всё идти по разным сокетам и без конкурентности… В чём профит, непонятно. Разве что какие-то внутренние проблемы редиса с большим кол-вом сокетов.

                          Когда начнёте профилировать, поймёте: экономия цпу от 10% до 40%. Когда это экономится на клиенте, это не так может заметно. А вот когда в редисе, то очень чувствительно!
                            0
                            > Т.е. 8 клиентов на 8 редис процессов могли бы идти по 8 сокетам. Все на разных ядрах.
                            А у вас 8 клиентов идут на 8 редис тредов через 1 сокет все в перемешку.

                            «Клиентов» в одном процессе — десятки. Редисов всего 96. Если по вашей логике, то это 1000 сокетов с одного только процесса клиента. А тачек клиентов у нас тоже не один десяток.
                    +4
                    Однониточность редиса дает офигенные гарантии атомарности и консистентности. В этом вся суть. (так же было и в nodejs, и я считаю недавнее введение там многониточности огромной ошибкой, теперь надо будет отлаживать трудноуловимые баги одновременной модификации данных. Причем вы даже можете не подозревать о существовании нитки, а сторонняя библиотека внутри создаст нитку и будет ваши колбэки дергать из нее).

                    Как только вы задумаетесь об атомарности и/или транзакциях в вашей многониточной БД, вам придется городить механизм локов, возможно механизм обнаружения дэдлоков, отката транзакций итд итп. И все это приведет к тому, что 99% перцентили у вас уедут в небеса.

                    Выше правильно написали, масштабировать в XXI веке не горизонтально — тупиковый путь, который не работает в большой перспективе.
                      0
                      И мы не спорим с этим! :)
                      Но и не мы разработчики той системы которую обслуживаем. А провести рефактор кода далеко не всегда возможно, и нам надо выкручиваться исходя из существующих вводных данных, а не того, чего хотелось бы в идеале.
                        0
                        Причем вы даже можете не подозревать о существовании нитки, а сторонняя библиотека внутри создаст нитку и будет ваши колбэки дергать из нее).

                        А разве функцию можно передать между потоками?

                          0

                          Это ж как надо расстреливать ноги, чтобы в nodejs нитка, о существовании которой я не подозреваю, дёргала "мои" колбеки?)

                            0

                            Ой да ладно. Проблемы с транзакциями существуют когда в одной транзакции трогается несколько ключей. Если же каждый раз щупается один ключ, то ни тебе дедлоков, ни тебе отката трагзакций. Все очень даже неплохо масштабируется, если правильно готовит.


                            Да, в редисе есть возможность использовать и что-то, что они называют транзакциями, и Lua процедуры. Но на практике этим пользуются невероятно редко.


                            Кстати, в KeyDB весь выигрыш только из распараллеливания сетевого взаимодействия. Вся работа с данными идет под глобальным локом.
                            Т.е.:
                            а) сетевое взаимодействие настолько накладно, что есть выигрыш от мношопоточности
                            б) типичная работа с данными настолько быстрая (в redis/keydb), что делать ее под глобальным локом не больно. А значит нет дедлоков и откатов транзакций.
                            в) конечно, если поставить keydb на 32 ядерную машину, проблемы от глобального лока начнутся. Так что им есть куда расти.

                              +2
                              > что они называют транзакциями, и Lua процедуры. Но на практике этим пользуются невероятно редко

                              Ну как так «невероятно редко». У нас например невероятно часто. Это же зависит от кейса — если кэш, то они не нужны. Если хранение данных, то нужны «часто».
                                –3
                                > Если хранение данных, то нужны «часто».

                                Храните данные в Redis?… у вас стальные яйца, я вам скажу. Зная, как работает Redis, я бы не стал в нём ни чего хранить.

                                Если вам нужно inMemory, посмотрите хотя бы в сторону Tarantool. Он намного надёжнее в плане сохранности. А так же гибче. И вряд ли медленнее.
                                  +1
                                  сессии юзеров, фоновые задачи, csrf токены, межсервисные локи, различные другие токены, коды подтвеждений sms — всё отлично там хранится.
                                  у редиса два механизма записи на диск — снапшоты и AOF. не вижу повода не хранить там даже важные данные.
                                    0
                                    > сессии юзеров, фоновые задачи, csrf токены, межсервисные локи, различные другие токены, коды подтвеждений sms — всё отлично там хранится.

                                    Т.е. любые данные, которые вроде и не хотелось бы потерять, но если потеряются, то и хрен с ними. Да, такое можно.

                                    > у редиса два механизма записи на диск — снапшоты и AOF. не вижу повода не хранить там даже важные данные.

                                    А я не вижу повода хранить:
                                    — AOF и snapshot к сожалению друг о друге не знают.
                                    — snapshot делается редко. Его рассматривать вообще нет смысла.
                                    — запись в AOF идёт в том же треде. Если диск притормаживается (реплика подключилась), то всё встаёт колом. И ЧТЕНИЕ ТОЖЕ!
                                    — а диск в том числе подтормаживает, когда AOF файл захотел сделать себе rewrite. Ну или если реплика приконнектилась.
                                    — AOF не содержит номера команд. Потому Redis не может его использовать для наливки слейва. И слейв не может его использовать, если вдруг рестартанулся. Единственный выход у Редиса — откладывать новый снапшот (здравствуйте тормоза диска) и копить изменения в памяти. Чтобы отреплицировать 6ГБ инстанс мне приходится в настройках держать `client-output-buffer-limit replica 0 0 0` и `repl-backlog-size 200mb`.

                                    Для сравнения, Tarantool:
                                    — снапшоты и xlog файлы образуют единый механизм,
                                    — xlog файлы содержат номер каждой операции
                                    — реплика читает свой снапшот, проигрывает свои xlog и только после этого коннектится к мастеру и читает с последней операции
                                    — мастер сперва шлёт данные с xlog файлов с нужного места (переслав перед этим уже готовый снапшота, если реплика новая или слишком отстала), и только когда реплика догоняет, переключается на inmemory посылку (емнип).
                                    — данные в xlog пишутся в соседнем треде. Основной тред продолжает работу и спокойно обслуживает read запросы (и готовит к записи новые write запросы) даже если диск подтупил слегка.
                                0
                                Да, в редисе есть возможность использовать и что-то, что они называют транзакциями, и Lua процедуры. Но на практике этим пользуются невероятно редко.


                                ;)))) ну вы поняли
                                  0
                                  Допустим, у нас Lua процедуры интенсивно используются в Redis. Но работают в основном с одним ключом (с одним Hash). В redis-cluster вообще сильно не попользуешься «разными ключами». Можно, конечно, если использовать фигурные скобки а-ля `namespace:{my-shard-key}-subkey`, но напряжно.

                                  А что я должен был понять?
                              +2
                              Кстати, не очень понятны вводные данные, сколько запросов в секунду редис обслуживал. Если предположить, что на каждый из 449000 CPM приходится один запрос в редис, то это около 7000 запросов в секунду. Что довольно мало для редиса. Он точно может тянуть в разы больше. Либо у вас гораздо больше фактических запросов, либо там не просто GET, либо где-то проблемы с настройкой системы.
                                0
                                А вот статистика по операции get в Redis:


                                А что подразумевается под «cpm», можно уточнить?
                                0
                                А можно приоткрыть завесу над инвалидацией кеша?
                                Что именно мешало в ней шардировать редис?
                                  0
                                  А вы не профилировали Redis? Было интересно посмотреть во что он упирается.
                                    0
                                    Очень хотелось!
                                    Но как всегда — на проде невозможно, кто же нам даст терзать прод в свою волю. А чтобы это воспроизвести не на проде — нужно поднимать идентичный тестовый стенд, на что конечно же нет ресурсов.
                                      +2
                                      Учитывая, что это php, и вряд ли они делали руками пайплаининг, редис упирался в sys cpu: чтение/запись в сеть.

                                      С хорошим pipelining редис может миллион rps на половинке ядра выдать.

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                    Самое читаемое