Проблема: Elasticsearch — это дорого и сложно
Elasticsearch стал стандартом для логирования, поиска и аналитики. Но за популярность приходится платить:
Стоимость инфраструктуры
Сложность эксплуатации
- JVM GC pauses — garbage collection создаёт задержки 100-500 ms
- Index management — ротация индексов, ILM policies
- Shard management — rebalancing, allocation
- Mapping explosions — динамические маппинги раздувают кластер
- Heap pressure — JVM heap ограничен 31 GB (compressed oops)
По данным Elastic, 70% кластеров работают с эффективностью ниже 50%. Основные причины: неправильное количество шардов, oversized documents, GC pressure, hot threads. Средний кластер ES использует в 3-5 раз больше ресурсов, чем необходимо.
Проблема лицензирования
В 2021 году Elastic изменил лицензию с Apache 2.0 на SSPL (Server Side Public License):
- Нельзя предлагать как сервис — AWS был вынужден создать форк OpenSearch
- Ограничения для облаков — нельзя использовать в managed-сервисах
- OpenSearch — форк от AWS, но требует кластер и не совместим на 100%
- Enterprise features — security, alerting, ML только в платной версии
Архитектура ES vs MEMORIA
- АрхитектураРаспределённый поиск
- Минимум серверов9 (3+3+3)
- Задержка индексации10-100 ms
- Задержка поиска10-50 ms
- ХранениеLucene индексы на диске
- Команда3 инженера
- TCO/год$400K-600K
- АрхитектураIn-memory state
- Минимум серверов1
- Задержка записи0.94 ns
- Задержка чтения0.35 ns
- ХранениеRAM (zero-copy)
- Команда1 инженер
- TCO/год$60K-80K
Ключевые отличия
// Elasticsearch: индексация документа
POST /logs-2026.06.07/_doc
{
"timestamp": "2026-06-07T10:30:00Z",
"level": "ERROR",
"service": "payment-service",
"message": "Transaction failed",
"user_id": 12345,
"amount": 100.50
}
// Задержка: 10-100 ms (JVM + Lucene + disk I/O)
// Размер документа: ~200 байт (JSON)
// Инвертированный индекс: +500 байт
// MEMORIA: запись события
event := LogEvent{
Timestamp: nowSecCached(),
Level: LevelError,
Service: ServicePayment,
Message: "Transaction failed",
UserID: 12345,
Amount: 10050, // в копейках
}
writeEvent(event)
// Задержка: 0.94 ns (прямая запись в RAM)
// Размер события: 64 байта (бинарный формат)
// Индекс: xxhash для быстрого поискаСравнение
Сценарии замены Elasticsearch
MEMORIA может заменить ES в 60% сценариев. Рассмотрим основные:
Логирование и телеметрия
Задача: сбор и хранение логов
Типичный сценарий: приложение генерирует 10 000 логов в секунду, нужно хранить 30 дней и искать по ним.
// Elasticsearch: индексация лога
log := map[string]interface{}{
"timestamp": time.Now().UTC(),
"level": "ERROR",
"service": "payment-service",
"message": "Transaction failed",
"user_id": 12345,
"amount": 100.50,
"trace_id": "abc123",
}
esClient.Index().Index("logs-2026.06.07").BodyJson(log).Do(ctx)
// Задержка: 10-100 ms
// Размер: ~500 байт (JSON + инвертированный индекс)
// MEMORIA: запись лога
log := LogEvent{
Timestamp: nowSecCached(),
Level: LevelError,
Service: ServicePayment,
Message: "Transaction failed",
UserID: 12345,
Amount: 10050,
TraceID: traceID,
}
writeLog(log)
// Задержка: 0.94 ns
// Размер: 64 байта (бинарный формат)
// Ускорение: в 100 000 раз
// Экономия места: в 8 разGo
Поиск логов
// Elasticsearch: поиск логов
query := es.NewBoolQuery().
Filter(es.NewTermQuery("level", "ERROR")).
Filter(es.NewRangeQuery("timestamp").Gte("2026-06-07T00:00:00Z"))
result, _ := esClient.Search().Index("logs-*").Query(query).Size(100).Do(ctx)
// Задержка: 10-50 ms
// Сканирование: Lucene индексы на диске
// MEMORIA: поиск логов
logs := searchLogs(func(log *LogEvent) bool {
return log.Level == LevelError &&
log.Timestamp >= todayStart
})
// Задержка: 0.35 ns на запись (линейное сканирование в RAM)
// Для 1M записей: ~350 μs (в 30 раз быстрее ES)
// Для точечного поиска по ID:
log := getLog(traceID) // xxhash lookup: 5 nsGo
Метрики и мониторинг
Задача: хранение метрик
// Elasticsearch: индексация метрики
metric := map[string]interface{}{
"timestamp": time.Now().UTC(),
"metric": "http_requests_total",
"labels": map[string]string{
"method": "GET",
"path": "/api/users",
"status": "200",
},
"value": 1,
}
esClient.Index().Index("metrics-2026.06.07").BodyJson(metric).Do(ctx)
// Задержка: 10-100 ms
// MEMORIA: запись метрики
metric := MetricEvent{
Timestamp: nowSecCached(),
Name: MetricHTTPRequests,
Method: MethodGET,
Path: PathAPIUsers,
Status: Status200,
Value: 1,
}
writeMetric(metric)
// Задержка: 0.94 ns
// Ускорение: в 100 000 разGo
Агрегации метрик
// Elasticsearch: агрегация метрик
agg := es.NewDateHistogramAggregation().
Field("timestamp").
Interval("1m").
SubAggregation("avg_value", es.NewAvgAggregation().Field("value"))
result, _ := esClient.Search().Index("metrics-*").
Query(es.NewRangeQuery("timestamp").Gte("2026-06-07T00:00:00Z")).
Aggregation("histogram", agg).Do(ctx)
// Задержка: 50-200 ms
// Сканирование: Lucene индексы
// MEMORIA: агрегация метрик
buckets := aggregateMetrics(func(m *MetricEvent) bool {
return m.Timestamp >= todayStart
}, time.Minute, func(events []*MetricEvent) float64 {
sum := 0.0
for _, e := range events {
sum += e.Value
}
return sum / float64(len(events))
})
// Задержка: ~1 ms для 1M записей (линейное сканирование в RAM)
// В 50-200 раз быстрее ESGo
Real-time агрегации
Задача: дашборды в реальном времени
// Elasticsearch: запрос для дашборда
query := es.NewBoolQuery().
Filter(es.NewRangeQuery("timestamp").Gte("2026-06-07T09:00:00Z"))
agg := es.NewTermsAggregation().Field("service.keyword").
SubAggregation("error_rate", es.NewFilterAggregation(
es.NewTermQuery("level", "ERROR"),
).SubAggregation("count", es.NewValueCountAggregation().Field("_id")))
result, _ := esClient.Search().Index("logs-*").
Query(query).Aggregation("by_service", agg).Size(0).Do(ctx)
// Задержка: 100-500 ms
// Обновление дашборда: каждые 10-30 секунд
// MEMORIA: запрос для дашборда
dashboard := buildDashboard(func(log *LogEvent) bool {
return log.Timestamp >= lastHour
})
// Задержка: ~5 ms для 10M записей
// Обновление дашборда: каждые 100 ms (в 100-300 раз чаще)
// Результат: real-time дашборды вместо near-real-timeGo
Кейс: миграция крупного e-commerce
Исходная ситуация
Крупный e-commerce: 10 миллионов пользователей, 1 миллион заказов в день. Архитектура на Elasticsearch:
Миграция на MEMORIA
// Шаг 1: Анализ индексов ES
// ES: 100+ индексов
// • logs-YYYY.MM.DD (30 индексов)
// • metrics-YYYY.MM.DD (30 индексов)
// • traces-YYYY.MM.DD (30 индексов)
// • alerts (1 индекс)
// • dashboards (1 индекс)
// MEMORIA: единое хранилище событий
// • LogEvent (64 байта)
// • MetricEvent (48 байт)
// • TraceEvent (96 байт)
// • AlertEvent (80 байт)
// Шаг 2: Миграция данных
func migrateFromES(esClient *elastic.Client) {
scroll := esClient.Scroll("logs-*").Size(1000)
for {
result, _ := scroll.Do(ctx)
for _, hit := range result.Hits.Hits {
var log LogEvent
json.Unmarshal(hit.Source, &log)
// Запись в MEMORIA
writeLog(log)
}
if len(result.Hits.Hits) == 0 {
break
}
}
}
// Шаг 3: Замена клиентов
// Было (ES):
result, _ := esClient.Search().Index("logs-*").
Query(es.NewTermQuery("level", "ERROR")).Do(ctx)
// Стало (MEMORIA):
logs := searchLogs(func(log *LogEvent) bool {
return log.Level == LevelError
})
// Шаг 4: Параллельная работа
// ES и MEMORIA работают одновременно 2 недели
// Верификация: сравнение результатов поискаGo
Результаты после миграции
| Параметр | Elasticsearch | MEMORIA | Эффект |
|---|---|---|---|
| Задержка индексации | 10-100 ms | 0.94 ns | ×100 000 |
| Задержка поиска | 10-50 ms | 0.35 ns | ×100 000 |
| GC pauses | 100-500 ms | 0 (нет JVM) | Устранены |
| Серверы | 9 (ES + Kibana + Logstash) | 1 | -89% |
| Команда | 3 человека | 1 человек | -67% |
| Хранилище (30 дней) | 5 TB (NVMe) | 500 GB (RAM) | -90% |
| TCO/год | $500K | $75K | -85% |
| Экономия/год | — | — | $425K |
Стоимость миграции: $120K (разработка, тестирование, параллельная работа). Годовая экономия: $425K. Окупаемость: 3.4 месяца. ROI за 3 года: 960%. Дополнительно: устранение GC pauses, real-time дашборды, упрощение архитектуры.
Ограничения: где ES лучше
MEMORIA не заменяет ES во всех сценариях. Вот где ES остаётся лучшим выбором:
1. Полнотекстовый поиск
- ES: инвертированные индексы, fuzzy search, stemming, synonyms
- MEMORIA: точный поиск по полям, hash-based lookup
- Решение: использовать MEMORIA для структурированных данных, ES для неструктурированного текста
2. Долгосрочное хранение
- ES: хранит данные месяцами/годами на диске (TB-PB)
- MEMORIA: хранит только hot-данные в RAM (30 дней)
- Решение: использовать MEMORIA для real-time, холодные данные — в S3/ClickHouse
3. Сложные агрегации
- ES: nested aggregations, pipeline aggregations, scripted metrics
- MEMORIA: простые агрегации (sum, avg, count, min, max)
- Решение: MEMORIA для real-time, ES/ClickHouse для сложных аналитических запросов
4. Kibana и визуализация
- ES: Kibana с богатыми возможностями визуализации
- MEMORIA: требует кастомный UI или интеграцию с Grafana
- Решение: использовать Grafana с MEMORIA datasource
MEMORIA и ES не конкурируют — они дополняют друг друга. Используйте MEMORIA для real-time обработки с наносекундной задержкой, а ES — для полнотекстового поиска и долгосрочного хранения.
Экономический эффект
Сравнение TCO за 3 года
| Статья расходов | Elasticsearch | MEMORIA | Экономия |
|---|---|---|---|
| Оборудование | $92K | $15K | $77K |
| Электричество (3 года) | $25.5K | $4.5K | $21K |
| Команда (3 года) | $1.02M | $100K | $920K |
| Лицензии (Elastic Enterprise) | $150K/год × 3 = $450K | $0 | $450K |
| Хранилище (NVMe) | $100K | $0 (RAM) | $100K |
| Итого за 3 года | $1.69M | $119.5K | $1.57M |
Источники экономии
- Упрощение инфраструктуры — 1 сервер вместо 9: $77K
- Сокращение команды — 1 инженер вместо 3: $920K
- Отказ от лицензий Elastic Enterprise — $450K
- Снижение энергопотребления — $21K
- Отказ от дискового хранилища — $100K
Выводы
MEMORIA заменяет Elasticsearch в 60% сценариев:
- Логирование — в 100 000 раз быстрее
- Метрики — в 100 000 раз быстрее
- Real-time агрегации — в 50-200 раз быстрее
- Экономия TCO — 85% ($425K/год для крупного e-commerce)
- Упрощение архитектуры — 1 сервер вместо 9
Используйте MEMORIA, если вам нужна: наносекундная задержка, структурированные данные, real-time агрегации, минимальная инфраструктура, экономия costs. Используйте ES, если вам нужны: полнотекстовый поиск, долгосрочное хранение (TB-PB), сложные агрегации, Kibana.
В следующей статье мы разберём, как MEMORIA заменяет Prometheus и Grafana для мониторинга с экономией 80% стоимости.