- PVSM.RU - https://www.pvsm.ru -
На Хабре уже опубликовано множество статей о платформе Tarantool. Например, есть обзорные материалы [1] о создании key-value хранилищ, но они редко углубляются в детали реализации. Также доступны практические примеры, такие как реализация key-value хранилища [2] на Tarantool 2.x с использованием фреймворка Cartridge [3] и Docker Compose. Однако эти примеры не раскрывают внутренней логики работы приложения.
Цель этой статьи — продемонстрировать процесс создания простого key-value хранилища на основе актуальной версии Tarantool 3.x, а также показать, как его собрать и развернуть.
Исходный код проекта доступен в репозитории [4].
Yet Another Key-Value Storage на основе Tarantool 3.x [5]
Оглавление [6]
2. Реализация key-value хранилища [13]
2.5.2. Роль storage [23]
2.5.3. Роль router [24]
Заключение [27]
Запись и обновление пар «ключ-значение».
Установка срока жизни (TTL) для записей.
Поиск значения по точному совпадению ключа.
Поиск записей по префиксу ключа.
Реализация на Tarantool 3.x.
Поддержка шардинга.
Предоставление метрик в формате Prometheus text-based exposition [28].
Поставка в виде Docker-образа и развертывание с помощью Docker Compose.
Tarantool — это платформа для вычислений, объединяющая встроенную базу данных и сервер приложений на языке Lua. Она включает модули [29], такие как:
box [30] — работа с данными.
fiber [31] — управление легковесными потоками для асинхронных задач.
http [32] — HTTP-клиент и сервер.
С помощью пакетного менеджера LuaRocks [33] (форк от команды Tarantool: github.com/tarantool/luarocks [34]) можно подключать сторонние модули, например:
vshard [35] — организация шардинга.
crud [36] — упрощение CRUD-операций в кластере vshard.
expirationd [37] — автоматическое удаление устаревших данных.
Модули для работы с MySQL (github.com/tarantool/mysql [38]) и PostgreSQL (github.com/tarantool/pg [39]).
metrics-export-role [40] — сбор метрик для Tarantool 3.x.
Модуль box [30] отвечает за работу с базой данных. Данные хранятся в spaces [41] — аналогах таблиц в SQL. Spaces содержат tuples [42] — записи базы данных.
Атрибуты space:
Уникальное имя, задаваемое пользователем.
Уникальный числовой идентификатор (автоматический или пользовательский).
Движок (engine): memtx (in-memory) или vinyl (on-disk для больших объемов данных).
Для space можно задавать первичный и вторичные индексы [43]. Схема данных в Tarantool 2.x и 3.x определяется программно через Lua-скрипты. Для миграций схемы в кластере Cartridge (Tarantool 2.x) используется модуль migrations [44].
Пример создания space и первичного индекса:
box.schema.create_space('key_value', {
format = {
{ name = 'key', type = 'string' },
{ name = 'value', type = 'string' }
},
if_not_exists = true
})
box.space.key_value:create_index('id', {
type = 'tree',
parts = { 'key' },
unique = true,
if_not_exists = true
})
Для шардинга используется модуль vshard [35], поддерживающий Tarantool 2.x и 3.x. Он применяется и в БД picodata [45]. Tuples делятся на виртуальные сегменты (buckets) [46], которые распределяются между шардами или наборами реплик (replicasets).
Для шардинга нужен индекс (shard_index), по умолчанию — bucket_id. Его имя можно изменить в настройках vshard [47].
Пример создания space с индексами для шардинга:
box.schema.create_space('key_value', {
format = {
{ name = 'key', type = 'string' },
{ name = 'bucket_id', type = 'unsigned' },
{ name = 'value', type = 'string' }
},
if_not_exists = true
})
box.space.key_value:create_index('id', {
type = 'tree',
parts = { 'key' },
unique = true,
if_not_exists = true
})
box.space.key_value:create_index('bucket_id', {
type = 'tree',
parts = { 'bucket_id' },
unique = false,
if_not_exists = true
})
Роли в кластере vshard:
storage [48]: хранение buckets. В replicaset один экземпляр — мастер (чтение и запись), остальные — реплики (только чтение).
router [49]: маршрутизация запросов. Есть экспериментальный Go VShard Router [50].
rebalancer [51]: равномерное распределение buckets (может назначаться автоматически).
Для создания приложений и управления экземплярами в Tarantool 2.x применялась утилита Cartridge CLI [52]. В Tarantool 3.x используется новая CLI — tt [53].
Примеры конфигураций на основе tt:
Одиночная БД: create_db [54].
Кластер с vshard: sharded_cluster [55].
С vshard и crud: sharded_cluster_crud [56].
С vshard, crud, и метриками: sharded_cluster_crud_metrics [57].
Установим Tarantool и tt. Официальные пакеты доступны для nix-систем, а для Windows — через WSL. Инструкции для Ubuntu [58]:
curl -L https://tarantool.io/repository/3/installer.sh | bash
sudo apt-get install -y tt tarantool
Создадим каркаc проекта на основе шаблона vshard_cluster [59]:
tt create cluster-app
--name tt_kv
-d ${PWD}
-f
-s
--var bucket_count=100
--var replicasets_count=1
--var replicas_count=2
--var roles_count=1
Команда создаст директорию tt_kv с файлами:
config.yaml: конфигурация кластера.
instances.yml: описание экземпляров.
router.lua: скрипт для router.
storage.lua: скрипт для storage.
tt_kv-scm-.rockspec*: конфигурация зависимостей.
Обновим файл tt_kv-scm-1.rockspec, добавив актуальные модули:
package = 'tt_kv'
version = 'scm-1'
source = {
url = '/dev/null',
}
dependencies = {
'crud == 1.5.2-1',
'expirationd == 1.6.1-1',
'metrics-export-role == 1.0.0-1',
'vshard == 0.1.34-1'
}
build = {
type = 'none'
}
Добавлены:
crud: упрощение работы с данными.
expirationd: удаление записей по TTL.
metrics-export-role: метрики для Prometheus.
Экземпляры storage хранят данные и реализуют логику работы с хранилищем. Они вызываются через router с использованием учетной записи storage, которой нужны права на выполнение функций crud.
Создадим space key_value с полями:
key (string): ключ.
bucket_id (unsigned): идентификатор для шардинга.
value (string): значение.
expire_at (unsigned): время истечения TTL.
Индексы:
id: первичный, по key (уникальный, tree).
bucket_id: для шардинга (неуникальный, tree).
expire_at_idx: для TTL (неуникальный, tree).
box.schema.create_space('key_value', {
format = {
{ name = 'key', type = 'string' },
{ name = 'bucket_id', type = 'unsigned' },
{ name = 'value', type = 'string' },
{ name = 'expire_at', type = 'unsigned' }
},
if_not_exists = true
})
box.space.key_value:create_index('id', {
type = 'tree',
parts = { 'key' },
unique = true,
if_not_exists = true
})
box.space.key_value:create_index('bucket_id', {
type = 'tree',
parts = { 'bucket_id' },
unique = false,
if_not_exists = true
})
box.space.key_value:create_index('expire_at_idx', {
type = 'tree',
parts = { 'expire_at' },
unique = false,
if_not_exists = true
})
Для автоматического удаления используем expirationd. Функция проверяет, истек ли срок записи (expire_at > 0 и текущее время > expire_at):
local function is_expired(args, tuple)
return (tuple[4] > 0) and (require('fiber').time() > tuple[4])
end
Функция get_by_prefix_locally выполняет поиск на каждом replicaset:
local function get_by_prefix_locally(prefix)
local result = {}
local index = box.space.key_value.index.id
local iter = index:iterator('GE', { prefix })
for tuple in iter do
local key = tuple[1]
if string.sub(key, 1, #prefix) == prefix then
table.insert(result, {
key = key,
value = tuple[3],
expire_at = tuple[4]
})
else
break
end
end
return result
end
Router маршрутизирует запросы к шардам. Для поиска по префиксу используем функцию get_by_prefix_locally через crud:
local function get_by_prefix(prefix)
local result, err = crud.map_call('key_value.get_by_prefix_locally', {prefix})
if not result then
return nil, "Error during map_call: " .. tostring(err)
end
return result.data
end
Настройка выполняется в config.yaml.
Создаем роль crud-role и учетную запись app:
config:
context:
app_user_password:
from: env
env: APP_USER_PASSWORD
client_user_password:
from: env
env: CLIENT_USER_PASSWORD
replicator_user_password:
from: env
env: REPLICATOR_USER_PASSWORD
storage_user_password:
from: env
env: STORAGE_USER_PASSWORD
credentials:
roles:
crud-role:
privileges:
- permissions: [ "execute" ]
lua_call: [ "crud.delete", "crud.get", "crud.upsert" ]
users:
app:
password: '{{ context.app_user_password }}'
roles: [ public, crud-role ]
client:
password: '{{ context.client_user_password }}'
roles: [ super ]
replicator:
password: '{{ context.replicator_user_password }}'
roles: [ replication ]
storage:
password: '{{ context.storage_user_password }}'
roles: [ sharding ]
Добавляем роли crud-storage, expirationd, metrics-export:
groups:
storages:
roles:
- roles.crud-storage
- roles.expirationd
- roles.metrics-export
roles_cfg:
roles.expirationd:
cfg:
metrics: true
key_value_task:
space: key_value
is_expired: key_value.is_expired
options:
atomic_iteration: true
force: true
index: 'expire_at_idx'
iterator_type: GT
start_key:
- 0
tuples_per_iteration: 10000
replication:
failover: election
database:
use_mvcc_engine: true
replicasets:
storage-001:
instances:
storage-001-a:
roles_cfg:
roles.metrics-export:
http:
- listen: '0.0.0.0:8081'
endpoints:
- path: /metrics/prometheus/
format: prometheus
iproto:
listen:
- uri: 127.0.0.1:3301
advertise:
client: 127.0.0.1:3301
storage-001-b:
roles_cfg:
roles.metrics-export:
http:
- listen: '0.0.0.0:8082'
endpoints:
- path: /metrics/prometheus/
format: prometheus
iproto:
listen:
- uri: 127.0.0.1:3302
advertise:
client: 127.0.0.1:3302
Добавляем роли crud-router и metrics-export:
groups:
routers:
roles:
- roles.crud-router
- roles.metrics-export
roles_cfg:
roles.crud-router:
stats: true
stats_driver: metrics
stats_quantiles: true
app:
module: router
sharding:
roles: [ router ]
replicasets:
router-001:
instances:
router-001-a:
roles_cfg:
roles.metrics-export:
http:
- listen: '0.0.0.0:8083'
endpoints:
- path: /metrics/prometheus/
format: prometheus
iproto:
listen:
- uri: 127.0.0.1:3303
advertise:
client: 127.0.0.1:3303
Создаем Docker-образ на основе tarantool/tarantool:
FROM tarantool/tarantool:3.2.0
# Install dependencies
RUN apt-get update &&
apt-get install -y git unzip cmake tt
# Initialize tt structure
RUN tt init &&
mkdir tt_kv &&
ln -sfn ${PWD}/tt_kv/ ${PWD}/instances.enabled/tt_kv
# Copy cluster configs
COPY tt_kv /opt/tarantool/tt_kv
# Build app
RUN tt build tt_kv
Разворачиваем кластер с помощью Docker Compose:
services:
tarantool:
build:
context: .
entrypoint: "tt start tt_kv -i"
environment:
APP_USER_PASSWORD: "app"
CLIENT_USER_PASSWORD: "client"
REPLICATOR_USER_PASSWORD: "replicator"
STORAGE_USER_PASSWORD: "storage"
После развертывания кластера вы можете проверить его состояние и выполнить операции с данными, используя утилиту tt и команды в контейнере Docker.
Разворачивание кластера:
Очистите старые контейнеры и запустите новый кластер с пересборкой образа:
docker compose rm -f
docker compose up --build -d
Проверка состояния кластера vshard:
Убедитесь, что маршрутизатор и шарды работают корректно:
docker exec tt_kv-tarantool-1 /bin/sh -c "echo "vshard.router.info()" | tt connect -x yaml "tt_kv:router-001-a""
Эта команда выводит информацию о состоянии маршрутизатора и распределении бакетов.
Вставка данных без TTL:
Добавьте пару test0 = test1 в пространство key_value, которая не будет удаляться по истечению времени:
docker exec tt_kv-tarantool-1 /bin/sh -c "echo "crud.insert_object('key_value', {key = 'test0', value = 'test1', expire_at = 0})" | tt connect -x yaml "tt_kv:router-001-a""
Вставка данных с TTL:
Добавьте пару test2 = test3 в пространство key_value, которая будет удалена через 5 секунд после вставки:
docker exec tt_kv-tarantool-1 /bin/sh -c "echo "crud.insert_object('key_value', {key = 'test2', value = 'test3', expire_at = require('os').time() + 5})" | tt connect -x yaml "tt_kv:router-001-a""
Мы создали простое key-value хранилище на Tarantool 3.x с поддержкой шардинга, TTL и метрик Prometheus. Приложение упаковано в Docker-образ и развернуто через Docker Compose. Добавленные команды позволяют легко развернуть кластер и протестировать его функциональность. Этот пример можно расширить, добавив HTTP-API или дополнительные функции, такие как сжатие данных или интеграция с внешними системами.
Автор: m_karimov
Источник [60]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/key-value-storage/423233
Ссылки в тексте:
[1] обзорные материалы: https://habr.com/ru/companies/vk/articles/829994/
[2] реализация key-value хранилища: https://github.com/tarantool/tarantool-operator/tree/cartridge-0.0.11/examples/kv
[3] Cartridge: https://github.com/tarantool/cartridge
[4] репозитории: https://github.com/MaratKarimov/tt_kv
[5] Yet Another Key-Value Storage на основе Tarantool 3.x: #yet-another-key-value-storage-%D0%BD%D0%B0-%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%B5-tarantool-3x
[6] Оглавление: #%D0%BE%D0%B3%D0%BB%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5
[7] Функциональные требования: #%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D1%82%D1%80%D0%B5%D0%B1%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F
[8] Нефункциональные требования: #%D0%BD%D0%B5%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D1%82%D1%80%D0%B5%D0%B1%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F
[9] 1. Общая информация о Tarantool: #1-%D0%BE%D0%B1%D1%89%D0%B0%D1%8F-%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%86%D0%B8%D1%8F-%D0%BE-tarantool
[10] 1.1. Организация хранения данных: #11-%D0%BE%D1%80%D0%B3%D0%B0%D0%BD%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F-%D1%85%D1%80%D0%B0%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85
[11] 1.2. Шардирование данных. Кластеры vshard: #12-%D1%88%D0%B0%D1%80%D0%B4%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85-%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D1%8B-vshard
[12] 1.3. Средства разработки: #13-%D1%81%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B2%D0%B0-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B8
[13] 2. Реализация key-value хранилища: #2-%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F-key-value-%D1%85%D1%80%D0%B0%D0%BD%D0%B8%D0%BB%D0%B8%D1%89%D0%B0
[14] 2.1. Настройка окружения и создание каркаса проекта: #21-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-%D0%BE%D0%BA%D1%80%D1%83%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B8-%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BA%D0%B0%D1%80%D0%BA%D0%B0%D1%81%D0%B0-%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B0
[15] 2.2. Обновление зависимостей: #22-%D0%BE%D0%B1%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B7%D0%B0%D0%B2%D0%B8%D1%81%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B5%D0%B9
[16] 2.3. Настройка экземпляров storage: #23-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-%D1%8D%D0%BA%D0%B7%D0%B5%D0%BC%D0%BF%D0%BB%D1%8F%D1%80%D0%BE%D0%B2-storage
[17] 2.3.1. Определение схемы данных: #231-%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D1%81%D1%85%D0%B5%D0%BC%D1%8B-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85
[18] 2.3.2. Удаление записей с истекшим TTL: #232-%D1%83%D0%B4%D0%B0%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B5%D0%B9-%D1%81-%D0%B8%D1%81%D1%82%D0%B5%D0%BA%D1%88%D0%B8%D0%BC-ttl
[19] 2.3.3. Поиск по префиксу ключа: #233-%D0%BF%D0%BE%D0%B8%D1%81%D0%BA-%D0%BF%D0%BE-%D0%BF%D1%80%D0%B5%D1%84%D0%B8%D0%BA%D1%81%D1%83-%D0%BA%D0%BB%D1%8E%D1%87%D0%B0
[20] 2.4. Настройка router: #24-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-router
[21] 2.5. Настройка кластера: #25-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80%D0%B0
[22] 2.5.1. Учетные записи: #251-%D1%83%D1%87%D0%B5%D1%82%D0%BD%D1%8B%D0%B5-%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B8
[23] 2.5.2. Роль storage: #252-%D1%80%D0%BE%D0%BB%D1%8C-storage
[24] 2.5.3. Роль router: #253-%D1%80%D0%BE%D0%BB%D1%8C-router
[25] 3. Развертывание хранилища: #3-%D1%80%D0%B0%D0%B7%D0%B2%D0%B5%D1%80%D1%82%D1%8B%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D1%85%D1%80%D0%B0%D0%BD%D0%B8%D0%BB%D0%B8%D1%89%D0%B0
[26] 3.1. Проверка и работа с хранилищем: #31-%D0%BF%D1%80%D0%BE%D0%B2%D0%B5%D1%80%D0%BA%D0%B0-%D0%B8-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0-%D1%81-%D1%85%D1%80%D0%B0%D0%BD%D0%B8%D0%BB%D0%B8%D1%89%D0%B5%D0%BC
[27] Заключение: #%D0%B7%D0%B0%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5
[28] Prometheus text-based exposition: https://github.com/prometheus/docs/blob/main/docs/instrumenting/exposition_formats.md
[29] модули: https://www.tarantool.io/ru/doc/latest/reference/reference_lua/
[30] box: https://www.tarantool.io/ru/doc/latest/reference/reference_lua/box/
[31] fiber: https://www.tarantool.io/ru/doc/latest/reference/reference_lua/fiber/
[32] http: https://github.com/tarantool/http
[33] LuaRocks: https://luarocks.org/
[34] github.com/tarantool/luarocks: https://github.com/tarantool/luarocks
[35] vshard: https://github.com/tarantool/vshard
[36] crud: https://github.com/tarantool/crud
[37] expirationd: https://github.com/tarantool/expirationd
[38] github.com/tarantool/mysql: https://github.com/tarantool/mysql
[39] github.com/tarantool/pg: https://github.com/tarantool/pg
[40] metrics-export-role: https://github.com/tarantool/metrics-export-role
[41] spaces: https://www.tarantool.io/ru/doc/latest/platform/ddl_dml/value_store/#index-box-space
[42] tuples: https://www.tarantool.io/ru/doc/latest/platform/ddl_dml/value_store/#term-tuple
[43] индексы: https://www.tarantool.io/ru/doc/latest/platform/ddl_dml/indexes/#index-box-index
[44] migrations: https://github.com/tarantool/migrations
[45] picodata: https://github.com/picodata/vshard
[46] виртуальные сегменты (buckets): https://www.tarantool.io/ru/doc/latest/platform/sharding/vshard_architecture/#vshard-vbuckets
[47] настройках vshard: https://www.tarantool.io/en/doc/latest/reference/reference_rock/vshard/vshard_ref/#cfg-basic-shard-index
[48] storage: https://www.tarantool.io/ru/doc/latest/platform/sharding/vshard_architecture/#vshard-architecture-storage
[49] router: https://www.tarantool.io/ru/doc/latest/platform/sharding/vshard_architecture/#vshard
[50] Go VShard Router: https://github.com/tarantool/go-vshard-router/blob/master/README_ru.md
[51] rebalancer: https://www.tarantool.io/ru/doc/latest/platform/sharding/vshard_architecture/#vshard-rebalancer
[52] Cartridge CLI: https://github.com/tarantool/cartridge-cli
[53] tt: https://github.com/tarantool/tt
[54] create_db: https://github.com/tarantool/doc/tree/main/doc/code_snippets/snippets/config/instances.enabled/create_db
[55] sharded_cluster: https://github.com/tarantool/doc/tree/main/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster
[56] sharded_cluster_crud: https://github.com/tarantool/doc/tree/main/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud
[57] sharded_cluster_crud_metrics: https://github.com/tarantool/doc/tree/main/doc/code_snippets/snippets/sharding/instances.enabled/sharded_cluster_crud_metrics
[58] Ubuntu: https://www.tarantool.io/ru/download/os-installation/ubuntu/
[59] vshard_cluster: https://github.com/tarantool/tt/blob/master/cli/create/builtin_templates/templates/vshard_cluster
[60] Источник: https://habr.com/ru/articles/920614/?utm_source=habrahabr&utm_medium=rss&utm_campaign=920614
Нажмите здесь для печати.