Не так давно на Хабре была опубликована статья о NoSQL базе — «Tarantool 1.6 от первого лица». Уверен, в своих кругах эта база данных отлично известна и уже завоёвывает популярность. Уверен так же и в том, что есть те начинающие, руки не дошли, кто хотел бы попробовать Tarantool в действии. Именно для таких желающих я приведу несколько простых примеров, помогающих начать знакомиться с этим интересным продуктом. Как понятно из названия статьи — речь идет о версии Tarantool 1.6.

Install


На сайте проекта подробно описаны варианты установки для разных систем. Для Ubuntu выглядит так:

wget http://tarantool.org/dist/public.key
sudo apt-key add ./public.key
release=`lsb_release -c -s`

cat > /etc/apt/sources.list.d/tarantool.list <<- EOF
deb http://tarantool.org/dist/master/ubuntu/ $release main
deb-src http://tarantool.org/dist/master/ubuntu/ $release main
EOF

sudo apt-get update
sudo apt-get install tarantool

Hello world!


Tarantool использует Lua в качестве встроенного языка (Lua 5.1 на базе on LuaJIT 2.0). Язык lua не сложный, быстро познакомиться с его основами можно здесь или на Хабре в публикации «Lua за 15 минут». Вы можете использовать Tarantool как Lua интерпретатор.

$ /usr/bin/tarantool
/usr/bin/tarantool: version 1.6.4-509-ga4af00e
type 'help' for interactive help
tarantool> 2 + 3
---
- 5
...
tarantool> print ('Ola lua!')
Ola lua!
---
...

Так же вы можете писать свою логику, используя скрипты на Lua. Напишем стартовый init.lua для запуска Tarantool и вывода 'Hello world'.

Одна из основных библиотек tarantool — box. С помощью box.cfg (в lua {...} — это таблица) создадим конфигурацию запуска:

box.cfg {
    listen = 3311,
    logger = 'tarantool.log',
} 

Tarantool будет запущен на порт 3311 и будет хранить логи в tarantool.log:

Создадим функцию hello()
local function hello()
    print ('Hello world!)
end

hello()

Запускаем и видим:

$ tarantool init.lua 
Hello world!

Data Base


В tarantool кортежи хранятся в пространствах (space) — некий аналог таблицы.

Создадим тестовый space и первичный (primary) индекс в нем (можно также строить и вторичный по всем полям записи), используя дерево для хранения индексов:

s = box.schema.space.create('test')
p = s:create_index('primary', {type = 'tree', parts = {1, 'NUM'}})

Заполним test записями вида {key, number}:

for i = 1, 10 do
    s:insert({i, i})
end 

В данном случае объект s ссылка на объект box.space.test. Соответственно, можно было записать:

box.space.test:insert({i, i})

Кстати, вы можете столкнуться с такой ошибкой при повторном запуске tarantool:

$ tarantool init.lua 
Hello world!
main/101/init.lua F> Space 'test' already exists

Tarantool хранит все свои данные в оперативной памяти. Для их сохранности Tarantool делает снимки состояний (snapshot) и бинарные логи (xlog). Ошибка возникает из-за того, что при старте Tarantool загружает данные с последнего использования с помощью файлов .snap (снимки состояния) и .xlog (по умолчанию в рабочей директории). Соответственно, ваш test space уже присутствует в базе. Вы можете удалять .snap и .xlog при каждом запуске, или же добавить проверку существования пространства.

s =  box.space.test
if not s then
    s = box.schema.space.create('test')
    p = s:create_index('primary', {type = 'tree', parts = {1, 'NUM'}})
    for i = 1, 10 do
        s:insert({i, i})
    end
end

Было бы очень неплохо проверить, сохранились ли наши данные, наша конфигурация в запущенном Tarantool. Для этого предусмотрена консоль для администратора. Вообще, в lua реализация подключения различных модулей, встроенных пакетов, других файлов с кодом происходит через механизм require.

local console = require('console')
console.listen(127.0.0.1:3312)

После запуска Tarantool вы сможете подключится к админской консоли на порту 3312 с использование утилит nc/telnet и rlwrap (для удобства работы). Подключившись, вы можете напрямую работать с Tarantool. Посмотрим, к примеру, данные нашего пространства test:

$ rlwrap nc 0 3312
Tarantool 1.6.4-509-ga4af00e (Lua console)                     
type 'help' for interactive help                               
box.space.test:select()
---
- - [1, 1]
  - [2, 2]
  - [3, 3]
  - [4, 4]
  - [5, 5]
  - [6, 6]
  - [7, 7]
  - [8, 8]
  - [9, 9]
  - [10, 10]
...
box.space.test:get(6)
---
- [6, 6]
...

Посмотрим параметры конфигурации нашего Tarantool:

box.cfg
box.cfg
---
- snapshot_count: 6
  too_long_threshold: 0.5
  slab_alloc_factor: 2
  slab_alloc_maximal: 1048576
  background: false
  logger: tarantool.log
  slab_alloc_arena: 1
  sophia:
    page_size: 131072
    threads: 5
    node_size: 134217728
    memory_limit: 0
  listen: '3311'
  logger_nonblock: true
  snap_dir: .
  coredump: false
  sophia_dir: .
  wal_mode: write
  slab_alloc_minimal: 64
  panic_on_snap_error: true
  panic_on_wal_error: true
  rows_per_wal: 500000
  wal_dir: .
  log_level: 5
  readahead: 16320
  snapshot_period: 0
  wal_dir_rescan_delay: 0.1
...


Итак, создав space выполним следующую задачу: в записях с ключом > 5 увеличим значение хранимого чиcла на 10.
Для нужной выборки воспользуемся библиотекой box.index.

for k,v in box.space.test.index.primary:pairs(5, {iterator = box.index.GT}) do
   s:update(v[1], {{'=', 2, v[2] + 10}})
end

box.space.index.primary — означает работу с пространством test и его primary индексом.
pairs() — функция итерирования, возвращающая итератор (переменная k в цикле) и таблицу значений (состоит из набора записей v), принимает на вход значение ключа(5) — старта итерирования и тип итератора.
iterator = box.index.GT — итератор GT (greater) возвращает записи с ключами, больше заданного.
update() — обновление записи в базе, принимает значение ключа записи (v[1]),
{' = ', 2, v[2] + 10} — обозначает обновление ('=') второго значения на v[2] + 10.

Посмотрим, используя админскую консоль, значения нашего space:

box.space.test:select()
---
- - [1, 1]
  - [2, 2]
  - [3, 3]
  - [4, 4]
  - [5, 5]
  - [6, 16]
  - [7, 17]
  - [8, 18]
  - [9, 19]
  - [10, 20]
...

Общий код нашего init.lua скрипта:
-- Пример init cкрипта для tarantool

-- Подключение всех необходимых в скрипте модулей
-- удобно описывать в начале
local console = require('console')

-- Стартуем админскую консоль
console.listen('127.0.0.1:3312')

-- Конфигурация tarantool
box.cfg {
    listen = 3311,
    logger = 'tarantool.log',
}

local function hello()
    print ('Hello world!')
end

-- Запуск функции hello
hello()

-- Создаем и заполняем тестовый space, если он отсутствует
s =  box.space.test
if not s then
    s = box.schema.space.create('test')
    -- Первичный индекс
    p = s:create_index('primary', {type = 'tree', parts = {1, 'NUM'}})
    for i = 1, 10 do
        s:insert({i, i})
    end
end

-- Увеличим хранящиеся значения во всех записях, где первичный ключ больше 5
for k,v in box.space.test.index.primary:pairs(5, {iterator = box.index.GT}) do
   s:update(v[1], {{'=', 2, v[2] + 10}})
end



Конечно, чтобы полноценно освоить Tarantool, знать его тонкости, нужно читать документацию на сайте проекта. Ведь помимо всего Tarantool — это не только база данных, но еще и lua application server. К примеру, есть http сервер и очереди. Но первый шаг сделан — свой маленький скрипт для Tarantool вы написали.

Еще вы можете почитать про Tarantool (в примерах там более старые версии проекта) в других статьях на Хабре:
«Изучаем Tarantool + Lua»,
«Уникальные возможности Tarantool».

Так же без всякой установки вы можете попробовать Tarantool на try.tarantool.org.

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


  1. evnuh
    31.03.2015 21:36
    -2

    Сервер вроде как уже обвешали функционалом, а чё нащёт бенчмарков? Чиста раз уж у вас и JIT и in-memory + disk да ещё и с http сервером и клиентом — вполне отличная вещь видится мне для сбора всяких статистик, хранения сессий и т.д, сразу из коробки.


    1. dimoclus
      31.03.2015 21:52
      +1

      С некоторыми оговорками:
      — до тех пор, пока хватает производительности одного ядра
      — до тех пор, пока оперативной памяти хватает для хранения данных
      Ну и при сборке статистики нужно иметь в виду, что ввиду однопоточности сервера, обработка данных прямо внутри сервера будет блокировать его работу (придется время от времени звать box.fiber.sleep(0), что может привнекти некоторые нежелательные эффекты — до сих пор не уверен, что будет с итератором, ссылающимся на tuple, который был удалён во время такого вот sleep-а).
      Как по мне, статистику в тарантуле удобней агрегировать (можно еще WAL отключить), а работать с результатом все-таки удобнее в реляционной СУБД, где для join'ов не требуется программировать.


      1. evnuh
        01.04.2015 20:38

        А LUA-шные fibers нельзя по ядрам размазать получается? Тогда зачем вам http сервер был внутри?


        1. kostja
          02.04.2015 00:12
          +1

          Я на знаю к кому Вы обращаетесь во множественном числе, это видимо твёрдая вера в то что всё заказано, проплачено и везде пиар :)))
          e-sha один из наших пользователей, ему http насколько я знаю не нужен, поэтому он Вам не ответит. А другим http внутри — удобно.


    1. e-sha Автор
      01.04.2015 12:38

      Бенчмарки(YCSB) есть внизу на главной странице сайта проекта.
      Прочитать про применение tarantool можно еще в статьях:
      Определение веса значимости пользователей по отношению друг к другу на основании их действий (Tarantool+Lua)
      Из говнокода в Highload. Используем ТАРАНtool. 5 рецептов повышения производительности
      Как раз про хранение истории посещений рассказывали на митапе в badoo:
      www.youtube.com/watch?v=O-MGE3wHkqs