Elasticsearch как хранилище логов: индексы по времени
Как именно логи ложатся в Elasticsearch и почему индексы разбивают по времени, а не валят в один большой.
Индекс по времени — приём, при котором логи каждого периода (день, неделя) пишутся в отдельный индекс вида
logs-2026.06.24, что упрощает поиск по дате и удаление старых данных.
Зачем разбивать индексы
Основы индексов и шардов Elasticsearch разобраны в отдельном учебнике — здесь нас интересует специфика именно логов. А специфика в том, что логи — это поток с временем жизни: их пишут постоянно, старые рано или поздно удаляют, читают чаще всего свежие. Под такой профиль один гигантский индекс «на всё» подходит плохо.
Если все логи лить в единственный индекс logs, то, во-первых, удалить данные старше 30 дней можно только запросом delete by query — он медленный и оставляет фрагментацию. Во-вторых, такой индекс бесконечно растёт, его шарды раздуваются, поиск по свежим данным замедляется из-за объёма старых.
Решение: индекс на период
Поэтому логи кладут в индексы, нарезанные по времени. Раньше это делали суффиксом-датой в имени:
logs-2026.06.22 logs-2026.06.23 logs-2026.06.24 <-- сюда пишутся сегодняшние логи
Удалить логи за день — это просто DELETE logs-2026.06.22: мгновенная операция, удаляющая целый индекс, без фрагментации. Поиск «за вчера» идёт только по одному нужному индексу, а не по всему массиву истории.
Как работает под капотом: data streams и rollover
Современный Elastic не заставляет вас вручную мудрить с датами в имени. Вместо этого есть data stream — абстракция «бесконечного потока логов», за которой скрывается набор скрытых индексов с автоматическими именами вида .ds-logs-app-2026.06.24-000017.
{
"index_patterns": ["logs-app-*"],
"data_stream": {},
"template": {
"settings": { "number_of_shards": 1 }
}
}Вы пишете в один логический поток logs-app, а Elasticsearch сам по правилу rollover создаёт новый физический индекс, когда текущий достигает порога — например, 50 ГБ или возраста 1 день. Так размер каждого индекса остаётся управляемым автоматически, без ручного выбора «по дням или по неделям».
пишем сюда: logs-app (один логический поток)
|
rollover при 50 ГБ / 1 дне создаёт под капотом:
.ds-logs-app-...000015 (закрыт)
.ds-logs-app-...000016 (закрыт)
.ds-logs-app-...000017 (активный, сюда идёт запись)Маппинг логов — коротко
Маппинг — это схема полей индекса (какое поле какого типа). Для логов важны несколько решений: поле времени @timestamp типа date (по нему всё сортируется и фильтруется), текст сообщения, числа (status, bytes), а строковые идентификаторы лучше делать типом keyword (точное совпадение и агрегации), а не text (полнотекстовый разбор). Подробный разбор маппинга — в учебнике по Elasticsearch; для логов запомните: @timestamp + продуманные типы полей, иначе агрегации и сортировка будут работать неверно.
Почему логи — это append-only поток
Важная особенность логов, определяющая всю стратегию их хранения: логи практически никогда не обновляются и не удаляются по одному. Запись лога фиксирует факт, который уже случился, — её не редактируют. Это отличает логи от типичных данных приложения (где строки таблиц постоянно меняются) и роднит их с временными рядами: только добавление в конец (append-only) и массовое удаление целыми периодами. Elasticsearch и нарезка по времени идеально ложатся на этот профиль: новые события дописываются в активный индекс, а устаревшие удаляются оптом сбросом целого индекса. Любая попытка обращаться с логами как с изменяемой базой (искать и обновлять отдельные документы) идёт против их природы и упирается в неэффективность.
Из append-only природы следует ещё одно: для логов почти всегда отключают или минимизируют то, что нужно изменяемым данным. Например, репликацию на холодных индексах убирают (потеря старого узла не страшна, переиндексировать нечего), refresh_interval увеличивают (мгновенная видимость каждой записи логам не нужна), а сами документы делают неизменяемыми. Понимание, что логи — это поток фактов, а не редактируемая база, помогает принимать все эти решения осознанно.
Частые ошибки
- Один индекс на всё. Главная ошибка хранения логов: невозможно дёшево удалять старое, индекс растёт бесконечно. Всегда нарезайте по времени (data stream + rollover).
- Слишком мелкая нарезка. Индекс на каждый час при малом объёме порождает тысячи крошечных шардов — это тоже вредно. Размер шарда логов держат в районе десятков ГБ.
- Идентификаторы как text. Если
trace_idилиuser_idпопадут типомtext, по ним нельзя точно агрегировать. Для таких полей нуженkeyword.
Итоги
- Логи — это поток с временем жизни, поэтому их хранят в индексах, нарезанных по времени, а не в одном большом.
- Нарезка делает удаление старого мгновенным (
DELETEцелого индекса) и ускоряет поиск по свежим данным. - Data stream + rollover автоматизируют создание новых индексов по порогу размера/возраста.
- Для логов критичны
@timestampтипаdateи идентификаторы типаkeyword.