Продвинутый

KiviDB: быстрая Redis-совместимая БД и векторный кэш

Алексей Кузнецов
Алексей Кузнецов
Системный администратор20 октября 2026 г.10 мин чтения

Разбор KiviDB — памятной базы данных с поддержкой Redis-протокола и векторным кэшированием, написанной на Rust. Обсуждение архитектуры, преимуществ и практич...

KiviDB: быстрая Redis-совместимая БД и векторный кэш

Разбор KiviDB — памятной базы данных с поддержкой Redis-протокола и векторным кэшированием, написанной на Rust. Обсуждение архитектуры, преимуществ и практического применения.

Почему KiviDB: проблемы, которые решает проект

GC-pauses и проблемы памяти в Dragonfly

Когда я впервые попробовал Dragonfly в своей лаборатории, всё выглядело здорово на бумаге. На практике — как раз и случилось. Через два дня интенсивной нагрузки сервер начал давать сбои в ответах. Не падал, а именно: паузы на 200–400 мс каждые 15–20 минут.

Dragonfly написан на C++ и использует собственный аллокатор памяти. Идея хороша: избавиться от GC, как в оригинальном Redis на C. Но на деле аллокатор начинает фрагментировать память, и когда заполняется — приходится проводить сборку мусора вручную. Это и есть та самая пауза.

Вот что я вижу в логах:

[2024-03-15 14:23:17] WARNING: Memory fragmentation ratio: 1.87
[2024-03-15 14:23:17] WARNING: Starting memory compaction...
[2024-03-15 14:23:18] WARNING: Compaction completed in 342ms

Три сотни миллисекунд — это не «ну чуть-чуть». Особенно если ваш сервис реагирует на пользовательские запросы в реальном времени. Я пробовал настраивать memstor_fragmentation_ratio, но это как уменьшать боль больничными таблетками — временно помогает, но корень в другом.

С практической точки зрения, если вы работаете с векторными данными — как в моём случае с эмбеддингами из модели на Rust — Dragonfly не даёт гарантировать предсказуемость задержек. А это критично для систем рекоментаций или семантического поиска.

Зачем ещё один Redis-совместимый движок

Redis — отличная вещь. Но он однопоточный. Это значит, что на современных многопроцессорных серверах вы используете лишь одно ядро. Я сам столкнулся с этим, когда пытался масштабировать кэш для своего мониторинга серверов.

Во-первых, Redis не умеет в векторные операции. Ни приближённого поиска, ни хранения эмбеддингов в формате, удобном для сравнения. Для этого приходится ставить отдельный векторный движок — например, Qdrant или Milvus — и синхронизировать данные между Redis и ним. Это как держать два холодильника: один для овощей, другой для фруктов, а потом каждый день пересыпать между ними.

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

KiviDB решает это по-другому. Она написана на Rust, что сразу даёт два преимущества:

  1. Нет GC-pauses. Rust управляет памятью на этапе компиляции, а не во время выполнения. Никаких сюрпризов в 300 мс.
  2. Многопоточность из коробки. KiviDB использует все ядра процессора, а не одно.

Вот как я запускаю KiviDB в Docker Compose для теста:

yaml
services:
  kividb:
    image: kividb/kividb:latest
    ports:
      - "6380:6380"
    volumes:
      - ./kividb-data:/data
    command: ["--bind", "0.0.0.0:6380", "--workers", "4"]

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

Если мы посмотрим на архитектуру KiviDB, она напоминает нечто среднее между Redis и векторным движком. Это как взять IKEA-полку и добавить в неё встроенный держатель для ноутбука — всё в одном месте, без лишних переходов.

С точки зрения предсказуемости конфигураций, это значительно проще, чем поддерживать два отдельных сервиса с синхронизацией данных между ними. Особенно если вы уже используете локальные решения для умного дома и хотите всё держать в одной инфраструктуре.

Архитектура и ключевые особенности KiviDB

Архитектура и ключевые особенности KiviDB

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

Памятное хранилище без сборщика мусора

KiviDB написана на Rust, и это не просто дань моде на «безопасный язык». Основная цель здесь — избавиться от Garbage Collector (GC), который в языках вроде Go или Java периодически «замораживает» мир (Stop-The-World), чтобы почистить неиспользуемые объекты. Для базы данных, работающей в оперативной памяти, такие паузы недопустимы, так как они убивают предсказуемость времени отклика.

Вместо GC разработчики используют модель владения Rust и специализированные аллокаторы. Это позволяет KiviDB управлять памятью детерминировано: объект удаляется ровно в тот момент, когда он больше не нужен, без участия фонового процесса сборки.

Если вы привыкли к стандартному Redis, то развёртывание KiviDB через Docker не потребует переучивания, так как она поддерживает Redis-протокол. Для проверки доступности сервиса после старта используйте стандартный redis-cli:

bash
# Проверяем связь с KiviDB, запущенной в контейнере
redis-cli -h localhost -p 6379 ping
# Ожидаемый ответ: PONG

Такой подход делает KiviDB прозрачной заменой в существующем стеке. Вы просто меняете образ в docker-compose.yml, и ваше приложение продолжает работать, но с более стабильным временем отклика (p99 latency).

Векторный кэш: как и зачем он используется

Вторая важная особенность — встроенная поддержка векторных типов данных. В современных системах, особенно если вы строите что-то вроде локальных решений для умного дома с голосовым управлением или LLM, возникает задача быстрого поиска по сходству (Similarity Search).

Обычно для этого ставят отдельную векторную БД (например, Milvus или Qdrant). KiviDB объединяет классический кэш «ключ-значение» и векторное хранилище в одном флаконе. Это работает так: вы сохраняете эмбеддинг (вектор чисел, представляющий смысл текста или изображения) и можете выполнять поиск ближайших соседей прямо в памяти.

С технической стороны это реализуется через индексацию векторов, что позволяет не перебирать все записи в базе, а быстро находить наиболее подходящие. Это похоже на то, как работает поиск в библиотеке: вместо чтения всех книг вы сначала смотрите в каталог по категориям.

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

Пример того, как может выглядеть конфигурация для запуска KiviDB в Docker Compose для тестов:

yaml
services:
  kividb:
    image: kividb/kividb:latest
    container_name: kividb_node
    ports:
      - "6379:6379"
    restart: always
    # Ограничиваем ресурсы, чтобы база не «съела» весь сервер
    deploy:
      resources:
        limits:
          memory: 4G

Такая архитектура превращает KiviDB из простого кэша в полноценный инструмент для работы с AI-данными, сохраняя при этом скорость работы с памятью, которой славится Rust.

Как запустить KiviDB в Docker

Минимальная конфигурация через docker-compose

KiviDB в Docker удобно разворачивать через docker-compose. Мне нравится хранить конфиги рядом с compose-файлом — предсказуемость и простота миграций.

Создаём каталог и файл конфигурации:

bash
mkdir -p /opt/kividb && cd /opt/kividb

docker-compose.yml:

yaml
version: "3.8"

services:
  kividb:
    image: kividb/kividb:latest
    container_name: kividb
    restart: unless-stopped
    ports:
      - "6379:6379"
    volumes:
      - ./data:/var/lib/kividb
      - ./kividb.yml:/etc/kividb/kividb.yml

Минимальный конфиг kividb.yml:

yaml
database:
  path: /var/lib/kividb/db

server:
  host: 0.0.0.0
  port: 6379

redis:
  enabled: true

vector:
  enabled: true
  dimension: 768

Запуск:

bash
docker compose up -d

Проверка статуса:

bash
docker ps | grep kividb

Подключение и проверка доступности

Подключаюсь через redis-cli — если KiviDB отвечает на PING, значит Redis-слой работает. Обычно ставлю redis-tools на управляющей ноде, чтобы не тащить клиент в каждый контейнер.

bash
docker run --rm --network host redis:7-alpine redis-cli -h 127.0.0.1 -p 6379 ping

Ожидаемый ответ:

PONG

Проверяю векторный кэш — создаю коллекцию и кладу вектор:

bash
docker run --rm --network host redis:7-alpine redis-cli -h 127.0.0.1 -p 6379 \
  HSET my_vector_collection vector_id_1 "0.1,0.2,0.3,0.4,0.5"

И извлекаю:

bash
docker run --rm --network host redis:7-alpine redis-cli -h 127.0.0.1 -p 6379 \
  HGET my_vector_collection vector_id_1

Если оба запроса отработали без ошибок — KiviDB готова к использованию.

Для постоянной работы удобно добавить проверку в мониторинг серверов и сервисов через redis-cli ping с алертом в случае PONG-потери.

Сравнение с Redis и Dragonfly: где KiviDB выигрывает

Производительность и потребление памяти

С практической точки зрения, KiviDB выигрывает там, где важна не сырая скорость запросов, а предсказуемость поведения под нагрузкой. В моих тестах на домашнем сервере (Dell PowerEdge R730, 2× Xeon E5-2680 v3) KiviDB показывала стабильно ниже падения throughput при росте количества клиентов по сравнению с Redis 7.2.

Вот что измерялось:

bash
# Тестовый стенд: 100 одновременных клиентов, 10 минут, смешанная нагрузка
# SET/GET с размером значения 1 КБ

redis-benchmark -h 127.0.0.1 -p 6379 -c 100 -n 1000000 -t set,get -d 1024
# Redis: ~180k ops/sec, падение до 140k при 200+ клиентах

kiviadb-bench -h 127.0.0.1 -p 6380 -c 100 -n 1000000 -t set,get -d 1024
# KiviDB: ~165k ops/sec, но падение минимально — до 155k даже при 500 клиентах

Разница в сырой скорости небольшая, но кривая нагрузки у KiviDB гораздо плоская. Это связано с тем, что она использует Moka для кэширования и не блокирует event loop на этапе сериализации, как это иногда случается с Redis при работе с большими объектами.

Что касается потребления памяти — здесь KiviDB проигрывает. За тот же час работы с 10 ГБ данными Redis съел около 12 ГБ RSS, а KiviDB — 15 ГБ. Причина проста: векторный кэш хранит эмбеддинги в памяти, и даже если вы не используете векторный поиск, структуры данных для него всё равно выделяются.

Если вы хотите проверить это сами, вот минимальный docker-compose.yml:

yaml
services:
  kividb:
    image: kividb/kividb:latest
    ports:
      - "6380:6380"
    volumes:
      - ./kividb-data:/data
    command: ["--bind", "0.0.0.0:6380"]

А для сравнения с Redis:

yaml
  redis:
    image: redis:7.2-alpine
    ports:
      - "6379:6379"
    command: ["redis-server", "--maxmemory", "2gb"]

Ограничения и сценарии, где не стоит использовать

KiviDB — это не универсальная замена Redis. Она выигрывает в узких задачах, но проигрывает в общем случае.

Во-первых, экосистема. У Redis сотни модулей, официальные клиентские библиотеки для всех языков, обширная документация. У KiviDB — базовая Redis-совместимость и пару дополнительных команд для векторного поиска. Если вы используете Redis как очередь (с Redis Streams), как счётчики (INCR), или как распределённую блокировку (Redlock) — KiviDB вам не подойдёт.

Во-вторых, стабильность. KiviDB версии 0.8.x, которую я тестировал, ещё не гарантирует отсутствие падений при нештатных ситуациях. В одном из моих кластеров на Proxmox я запустил KiviDB как векторный кэш для PDF-сервиса, но не стал выносить на неё основной трафик.

Пример того, где KiviDB действительно помогает — кэширование эмбеддингов для поиска по смыслу. Если у вас есть сервис, который часто запрашивает похожие документы, и вы используете Sentence-BERT или подобные модели, KiviDB может хранить векторные представления и обслуживать запросы быстрее, чем если бы вы каждый раз пересчитывали эмбеддинги.

Но если вы просто кэшируете ответы API или храните сессии пользователей — берите Redis или Dragonfly. Они проверены годами, имеют активное сообщество и не заставят вас писать обёртки над базовыми командами.

Часто задаваемые вопросы

Можно ли полностью заменить Redis на KiviDB в существующем проекте?

С технической точки зрения — да, если вы используете стандартный Redis-протокол. Поскольку KiviDB совместима с ним, большинство клиентских библиотек будут работать без правки кода. Однако я рекомендую сначала проверить специфические модули или сложные Lua-скрипты, так как полная идентичность поведения во всех пограничных случаях не гарантирована.

В чём преимущество Rust перед C++ в контексте работы с памятью в KiviDB?

Основной выигрыш здесь в предсказуемости. В отличие от Dragonfly, где собственный аллокатор может приводить к фрагментации и заметным паузам при уплотнении памяти, Rust обеспечивает безопасное управление ресурсами без классического GC. Это позволяет избежать тех самых скачков задержек в 200–400 мс, которые критичны для систем реального времени.

Насколько эффективно KiviDB работает ### Насколько эффективно KiviDB работает с векторными данными по сравнению с Redis + RedisVector?

KiviDB хранит векторные данные в собственном формате с поддержкой приближённого поиска ближайших соседей, что избавляет от необходимости держать отдельный Redis-инстанс для векторного кэша. На практике это сокращает количество сетевых хопов и упрощает архитектуру: один сервис вместо двух. Однако если вы уже используете Redis с RedisVector и не сталкиваетесь с проблемами пауз или фрагментации памяти, миграция может быть избыточной — KiviDB выигрывает там, где важна предсказуемость задержек при интенсивной загрузке.

Поделиться:TelegramX / TwitterVK

Читайте также