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

Кластерный конфиг

Одно из главных нововведений в последней мажорной версии Tarantool — единая точка конфигурации.

Настраивать Tarantool через box.cfg слишком сложно? Неудобно хранить настройки в Lua-файлах или передавать их через env? В Tarantool 3.0 появилась возможность создать общий кластерный конфиг для всех узлов Tarantool.

Чтобы запустить Tarantool, вам понадобится конфиг следующего вида:

# объявляем пользователей в кластере
credentials:
  users:
    guest:
      roles: [super]

# объявляем настройки для подключения к узлу
iproto:
  listen:
  - uri: 'unix/:./{{ instance_name }}.iproto'

# описываем топологию кластера и настройки всех узлов
groups:
  group-001:
    replicasets:
      replicaset-001:
        instances:
          instance-001: {}

Потом понадобится выполнить следующую команду:

tarantool --config config.yaml --name instance-001

Что можно указать в конфигурационном файле:

  • все параметры для настройки Tarantool (то, что раньше указывалось в box.cfg);

  • топологию, в том числе и для шардированного кластера;

  • параметры и роли приложений (похожим образом, как в картридже);

  • параметры фейловера — в скором времени мы добавим их еще больше.

В Tarantool Enterprise конфиг можно хранить в etcd, и обновления будут загружаться в Tarantool автоматически.

Больше примеров лежит в гитхабе.

Персистентность и WAL

Многие все еще думают о Tarantool как о простом кэше, который нельзя использовать для хранения важных данных. Когда-то это было так, но все давно изменилось. Персистентность в Tarantool включена по умолчанию: он всегда сохраняет данные на диске, перед тем как отдать пользователю подтверждение записи.

Но вы всегда можете превратить Tarantool в кэш всего одной настройкой:

box.cfg{wal_mode = 'none'} -- делаем из Tarantool кэш

А для автоматического удаления устаревших данных можно использовать наш модуль expirationd:

local expirationd = require("expirationd")

function is_expired(args, tuple)
  return true
end

function delete_tuple(space, args, tuple)
  box.space[space]:delete{tuple[1]}
end

expirationd.start(job_name, space.id, is_expired, {
    process_expired_tuple = delete_tuple,
    args = nil,
    tuples_per_iteration = 50,
    full_scan_time = 3600
})

Синхра и рафт

Вам нужны дополнительные гарантии при записи? Вас пугает асинхронная репликация? Попробуйте синхру! А если вы хотите автоматическое переключение лидера, не забудьте настроить рафт.

Мы писали о рафте и синхре на Хабре много раз, но, если вы пропустили, рекомендуем ознакомиться подробнее в предыдущих статьях: 

Как пользоваться синхрой и рафтом?

Первый узел:

# объявляем пользователей в кластере
credentials:
  users:
    admin:
      password: 'passwd'
      roles: [super]

# объявляем настройки для общения инстансов между собой
# (их также можно задавать на уровне узлов, репликасетов и групп)
iproto:
  listen:
  - uri: 'unix/:./{{ instance_name }}.iproto'
  advertise:
    peer:
      login: 'admin'

# включаем автоматический фейловер на базе Raft
replication:
  failover: election

# описываем топологию кластера и настройки всех узлов
groups:
  group-001:
    replicasets:
      replicaset-001:
        instances:
          instance-001: {}
          instance-002: {}
          instance-003: {}

Запускаем кластер:

tarantool --config config.yaml --name instance-001
tarantool --config config.yaml --name instance-002
tarantool --config config.yaml --name instance-003

Снова на первом узле:

box.ctl.promote()

box.schema.space.create('employees', {is_sync = true})
box.space.customers:format({
    {name = 'id', type = 'integer'},
    {name = 'name', type = 'string'},
    {name = 'salary', type = 'integer'},
    {name = 'department', type = 'string'},
})
box.space.employees:create_index('pk', {parts = {'id'})
    
-- теперь можно работать с данными:
    
box.space.customers:insert{...}

Хранение на диске с Vinyl

Tarantool, как и все современные базы данных, умеет сохранять данные на диске.

Процесс создания дискового спейса в Tarantool ничем не отличается от спейсов в памяти:

-- создаем спейс с дисковым движком
box.schema.create_space('former_employees', {engine = 'vinyl'})

box.space.former_employees:format({
    {name = 'id', type = 'integer'},
    {name = 'name', type = 'string'},
    {name = 'salary', type = 'integer'},
    {name = 'department', type = 'string'},
    {name = 'end_date', type = 'datetime'},
})

box.space.former_employees:create_index('pk', {parts = {'employee_id'}})


-- переносим устаревшие данные в холодное хранилище

box.begin()
for key, tuple in box.space.employees.index.end_date:pairs(now, {iterator = 'LE'}) do
    box.space.former_employees:insert(tuple)
    box.space.employees:delete(key)
end
box.commit()

При работе с vinyl всегда стоит помнить о том, что Tarantool в первую очередь in-memory-технология. У дискового движка есть серьезные ограничения — например, мы не рекомендуем создавать больше одного индекса для таких данных и делать апдейты в сохраненных данных.

Подробнее о vinyl можно прочитать в статье на Хабре.

Констрейнты и внешние ключи

Хотите, чтобы было как в SQL? Попробуйте констрейнты и внешние ключи.

-- Define a tuple constraint function --
box.schema.func.create('check_dates', {
    language = 'LUA',
    is_deterministic = true,
    body = [[function(t) 
        if t.end_date then 
            return t.start_date < t.end_date
        end
        return
    end]]
})

-- Define a field constraint function --
box.schema.func.create('check_salary', {
    language = 'LUA',
    is_deterministic = true,
    body = 'function(f) return f > 0 and f < 10^9 end'
})

box.schema.space.create('departments')
box.space.deparments:format({
    {name = 'id', type = 'string'},
    {name = 'name', type = 'string'},
    {name = 'manager', type = 'string'},
})
box.space.deparments:create_index('pk', {parts = {'id'}})


box.schema.space.create('employees', {constraint = 'check_dates'})
box.space.customers:format({
    {name = 'id', type = 'integer'},
    {name = 'name', type = 'string'},
    {name = 'salary', type = 'integer', constraint = 'check_salary'},
    {name = 'department', type = 'string', foreign_key = {space = 'departments', field = 'id'}},
    {name = 'start_date', type = 'datetime'},
    {name = 'end_date', type = 'datetime', is_nullable = true},
})
box.space.employees:create_index('pk', {parts = {'id'})

Помните, что для некоторых изменений формата вам потребуется произвести дополнительные миграции.

Транзакции, стримы и отменяемые запросы

Вы привыкли, что в транзакциях в Tarantool нельзя идлить? Привыкли к тому, что все транзакции по умолчанию serializable? Хотите более гибкого контроля над транзакциями? Используйте MVCC-движок:

local fiber = require('fiber')
local log = require('log')
box.cfg{memtx_use_mvcc_engine = true}
box.schema.create_space('test')
box.space.test:format{{name='first', type='string'}, {name='second', type='integer'}}
box.space.test:create_index('pk', {parts = {'first'}})

box.space.test:put{'value', 1}
fiber.new(function() v1 = box.space.test:select() end) -- этот код исполнится после йилда
box.atomic(function()
    box.space.test:put{'value', 2}
    fiber.yield()
    v2 = box.space.test:select()
    box.space.test:put{'value', 3}
end)
v3 = box.space.test:select()
log.info(v1, v2, v3)


---
# это значение в момент йилда в транзакции, 
# транзакция не завершена, поэтому мы получаем старое значение
- - ['value', 1]
# это значение, прочитанное в транзакции
# в рамках самой транзакции эти данные уже существуют, поэтому здесь 2
- - ['value', 2]
# это значение, полученное после завершения транзакции
- - ['value', 3]
...

Вы также можете использовать интерактивные транзакции или выбирать время, через которое долгие запросы будут отменены:

fiber.set_max_slice{warn = 0.5, err = 1.0}

SQL

Вы страдаете от того, что нужно писать сложные запросы на Lua? Боитесь, что ваши аналитики не справятся с Tarantool? Напишите запросы на SQL! (Или прочитайте статью про сложные запросы в Tarantool).

Вот так можно создать таблицу и написать к ней запросы на SQL.

1. Для начала вам потребуется поменять язык в тарантульной консоли (если вы работаете через подключение к узлу) с помощью \set language sql или позвать команду box.execute и передать туда свой SQL-запрос.

Создаем таблицы:

CREATE TABLE employees (
​​​    id INTEGER,
​​​    name STRING, 
​​​    salary INTEGER, 
​​​    department STRING, 
​​​    PRIMARY KEY (id)
​​​);
 CREATE TABLE departments (
​​​    id STRING,
​​​    manager STRING, 
​​​    PRIMARY KEY (id)
​​​);

2. Создаем индексы:

​​​CREATE INDEX salary ON employees (salary);

3. Вставляем данные:

​​​INSERT INTO departments VALUES ('Sales', 'Mighty Manager');
​​​...
​​​INSERT INTO employees VALUES (1, 'John', 100000, 'Sales');
​​​...

4. Пишем запросы:

​​​SET SESSION "sql_seq_scan" = true;
​​​
​​​SELECT department, AVG(salary)
​​​FROM employees
​​​GROUP BY department;
  ---
   - metadata:
     - name: department
       type: string
     - name: COLUMN_1
       type: integer
     rows:
     - ['Sales', 100000]

5. Пишем джойны:

​​​SELECT employees.name, employees.salary, employees.department, department.manager 
​​​FROM employees JOIN departments
​​​ON (employees.department = departments.id);
​​​- metadata:
​​​  - name: name
​​​    type: string
​​​  - name: salary
​​​    type: integer
​​​  - name: department
​​​    type: string
​​​  - name: manager
​​​    type: string
​​​  rows:
​​​  - ['John', 100000, 'Sales', 'Mighty Manager']

Больше примеров —  в гайде по Tarantool.

И помните, что с любыми тарантульными спейсами можно работать как из Lua, так и из SQL, меняя подход в зависимости от задачи.

Шардирование

Шардирование — стандартный подход к масштабированию баз данных. В отличие от многих реляционных БД, в Tarantool шардирование давно стало стандартным модулем и поддерживается в конфиге. Все, что нужно сделать, — установить модуль vshard (tt rocks install vshard) и написать кластерный конфиг для Tarantool:

credentials:
  users:
    admin:
      password: 'passwd'
      roles: [super]

iproto:
  listen:
  - uri: 'unix/:./{{ instance_name }}.iproto'
  advertise:
    peer:
      login: 'admin'
    sharding:
      login: 'admin'

sharding:
  bucket_count: 10000
  
replication:
  failover: election

groups:
  storages:
    sharding:
      roles: [storage]
    replicasets:
      storages-001:
        instances:
          storage-001:
            iproto:
              listen:
              - uri: 'localhost:3301'
          storage-002:  
            iproto:
              listen:
              - uri: 'localhost:3302'
      storages-002:
        instances:
          storage-003:   
            iproto:
              listen:
              - uri: 'localhost:3303'
          storage-004:
            iproto:
              listen:
              - uri: 'localhost:3304'
  routers:
    replicasets:
      routers-001:
        sharding:
          roles: [router]
        instances:
          router-001:   
            iproto:
              listen:
              - uri: 'localhost:3300'


tt connect admin:passwd@localhost:3300

Чтобы начать работать с шардом, надо забутстрапить кластер:

vshard.router.bootstrap()

Теперь можно вставлять данные. На роутере:

vshard.router.callrw('box.space.test_space:insert', {...})

Хранение данных в различных парадигмах

Современный Tarantool позволяет вам применять различные парадигмы для работы с данными. Вот некоторые из примеров различных подходов, которые вы можете комбинировать в Tarantool:

  • key-value, document. Настройки формата в Tarantool позволяют вам не применять никакой формат вообще, что дает вам построить документное или kv-хранилище.

  • cache/master-storage. Tarantool можно использовать в качестве кэша (см. выше) или надежно хранить там важные данные.

  • in-memory/disk. Держите ваши спейсы в памяти, сгрузите все на диск или сделайте и то, и другое.

  • graph. Вы можете написать графовое хранилище самостоятельно, воспользовавшись нашими R-TREE-индексами, или взять готовый Tarantool Graph DB (Enterprise only).

  • column. Что? Хранение данных в колонках в памяти? В Tarantool? Да, с недавнего времени в Tarantool ЕЕ появился новый движок для хранения колонок.

  • queue. На Tarantool построено огромное количество очередей. Есть как бесплатные Tarantool queue и Sharded-queue, а есть Tarantool Queue Enterprise. Подробнее об очередях можно почитать в статье на Хабре.

  • cluster, multi-cluster. Tarantool отлично работает в кластерном режиме. Вы можете строить свои шардированные хранилища на базе опенсорсных vshard и Cartridge, а можете взять энтерпрайзные TDG или TDB. Также на Tarantool можно создать два кластера, которые полностью дублируют свои данные, с помощью Tarantool Clusters Federation.

Графический интерфейс

Управлять кластером всегда проще, когда можно взглянуть на страницу с обновляющимися онлайн статусами. Tarantool есть что вам предложить.

Grafana Dashboard

Тарантульный дашборд с метриками предоставляет все возможные параметры, за которыми можно следить. Все, что вам нужно знать о вашем тарантульном кластере, находится здесь: память, CPU и все возможные параметры рантайма в одном месте. Подробнее в статье на Хабре.

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

Cartridge

Для Tarantool 1.10 / 2.х есть Cartridge — как создать первое приложение c его помощью, можно прочитать по ссылке. Помимо отображения информации о состоянии кластера, Cartridge занимается управлением кластерами и предоставляет свое API для различных настроек в кластере.

Tarantool Cluster Manager

Для новых кластеров на Tarantool 3.х есть Tarantool Cluster Manager (EE-only). В отличие от Cartridge, в TCM меньше управления кластером Tarantool (теперь кластерными вещами занимается сам Tarantool), но при этом больше возможностей для мониторинга и управления отдельными узлами, в том числе в разных кластерах.

Заключение

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

Узнавайте о новых релизах, вебинарах и выходящих статьях в телеграм-канале Tarantool News.

Задать вопросы команде разработчиков про использование Tarantool можно в официальном канале сообщества.  

О принципах и примерах работы продуктов Tarantool читайте в блоге на сайте.

Комментарии (4)


  1. theonevolodya
    13.09.2024 16:37

    А что насчёт ttl записей?


    1. hogstaberg
      13.09.2024 16:37
      +1

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


  1. kanvas
    13.09.2024 16:37

    Спасибо за обзор, было бы интересно какие новые варианты использования в практике (для бизнеса/общества) дают эти возможности?


  1. mr_bag
    13.09.2024 16:37

    Redis, Apache Ignite, MongoDB точно не хватит для высоких нагрузок?