2015-06-28

Об InfluxDB

Оказывается, 11 июня отрелизился InfluxDB версии 0.9.0. Обещали релиз аж в апреле, но получилось только сейчас. И то хорошо.
InfluxDB
InfluxDB — это такая хипстерская БД для хранения временных рядов. Тех самых наших любимых метрик, и прочих данных, тесно привязанных ко времени их возникновения. Сравнивать Инфлюксину следует с Graphite иOpenTSDB.
InfluxDB полностью написана на Go. Даже движок хранения данных (в версии 0.9) — BoltDB на Go. Go очень очень сильно упрощает развертывание. У InfluxDB в deb пакете — лишь один исполняемый файл демона, да конфиг. Для сравнения Graphite — это нехилая кучка кода на Python и даже Django приложение. Ну а OpenTSDB вообще работает поверх Hadoop.
Инфлюксина еще не добралась до версии 1.0. И это заметно. Даже в версии 0.9 явно не все еще доделали. Но пользоваться вполне можно. Помните только, что Influx 0.8 и Influx 0.9 — это две совсем большие разницы.
Ничего не могу сказать про их работу в кластере, тут много чего поменяли, не пробовал. Меня больше интересует смена представляения данных.
InfluxDB 0.8 ближе к Graphite. Здесь каждая метрика представлена иерархическим именем, значением и таймстампом. Впрочем, все же можно группировать независимые наборы метрик в базы данных. А также, кроме собственно числового значения метрики, с каждым отчетом можно хранить произвольный набор дополнительных значений-колонок. Это позволяет использовать InfluxDB не только для хранения метрик, но и, например, для хранения логов, да и любых данных, изменяющихся во времени. Для запросов нескольких метрик, их группировки, предлагается использовать регулярные выражения, применяемые к имени метрики, почти как в Graphite.
В версии 0.9 все изменилось. Появились теги, по аналогии с OpenTSDB. Теперь нет нужды кодировать, например, имя хоста и другие параметры группировки метрик, в названии самой метрики, как это принято в Graphite. Теперь можно с каждой метрикой связать конкретные теги, host=localhost. И именно конкретные значения тегов использовать в выборке и группировке конкретных метрик. Вместо не столь удобных для этого регулярных выражений.
Теги — это прекрасно. Они действительно сильно упрощают понимание структуры БД и ускоряют запросы. Если бы не один неприятный атавизм. Собиралки метрик, например, мой по-прежнему сильно любимый Diamond, понятия не имеют о тегах. Diamond создавался под Graphite. И представление метрики в нем — графитовое: длинное имя-путь и значение. Нет тегов.
В попытке решить проблему, в InfluxDB, при приеме метрик по протоколу Graphite, городят очаровательные костыли. Пользователю предлагается написать набор шаблонов, которые будут сообщать, что, допустим, в метрике с названием servers.localhost.cpu.loadavg.10 'localhost' — это значение тега 'host', а 'loadavg.10' — это, собственно название метрики (средняя загрузка системы за последние 10 минут). Этот костылик не вошел в InfluxDB 0.9.0, его замержили буквально совсем недавно в 0.9.1-rc1.
Конечно, правильнее научить Diamond тегам. Но что-то на Гитхабе не видно шевеления на эту тему. Это ж надо переписать весь Diamond, ибо теги надо добавить в самую середину, в класс Metric. И не только сам Diamond перетрясти нужно, но и кучу сторонних коллекторов, совместимых с ним. Кто знает, есть ли альтернатива Diamond, но с тегами?
InfluxDB по умолчанию хранит данные в том виде, как они пришли, вечно. Тут он ближе к OpenTSDB. Однако можно настроить правила удаления старых данных (называются Retention Policies). А также запросы по свертке старых данных. Да, тут правила сворачивания старых данных не зашиты строго, а могут полностью определяться пользователем. Нужно создать Continuous Queries, которые постоянно будут читать одни метрики, сворачивать их и складывать в другие метрики.
У InfluxDB есть веб админка. Не самая выдающаяся, но через неё вполне можно разобраться с базами данных и выяснить, что за метрики там хранятся.
Гораздо интереснее API InfluxDB. Чтобы вставить данные в версии 0.8 предлагалось постить JSON. В версии 0.9 перешли на текстовый протокол, сильно похожий на Graphite. (Таймстамп Инфлюксина может подставить сама).
% curl -i -XPOST 'http://localhost:8086/write?db=test' \
    -d 'test_metric,host=localhost,test=alpha value=1'
% curl -i -XPOST 'http://localhost:8086/write?db=test' \
    -d 'test_metric,host=localhost,test=alpha value=2
        test_metric,host=localhost,test=beta value=3'
При выборке возвращается JSON.
% curl -G 'http://localhost:8086/query' \
    --data-urlencode "db=test" \
    --data-urlencode "pretty=true" \
    --data-urlencode "q=SELECT * FROM test_metric"      
{
    "results": [
        {
            "series": [
                {
                    "name": "test_metric",
                    "tags": {
                        "host": "localhost",
                        "test": "alpha"
                    },
                    "columns": [
                        "time",
                        "value"
                    ],
                    "values": [
                        [
                            "2015-06-28T16:31:47.795210017Z",
                            1
                        ],
                        [
                            "2015-06-28T16:33:43.423490806Z",
                            2
                        ]
                    ]
                },
                {
                    "name": "test_metric",
                    "tags": {
                        "host": "localhost",
                        "test": "beta"
                    },
                    "columns": [
                        "time",
                        "value"
                    ],
                    "values": [
                        [
                            "2015-06-28T16:33:43.423490806Z",
                            3
                        ]
                    ]
                }
            ]
        }
    ]
}
Да, все верно, язык запросов в InfluxDB — SQL-подобное нечто.
Теги можно использовать как условие выборки.
% curl -G 'http://localhost:8086/query' \
        --data-urlencode "db=test" \
        --data-urlencode "pretty=true" \
        --data-urlencode "q=SELECT * FROM test_metric WHERE test='alpha'"    
{
    "results": [
        {
            "series": [
                {
                    "name": "test_metric",
                    "tags": {
                        "host": "localhost",
                        "test": "alpha"
                    },
                    "columns": [
                        "time",
                        "value"
                    ],
                    "values": [
                        [
                            "2015-06-28T16:31:47.795210017Z",
                            1
                        ],
                        [
                            "2015-06-28T16:33:43.423490806Z",
                            2
                        ]
                    ]
                }
            ]
        }
    ]
}
Можно агрегировать результаты как по времени, так и по тегам.
% curl -G 'http://localhost:8086/query' \
            --data-urlencode "db=test" \
            --data-urlencode "pretty=true" \
            --data-urlencode "q=SELECT sum(value) FROM test_metric \
                                WHERE time > '2015-06-28T16:31:00Z' AND time < '2015-06-28T16:34:00Z' \
                                GROUP BY time(1m), test"  
{
    "results": [
        {
            "series": [
                {
                    "name": "test_metric",
                    "tags": {
                        "test": "alpha"
                    },
                    "columns": [
                        "time",
                        "sum"
                    ],
                    "values": [
                        [
                            "2015-06-28T16:31:00Z",
                            1
                        ],
                        [
                            "2015-06-28T16:32:00Z",
                            null
                        ],
                        [
                            "2015-06-28T16:33:00Z",
                            2
                        ]
                    ]
                },
                {
                    "name": "test_metric",
                    "tags": {
                        "test": "beta"
                    },
                    "columns": [
                        "time",
                        "sum"
                    ],
                    "values": [
                        [
                            "2015-06-28T16:31:00Z",
                            null
                        ],
                        [
                            "2015-06-28T16:32:00Z",
                            null
                        ],
                        [
                            "2015-06-28T16:33:00Z",
                            3
                        ]
                    ]
                }
            ]
        }
    ]
}
Группировка по тегам порождает несколько серий в результате. Группировка по времени нужна для построения графиков. В этом случае обязательно нужно указать интервал времени, и все отчеты за указанный период будут сгруппированы по интервалам указанной длины с использованием указанной функции агрегирования. Функций этих довольно много. Но все же меньше, чем у Graphite. Впрочем, это несколько компенсируется возможностью использовать выражения в запросах, ну как в SQL. И, вроде как, можно делать запросы к нескольним метрикам сразу, джойнить "таблицы". Кстати, складывается ощущение, что не все функции и фичи по джойну из версии 0.8 перенесли в 0.9. Но работа идет.
В отличие от Graphite, у InfluxDB нет встроенного средства рисования графиков. Но с InfluxDB классно работает мощнющий и красивый дашбоард Grafana. Причем даже поддерживается версия 0.9.
Grafana
Однако дашбоарды хороши, если вам нужно глазками постоянно наблюдать обновляющиеся данные. Если же нужно просто показать исторические данные, совсем не трудно нарисовать свой собственный дашбоард. В конце концов, InfluxDB возвращает JSON, который легко обработать в JavaScript прямо в браузере и нарисовать график с помощью какого-нибудь Richshaw. Правда правда, это просто.