MongoDB
Основные концепции
MongoDB - документо-ориентированная NoSQL база данных, которая хранит данные в формате BSON (Binary JSON). Основные отличия от реляционных БД:
- Гибкая схема данных (schema-less)
- Горизонтальное масштабирование из коробки
- Встроенная поддержка репликации и шардинга
Терминология:
- Database - база данных (аналог схемы в SQL)
- Collection - коллекция документов (аналог таблицы)
- Document - документ в формате BSON (аналог строки)
- Field - поле документа (аналог колонки)
- _id - уникальный идентификатор документа (автогенерируется)
CRUD операции
Create (Создание)
// Вставка одного документа
db.users.insertOne({name: "John", age: 30, email: "john@example.com"})
// Вставка нескольких документов
db.users.insertMany([
{name: "Alice", age: 25},
{name: "Bob", age: 35}
])
Read (Чтение)
// Найти все документы
db.users.find()
// Найти с условием
db.users.find({age: {$gte: 30}})
// Найти один документ
db.users.findOne({name: "John"})
// Проекция (выбор конкретных полей)
db.users.find({}, {name: 1, email: 1, _id: 0})
Update (Обновление)
// Обновить один документ
db.users.updateOne(
{name: "John"},
{$set: {age: 31}}
)
// Обновить несколько документов
db.users.updateMany(
{age: {$lt: 30}},
{$inc: {age: 1}}
)
// Upsert (создать если не существует)
db.users.updateOne(
{name: "Mike"},
{$set: {age: 28}},
{upsert: true}
)
Delete (Удаление)
// Удалить один документ
db.users.deleteOne({name: "John"})
// Удалить несколько документов
db.users.deleteMany({age: {$lt: 18}})
Операторы запросов
Операторы сравнения
$eq
- равно$ne
- не равно$gt
- больше$gte
- больше или равно$lt
- меньше$lte
- меньше или равно$in
- содержится в массиве$nin
- не содержится в массиве
Логические операторы
// AND (по умолчанию)
db.users.find({age: {$gte: 25}, status: "active"})
// OR
db.users.find({$or: [{age: {$lt: 25}}, {status: "premium"}]})
// NOT
db.users.find({age: {$not: {$gte: 30}}})
Операторы для массивов
// $all - содержит все элементы
db.users.find({skills: {$all: ["Java", "MongoDB"]}})
// $size - размер массива
db.users.find({skills: {$size: 3}})
// $elemMatch - элемент массива соответствует условию
db.users.find({scores: {$elemMatch: {$gte: 80, $lt: 90}}})
Индексы
Индексы - структуры данных для ускорения поиска. MongoDB автоматически создает индекс для поля _id.
// Создание индекса
db.users.createIndex({name: 1}) // по возрастанию
db.users.createIndex({age: -1}) // по убыванию
db.users.createIndex({name: 1, age: -1}) // составной индекс
// Уникальный индекс
db.users.createIndex({email: 1}, {unique: true})
// Текстовый индекс для поиска
db.users.createIndex({name: "text", bio: "text"})
// Просмотр индексов
db.users.getIndexes()
// Удаление индекса
db.users.dropIndex({name: 1})
Aggregation Framework
Aggregation Pipeline - мощный инструмент для обработки данных, работает как конвейер этапов.
Основные этапы (stages):
$match
- фильтрация документов$group
- группировка данных$sort
- сортировка$project
- преобразование структуры$limit
- ограничение количества$skip
- пропуск документов$lookup
- join с другой коллекцией
// Группировка и подсчет
db.orders.aggregate([
{$match: {status: "completed"}},
{$group: {
_id: "$customerId",
totalOrders: {$sum: 1},
totalAmount: {$sum: "$amount"}
}},
{$sort: {totalAmount: -1}}
])
// Lookup (join)
db.orders.aggregate([
{$lookup: {
from: "customers",
localField: "customerId",
foreignField: "_id",
as: "customer"
}}
])
Репликация
Replica Set - группа MongoDB серверов для обеспечения высокой доступности. Состоит из:
- Primary - принимает все записи
- Secondary - реплики для чтения
- Arbiter - участвует только в выборах (опционально)
// Инициализация replica set
rs.initiate({
_id: "myReplicaSet",
members: [
{_id: 0, host: "mongo1:27017"},
{_id: 1, host: "mongo2:27017"},
{_id: 2, host: "mongo3:27017"}
]
})
// Проверка статуса
rs.status()
Шардинг
Шардинг - горизонтальное разделение данных между несколькими серверами. Компоненты:
- Shard - сервер с частью данных
- Config Server - хранит метаданные
- Mongos - роутер запросов
// Включение шардинга для базы
sh.enableSharding("myDatabase")
// Шардинг коллекции
sh.shardCollection("myDatabase.users", {userId: 1})
Транзакции
Транзакции - ACID операции для нескольких документов (доступны с версии 4.0).
// Начало сессии
session = db.getMongo().startSession()
// Транзакция
session.startTransaction()
try {
session.getDatabase("mydb").users.insertOne({name: "John"})
session.getDatabase("mydb").orders.insertOne({userId: "john_id"})
session.commitTransaction()
} catch (error) {
session.abortTransaction()
}
Spring Data MongoDB
Spring Data MongoDB - интеграция MongoDB с Spring Framework.
Основные аннотации:
@Document
- маркирует класс как документ@Id
- указывает поле ID@Field
- кастомное имя поля@Indexed
- создает индекс@DBRef
- ссылка на другой документ
@Document(collection = "users")
public class User {
@Id
private String id;
@Field("full_name")
private String name;
@Indexed(unique = true)
private String email;
@DBRef
private List<Order> orders;
}
Repository интерфейс:
public interface UserRepository extends MongoRepository<User, String> {
List<User> findByAgeGreaterThan(int age);
@Query("{'name': ?0}")
User findByCustomName(String name);
@Aggregation(pipeline = {
"{ '$match': { 'status': 'active' } }",
"{ '$group': { '_id': '$department', 'count': { '$sum': 1 } } }"
})
List<DepartmentStats> getActiveUsersByDepartment();
}
Производительность и оптимизация
Основные рекомендации:
- Создавайте индексы для часто используемых полей в запросах
- Используйте проекцию для выборки только нужных полей
- Денормализуйте данные для избежания лишних join-ов
- Мониторьте производительность с помощью explain()
// Анализ производительности запроса
db.users.find({age: {$gte: 30}}).explain("executionStats")
// Профилирование (включение логирования медленных запросов)
db.setProfilingLevel(2, {slowms: 100})
Важные особенности для собеседования
Отличия от реляционных БД:
- Схема данных: гибкая vs жесткая
- ACID: поддержка транзакций появилась в версии 4.0
- Масштабирование: горизонтальное vs вертикальное
- Запросы: специфичные операторы vs SQL
Когда использовать MongoDB:
- Быстрая разработка с изменяющимися требованиями
- Большие объемы данных с необходимостью горизонтального масштабирования
- Документы с вложенной структурой
- Кэширование и сессии
Ограничения:
- Размер документа не более 16MB
- Максимум 100 уровней вложенности
- Имена полей не могут содержать точки или начинаться с $
- Отсутствие foreign key constraints
Команды администрирования
// Информация о базе данных
db.stats()
// Размер коллекции
db.users.stats()
// Восстановление и проверка
db.repairDatabase()
// Backup
mongodump --db myDatabase --out /backup/
// Restore
mongorestore --db myDatabase /backup/myDatabase/
Redis
Основные концепции
Redis (Remote Dictionary Server) — это высокопроизводительная NoSQL база данных типа "ключ-значение" в памяти, которая может использоваться как кеш, брокер сообщений или основное хранилище данных.
Ключевые особенности:
- Все данные хранятся в оперативной памяти
- Поддерживает различные структуры данных
- Атомарные операции
- Персистентность данных (снимки и журналирование)
- Встроенная репликация и кластеризация
Структуры данных
Строки (Strings)
Самый простой тип данных — бинарно-безопасные строки до 512 МБ.
SET user:1:name "John" # Установить значение
GET user:1:name # Получить значение
INCR counter # Атомарно увеличить число на 1
EXPIRE session:123 3600 # Установить TTL (время жизни) в секундах
Хеши (Hashes)
Коллекции полей и значений, идеальны для представления объектов.
HSET user:1 name "John" age 30 email "john@example.com"
HGET user:1 name # Получить одно поле
HGETALL user:1 # Получить все поля
HEXISTS user:1 phone # Проверить существование поля
Списки (Lists)
Упорядоченные коллекции строк, реализованные как двусвязные списки.
LPUSH queue:tasks "task1" "task2" # Добавить в начало
RPUSH queue:tasks "task3" # Добавить в конец
LPOP queue:tasks # Извлечь с начала
LRANGE queue:tasks 0 -1 # Получить все элементы
Множества (Sets)
Неупорядоченные коллекции уникальных строк.
SADD tags:post:1 "redis" "database" "nosql"
SMEMBERS tags:post:1 # Получить все элементы
SISMEMBER tags:post:1 "redis" # Проверить принадлежность
SINTER tags:post:1 tags:post:2 # Пересечение множеств
Отсортированные множества (Sorted Sets)
Множества с весами (scores) для каждого элемента.
ZADD leaderboard 100 "player1" 250 "player2" 180 "player3"
ZRANGE leaderboard 0 -1 WITHSCORES # Получить по возрастанию
ZREVRANGE leaderboard 0 2 # Топ-3 игрока
ZRANK leaderboard "player1" # Позиция в рейтинге
Паттерны использования
Кеширование
Redis часто используется для кеширования дорогих операций или результатов запросов к базе данных.
Стратегии кеширования:
- Cache-Aside — приложение управляет кешем
- Write-Through — запись одновременно в кеш и БД
- Write-Behind — асинхронная запись в БД
Сессии пользователей
Хранение данных сессий с автоматическим истечением.
SETEX session:abc123 1800 '{"user_id": 42, "role": "admin"}'
Счетчики и метрики
Атомарные операции для подсчета событий.
INCR page:views:home
HINCRBY stats:daily visits 1
Pub/Sub (Издатель-Подписчик)
Система обмена сообщениями в реальном времени.
PUBLISH news:updates "Breaking news!"
SUBSCRIBE news:updates
Персистентность данных
RDB (Redis Database)
Создает снимки данных через определенные интервалы.
- Плюсы: компактность, быстрое восстановление
- Минусы: возможна потеря данных между снимками
AOF (Append Only File)
Записывает каждую операцию изменения в лог-файл.
- Плюсы: минимальная потеря данных
- Минусы: больший размер файлов, медленнее восстановление
Настройка персистентности
# В redis.conf
save 900 1 # Сохранить если изменился 1 ключ за 15 минут
appendonly yes # Включить AOF
Репликация и высокая доступность
Master-Slave репликация
Асинхронная репликация данных с главного сервера на реплики.
# На slave-сервере
SLAVEOF 192.168.1.100 6379
Redis Sentinel
Система мониторинга и автоматического переключения при отказе master-сервера.
- Отслеживает состояние Redis-инстансов
- Автоматически переключает slave на master
- Предоставляет service discovery
Redis Cluster
Горизонтальное масштабирование с автоматическим шардингом.
- Данные распределяются по 16384 слотам
- Каждый узел отвечает за определенные слоты
- Встроенная репликация и failover
Производительность и оптимизация
Управление памятью
INFO memory # Информация об использовании памяти
FLUSHDB # Очистить текущую БД
CONFIG SET maxmemory 256mb # Ограничить использование памяти
Политики вытеснения (Eviction Policies)
Когда память заканчивается:
- allkeys-lru — удаляет наименее используемые ключи
- volatile-lru — удаляет наименее используемые ключи с TTL
- allkeys-random — удаляет случайные ключи
- volatile-ttl — удаляет ключи с наименьшим TTL
Пайплайнинг
Отправка нескольких команд за один раз для уменьшения latency.
// Java пример с Jedis
Pipeline pipeline = jedis.pipelined();
pipeline.set("key1", "value1");
pipeline.set("key2", "value2");
pipeline.sync();
Интеграция с Java
Популярные клиенты
- Jedis — простой и быстрый клиент
- Lettuce — асинхронный клиент с поддержкой reactive streams
- Redisson — полнофункциональный клиент с распределенными объектами
Spring Data Redis
@RedisHash("users")
public class User {
@Id
private String id;
private String name;
// ...
}
@Repository
public interface UserRepository extends CrudRepository<User, String> {
List<User> findByName(String name);
}
Мониторинг и отладка
Полезные команды
INFO # Общая информация о сервере
MONITOR # Отслеживание всех команд в реальном времени
SLOWLOG GET 10 # Получить 10 самых медленных операций
CLIENT LIST # Список подключенных клиентов
MEMORY USAGE key # Использование памяти конкретным ключом
Ключевые метрики
- connected_clients — количество подключений
- used_memory — использованная память
- hits/misses ratio — эффективность кеширования
- evicted_keys — количество удаленных ключей
- keyspace_hits/keyspace_misses — статистика обращений
Безопасность
Базовая защита
# В redis.conf
requirepass mypassword # Установить пароль
bind 127.0.0.1 # Привязать к определенному IP
rename-command FLUSHDB "" # Отключить опасные команды
Шифрование
Redis 6.0+ поддерживает TLS для шифрования соединений и AUTH для аутентификации пользователей.
Распространенные проблемы
Большие ключи
Ключи размером >1MB могут блокировать сервер. Используйте MEMORY USAGE
для мониторинга.
Горячие ключи
Часто запрашиваемые ключи могут создавать узкие места. Решение — репликация или шардинг.
Блокирующие операции
Команды вроде KEYS *
могут заблокировать сервер. Используйте SCAN
для итерации по ключам.
Альтернативы и сравнение
- Memcached — проще, но менее функционален
- Hazelcast — Java-ориентированное решение
- Apache Ignite — более сложная платформа с SQL-поддержкой
Redis остается популярным выбором благодаря простоте, производительности и богатому функционалу для большинства задач кеширования и обмена сообщениями в enterprise-приложениях.
ClickHouse
Основные концепции
ClickHouse — это колоночная СУБД для онлайн-аналитической обработки данных (OLAP), оптимизированная для быстрого выполнения аналитических запросов на больших объемах данных.
Ключевые особенности:
- Колоночное хранение — данные хранятся по столбцам, а не по строкам
- Векторизация — обработка данных блоками для повышения производительности
- Сжатие данных — эффективное сжатие благодаря колоночному формату
- Линейная масштабируемость — производительность растет пропорционально добавлению серверов
- SQL-совместимость — поддерживает большинство SQL-конструкций
Архитектура и движки таблиц
Движок MergeTree
Основной движок для аналитических данных с поддержкой сортировки и индексов.
CREATE TABLE events (
date Date,
user_id UInt64,
event_type String,
value Float64
) ENGINE = MergeTree()
ORDER BY (date, user_id);
Особенности MergeTree:
- Данные автоматически сортируются по ключу сортировки
- Поддерживает первичные и вторичные индексы
- Фоновое слияние кусков данных для оптимизации хранения
- Поддержка партиционирования по датам или другим полям
ReplacingMergeTree
Автоматически удаляет дубликаты при слиянии кусков данных.
CREATE TABLE user_profiles (
user_id UInt64,
name String,
updated_at DateTime
) ENGINE = ReplacingMergeTree(updated_at)
ORDER BY user_id;
SummingMergeTree
Автоматически суммирует числовые столбцы при слиянии записей с одинаковыми ключами.
CREATE TABLE metrics (
date Date,
metric_name String,
value UInt64
) ENGINE = SummingMergeTree()
ORDER BY (date, metric_name);
Distributed
Распределяет данные по нескольким серверам кластера.
CREATE TABLE events_distributed AS events
ENGINE = Distributed(cluster_name, database, events, rand());
Типы данных
Числовые типы
- UInt8, UInt16, UInt32, UInt64 — беззнаковые целые числа
- Int8, Int16, Int32, Int64 — знаковые целые числа
- Float32, Float64 — числа с плавающей точкой
- Decimal(P, S) — точные десятичные числа
Строковые типы
- String — переменная длина, UTF-8
- FixedString(N) — фиксированная длина N байт
- LowCardinality(String) — оптимизация для строк с малым количеством уникальных значений
Дата и время
- Date — дата в формате YYYY-MM-DD
- DateTime — дата и время с точностью до секунды
- DateTime64 — дата и время с подсекундной точностью
Массивы и сложные типы
CREATE TABLE complex_data (
id UInt64,
tags Array(String),
metadata Map(String, String),
nested Nested(name String, value UInt64)
);
Индексы и оптимизация
Первичный ключ
Определяется параметром ORDER BY и создает разреженный индекс.
-- Данные физически сортируются по (date, user_id)
-- Индекс содержит каждую 8192-ю строку
ORDER BY (date, user_id)
Вторичные индексы
Дополнительные индексы для ускорения запросов по неключевым полям.
ALTER TABLE events ADD INDEX idx_event_type event_type TYPE set(100) GRANULARITY 1;
Материализованные представления
Предвычисленные агрегаты для ускорения аналитических запросов.
CREATE MATERIALIZED VIEW daily_stats
ENGINE = SummingMergeTree()
ORDER BY (date, event_type)
AS SELECT
toDate(timestamp) as date,
event_type,
count() as events_count
FROM events
GROUP BY date, event_type;
Производительность запросов
Проекции
Альтернативные представления данных той же таблицы с другой сортировкой.
ALTER TABLE events ADD PROJECTION proj_by_event_type (
SELECT user_id, event_type, value
ORDER BY (event_type, user_id)
);
Sampling
Выборочный анализ данных для быстрого получения приблизительных результатов.
CREATE TABLE events_sampled (
date Date,
user_id UInt64,
event_type String
) ENGINE = MergeTree()
ORDER BY (date, user_id)
SAMPLE BY intHash32(user_id);
-- Запрос с выборкой 10% данных
SELECT event_type, count() * 10 as estimated_count
FROM events_sampled SAMPLE 0.1
GROUP BY event_type;
Партиционирование
Разделение данных на части для ускорения запросов и управления данными.
CREATE TABLE events_partitioned (
date Date,
user_id UInt64,
event_type String
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(date)
ORDER BY (date, user_id);
Функции и операторы
Агрегатные функции
SELECT
count() as total_events,
uniq(user_id) as unique_users,
avg(value) as avg_value,
quantile(0.95)(response_time) as p95_response_time,
topK(5)(event_type) as top_events
FROM events;
Функции для работы с датами
SELECT
toStartOfDay(timestamp) as day,
toStartOfHour(timestamp) as hour,
toYYYYMM(date) as year_month,
dateDiff('day', start_date, end_date) as days_diff
FROM events;
Функции для работы с массивами
SELECT
arrayJoin(tags) as tag, -- Развернуть массив в строки
has(tags, 'premium') as is_premium,
length(tags) as tags_count
FROM user_data;
Интеграция с Java
JDBC драйвер
// Подключение к ClickHouse
String url = "jdbc:clickhouse://localhost:8123/default";
Connection conn = DriverManager.getConnection(url);
// Batch-вставка для высокой производительности
PreparedStatement stmt = conn.prepareStatement(
"INSERT INTO events (date, user_id, event_type, value) VALUES (?, ?, ?, ?)"
);
for (Event event : events) {
stmt.setDate(1, event.getDate());
stmt.setLong(2, event.getUserId());
stmt.setString(3, event.getType());
stmt.setDouble(4, event.getValue());
stmt.addBatch();
}
stmt.executeBatch();
HTTP интерфейс
// Отправка данных через HTTP POST
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://localhost:8123/"))
.header("Content-Type", "text/plain")
.POST(HttpRequest.BodyPublishers.ofString(
"INSERT INTO events FORMAT CSV\n" + csvData
))
.build();
Репликация и кластеризация
Репликация
Синхронная репликация данных между серверами с использованием ZooKeeper.
CREATE TABLE events_replicated (
date Date,
user_id UInt64,
event_type String
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/events', 'replica1')
ORDER BY (date, user_id);
Шардирование
Горизонтальное разделение данных между узлами кластера.
<!-- config.xml -->
<remote_servers>
<analytics_cluster>
<shard>
<replica>
<host>clickhouse1.example.com</host>
<port>9000</port>
</replica>
</shard>
<shard>
<replica>
<host>clickhouse2.example.com</host>
<port>9000</port>
</replica>
</shard>
</analytics_cluster>
</remote_servers>
Мониторинг и производительность
Системные таблицы
-- Активные запросы
SELECT query, elapsed, memory_usage
FROM system.processes
WHERE query != '';
-- Статистика по таблицам
SELECT table, rows, bytes_on_disk
FROM system.parts
WHERE active = 1;
-- Медленные запросы
SELECT query, query_duration_ms, memory_usage
FROM system.query_log
WHERE query_duration_ms > 1000
ORDER BY query_duration_ms DESC;
Профилирование запросов
-- Включить профилирование
SET send_logs_level = 'trace';
SET log_queries = 1;
-- Анализ плана выполнения
EXPLAIN PIPELINE SELECT count() FROM events WHERE date >= '2023-01-01';
Особенности разработки
Асинхронные вставки
Группировка небольших вставок для повышения производительности.
SET async_insert = 1;
SET wait_for_async_insert = 1;
INSERT INTO events VALUES (today(), 123, 'click', 1.5);
Оптимизация памяти
-- Настройка размера блока для запросов
SET max_block_size = 65536;
SET max_memory_usage = 10000000000; -- 10GB
-- Использование внешней сортировки при нехватке памяти
SET max_bytes_before_external_group_by = 1000000000;
Работа с большими данными
-- Потоковая обработка для больших результатов
SELECT * FROM huge_table
FORMAT TSVRaw
SETTINGS max_result_rows = 0, max_result_bytes = 0;
Сравнение с другими СУБД
ClickHouse vs PostgreSQL
- ClickHouse: колоночное хранение, OLAP, быстрые агрегации
- PostgreSQL: строковое хранение, OLTP, ACID-транзакции
ClickHouse vs Apache Spark
- ClickHouse: SQL-база данных, интерактивные запросы
- Apache Spark: вычислительный движок, batch-обработка
ClickHouse vs Elasticsearch
- ClickHouse: структурированные данные, SQL-запросы
- Elasticsearch: полнотекстовый поиск, неструкт. данные
Практические рекомендации
Дизайн схемы
- Используйте правильный порядок колонок в ORDER BY (от наиболее к наименее селективным)
- Применяйте LowCardinality для строк с малым количеством уникальных значений
- Избегайте nullable полей без необходимости
Оптимизация запросов
- Используйте PREWHERE вместо WHERE для фильтрации по колонкам не из ORDER BY
- Применяйте LIMIT для ограничения результатов
- Используйте материализованные представления для часто выполняемых агрегаций
Управление данными
- Регулярно выполняйте OPTIMIZE TABLE для принудительного слияния кусков
- Используйте TTL для автоматического удаления старых данных
- Мониторьте количество кусков данных (parts) в таблицах
ClickHouse — это мощная система для аналитики больших данных, которая требует понимания её архитектуры и особенностей для эффективного использования в enterprise-проектах.
MinIO S3
Что такое MinIO
MinIO — это высокопроизводительное объектное хранилище, совместимое с Amazon S3 API. Основные преимущества:
- Self-hosted решение — можно развернуть на собственной инфраструктуре
- S3-совместимость — используется тот же API, что и у Amazon S3
- Высокая производительность — оптимизировано для больших объемов данных
- Distributed storage — поддержка кластерной архитектуры
- Erasure coding — технология защиты данных от потерь
Архитектура и основные концепции
Bucket (Корзина)
Контейнер для хранения объектов. Аналог папки верхнего уровня в файловой системе.
Object (Объект)
Файл + метаданные. Каждый объект имеет уникальный ключ (key) внутри bucket.
Key (Ключ)
Уникальный идентификатор объекта внутри bucket. Может содержать "/" для имитации иерархии папок.
Metadata (Метаданные)
Дополнительная информация об объекте (Content-Type, размер, пользовательские заголовки).
Настройка Java клиента
Зависимости Maven
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.7</version>
</dependency>
Создание клиента
MinioClient client = MinioClient.builder()
.endpoint("http://localhost:9000") // URL MinIO сервера
.credentials("minioadmin", "minioadmin") // Access key и Secret key
.build();
Объяснение параметров:
endpoint
— URL сервера MinIO (по умолчанию порт 9000)credentials
— пара ключей для аутентификации- Для production используйте HTTPS и сильные пароли
Операции с Bucket
Создание bucket
client.makeBucket(MakeBucketArgs.builder()
.bucket("my-bucket")
.region("us-east-1") // Регион (опционально)
.build());
Проверка существования bucket
boolean exists = client.bucketExists(BucketExistsArgs.builder()
.bucket("my-bucket")
.build());
Список всех bucket
List<Bucket> buckets = client.listBuckets();
for (Bucket bucket : buckets) {
System.out.println(bucket.name() + " - " + bucket.creationDate());
}
Удаление bucket
client.removeBucket(RemoveBucketArgs.builder()
.bucket("my-bucket")
.build());
Важно: Bucket можно удалить только если он пустой.
Операции с объектами
Загрузка файла
client.uploadObject(UploadObjectArgs.builder()
.bucket("my-bucket")
.object("path/to/file.txt") // Ключ объекта
.filename("/local/path/file.txt") // Локальный путь
.contentType("text/plain") // MIME-тип
.build());
Загрузка из InputStream
InputStream stream = new FileInputStream("file.txt");
client.putObject(PutObjectArgs.builder()
.bucket("my-bucket")
.object("file.txt")
.stream(stream, stream.available(), -1)
.contentType("text/plain")
.build());
Параметры stream():
stream
— поток данныхobjectSize
— размер объекта (-1 если неизвестен)partSize
— размер части для multipart upload (-1 для автоопределения)
Скачивание файла
client.downloadObject(DownloadObjectArgs.builder()
.bucket("my-bucket")
.object("file.txt")
.filename("/local/download/file.txt")
.build());
Получение объекта как InputStream
try (InputStream stream = client.getObject(GetObjectArgs.builder()
.bucket("my-bucket")
.object("file.txt")
.build())) {
// Работа с потоком
byte[] data = stream.readAllBytes();
}
Получение информации об объекте
StatObjectResponse stat = client.statObject(StatObjectArgs.builder()
.bucket("my-bucket")
.object("file.txt")
.build());
System.out.println("Size: " + stat.size());
System.out.println("ETag: " + stat.etag());
System.out.println("Content-Type: " + stat.contentType());
ETag — уникальный идентификатор версии объекта, аналог MD5 хеша.
Удаление объекта
client.removeObject(RemoveObjectArgs.builder()
.bucket("my-bucket")
.object("file.txt")
.build());
Массовое удаление
List<DeleteObject> objects = Arrays.asList(
new DeleteObject("file1.txt"),
new DeleteObject("file2.txt")
);
Iterable<Result<DeleteError>> results = client.removeObjects(
RemoveObjectsArgs.builder()
.bucket("my-bucket")
.objects(objects)
.build());
// Проверка ошибок
for (Result<DeleteError> result : results) {
DeleteError error = result.get();
if (error != null) {
System.err.println("Error: " + error.message());
}
}
Работа со списками объектов
Список объектов в bucket
Iterable<Result<Item>> results = client.listObjects(
ListObjectsArgs.builder()
.bucket("my-bucket")
.prefix("folder/") // Фильтр по префиксу
.recursive(true) // Рекурсивный обход
.maxKeys(1000) // Максимум объектов за запрос
.build());
for (Result<Item> result : results) {
Item item = result.get();
System.out.println(item.objectName() + " - " + item.size());
}
Параметры:
prefix
— фильтрация по началу ключа (имитация папок)recursive
— true для рекурсивного обхода всех "подпапок"maxKeys
— ограничение количества результатов
Пагинация при больших списках
String marker = null;
do {
ListObjectsArgs args = ListObjectsArgs.builder()
.bucket("my-bucket")
.maxKeys(100)
.marker(marker) // Начать с этого ключа
.build();
Iterable<Result<Item>> results = client.listObjects(args);
for (Result<Item> result : results) {
Item item = result.get();
marker = item.objectName(); // Сохраняем для следующей итерации
}
} while (marker != null);
Presigned URLs
Presigned URL — временная ссылка для доступа к объекту без аутентификации.
Создание ссылки для скачивания
String url = client.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket("my-bucket")
.object("file.txt")
.expiry(60 * 60) // Время жизни в секундах (1 час)
.build());
Создание ссылки для загрузки
String uploadUrl = client.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.PUT)
.bucket("my-bucket")
.object("new-file.txt")
.expiry(60 * 10) // 10 минут
.build());
Применение: Позволяет клиентам напрямую загружать/скачивать файлы, минуя ваш сервер.
Метаданные и заголовки
Установка метаданных при загрузке
Map<String, String> userMetadata = new HashMap<>();
userMetadata.put("author", "john-doe");
userMetadata.put("department", "engineering");
client.putObject(PutObjectArgs.builder()
.bucket("my-bucket")
.object("document.pdf")
.stream(inputStream, size, -1)
.contentType("application/pdf")
.userMetadata(userMetadata) // Пользовательские метаданные
.build());
Установка заголовков
Map<String, String> headers = new HashMap<>();
headers.put("Cache-Control", "max-age=3600");
headers.put("Content-Disposition", "attachment; filename=document.pdf");
client.putObject(PutObjectArgs.builder()
.bucket("my-bucket")
.object("document.pdf")
.stream(inputStream, size, -1)
.headers(headers)
.build());
Разница:
userMetadata
— произвольные метаданные с префиксом "x-amz-meta-"headers
— стандартные HTTP заголовки
Копирование объектов
Простое копирование
client.copyObject(CopyObjectArgs.builder()
.bucket("destination-bucket")
.object("new-file.txt")
.source(CopySource.builder()
.bucket("source-bucket")
.object("original-file.txt")
.build())
.build());
Копирование с изменением метаданных
Map<String, String> metadata = new HashMap<>();
metadata.put("copied-at", "2025-01-15");
client.copyObject(CopyObjectArgs.builder()
.bucket("destination-bucket")
.object("new-file.txt")
.source(CopySource.builder()
.bucket("source-bucket")
.object("original-file.txt")
.build())
.metadataDirective(Directive.REPLACE) // Заменить метаданные
.userMetadata(metadata)
.build());
Multipart Upload
Multipart Upload — загрузка больших файлов частями. Преимущества:
- Возможность параллельной загрузки частей
- Восстановление после сбоев
- Загрузка файлов до 5TB
Инициация multipart upload
CreateMultipartUploadResponse response = client.createMultipartUpload(
CreateMultipartUploadArgs.builder()
.bucket("my-bucket")
.object("large-file.zip")
.build());
String uploadId = response.result().uploadId();
Загрузка части
UploadPartResponse partResponse = client.uploadPart(
UploadPartArgs.builder()
.bucket("my-bucket")
.object("large-file.zip")
.uploadId(uploadId)
.partNumber(1) // Номер части (начинается с 1)
.data(partInputStream, partSize, -1)
.build());
String etag = partResponse.etag(); // Сохранить для завершения
Завершение multipart upload
Part[] parts = {new Part(1, etag1), new Part(2, etag2)};
client.completeMultipartUpload(
CompleteMultipartUploadArgs.builder()
.bucket("my-bucket")
.object("large-file.zip")
.uploadId(uploadId)
.parts(parts)
.build());
Отмена multipart upload
client.abortMultipartUpload(
AbortMultipartUploadArgs.builder()
.bucket("my-bucket")
.object("large-file.zip")
.uploadId(uploadId)
.build());
Политики безопасности (Bucket Policy)
Bucket Policy — JSON-документ, определяющий разрешения доступа.
Установка публичной политики чтения
String policy = """
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"AWS": "*"},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*"
}
]
}
""";
client.setBucketPolicy(SetBucketPolicyArgs.builder()
.bucket("my-bucket")
.config(policy)
.build());
Получение текущей политики
String currentPolicy = client.getBucketPolicy(
GetBucketPolicyArgs.builder()
.bucket("my-bucket")
.build());
Элементы политики:
Effect
— Allow/DenyPrincipal
— кто получает разрешение (* = все)Action
— какие действия разрешеныResource
— на какие ресурсы распространяется
Уведомления (Event Notifications)
Event Notifications — уведомления о событиях в bucket (создание, удаление объектов).
Настройка уведомлений
NotificationConfiguration config = new NotificationConfiguration();
QueueConfiguration queueConfig = new QueueConfiguration();
queueConfig.setQueue("arn:minio:sqs::primary:webhook");
queueConfig.addEvent(EventType.OBJECT_CREATED_PUT);
queueConfig.addFilterRule("prefix", "documents/");
queueConfig.addFilterRule("suffix", ".pdf");
config.setQueueConfigurations(Arrays.asList(queueConfig));
client.setBucketNotification(SetBucketNotificationArgs.builder()
.bucket("my-bucket")
.config(config)
.build());
Типы событий:
OBJECT_CREATED_PUT
— создание объекта через PUTOBJECT_CREATED_POST
— создание через POSTOBJECT_REMOVED_DELETE
— удаление объекта
Обработка ошибок
Основные исключения
try {
client.statObject(StatObjectArgs.builder()
.bucket("my-bucket")
.object("nonexistent.txt")
.build());
} catch (ErrorResponseException e) {
if (e.errorResponse().code().equals("NoSuchKey")) {
System.out.println("Объект не найден");
}
} catch (InsufficientDataException e) {
System.out.println("Недостаточно данных");
} catch (InternalException e) {
System.out.println("Внутренняя ошибка");
} catch (InvalidKeyException | InvalidResponseException e) {
System.out.println("Проблемы с форматом");
} catch (IOException e) {
System.out.println("Ошибка сети");
} catch (NoSuchAlgorithmException | XmlParserException e) {
System.out.println("Проблемы с парсингом/криптографией");
}
Проверка доступности сервиса
public boolean isMinIOHealthy() {
try {
client.listBuckets();
return true;
} catch (Exception e) {
return false;
}
}
Конфигурация для Production
Connection Pool
OkHttpClient httpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES))
.build();
MinioClient client = MinioClient.builder()
.endpoint("https://minio.company.com")
.credentials(accessKey, secretKey)
.httpClient(httpClient)
.build();
SSL/TLS конфигурация
MinioClient client = MinioClient.builder()
.endpoint("https://minio.company.com", 9000, true) // true = HTTPS
.credentials(accessKey, secretKey)
.build();
Retry механизм
public <T> T executeWithRetry(Supplier<T> operation, int maxRetries) {
Exception lastException = null;
for (int i = 0; i < maxRetries; i++) {
try {
return operation.get();
} catch (Exception e) {
lastException = e;
if (i < maxRetries - 1) {
try {
Thread.sleep(1000 * (i + 1)); // Exponential backoff
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
}
}
throw new RuntimeException("Operation failed after retries", lastException);
}
Spring Boot интеграция
Configuration класс
@Configuration
public class MinioConfig {
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.access-key}")
private String accessKey;
@Value("${minio.secret-key}")
private String secretKey;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
}
Service класс
@Service
public class FileStorageService {
private final MinioClient minioClient;
private final String bucketName = "app-files";
public FileStorageService(MinioClient minioClient) {
this.minioClient = minioClient;
initializeBucket();
}
@PostConstruct
private void initializeBucket() {
try {
if (!minioClient.bucketExists(BucketExistsArgs.builder()
.bucket(bucketName).build())) {
minioClient.makeBucket(MakeBucketArgs.builder()
.bucket(bucketName).build());
}
} catch (Exception e) {
throw new RuntimeException("Не удалось создать bucket", e);
}
}
public String uploadFile(String fileName, InputStream inputStream,
String contentType) throws Exception {
String objectName = UUID.randomUUID() + "_" + fileName;
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(inputStream, inputStream.available(), -1)
.contentType(contentType)
.build());
return objectName;
}
}
Мониторинг и метрики
Основные метрики для отслеживания
- Request latency — время отклика операций
- Error rate — процент неуспешных запросов
- Throughput — количество операций в секунду
- Storage usage — использование дискового пространства
- Connection pool usage — использование пула соединений
Логирование операций
@Slf4j
@Component
public class MinioOperationLogger {
public <T> T logOperation(String operation, Supplier<T> supplier) {
long startTime = System.currentTimeMillis();
try {
T result = supplier.get();
long duration = System.currentTimeMillis() - startTime;
log.info("MinIO operation {} completed in {}ms", operation, duration);
return result;
} catch (Exception e) {
long duration = System.currentTimeMillis() - startTime;
log.error("MinIO operation {} failed after {}ms: {}",
operation, duration, e.getMessage());
throw e;
}
}
}
Важные особенности для собеседования
Eventual Consistency
MinIO обеспечивает строгую консистентность для операций записи, в отличие от Amazon S3 (eventual consistency).
Erasure Coding
Технология защиты данных, разбивающая объект на части и добавляющая избыточность. Позволяет восстановить данные при потере части дисков.
Distributed Mode
В кластерном режиме MinIO требует минимум 4 диска. Рекомендуется четное количество узлов.
Performance Tips
- Используйте connection pooling
- Реализуйте retry логику
- Для больших файлов используйте multipart upload
- Мониторьте размеры bucket (влияет на производительность list операций)
Security Best Practices
- Всегда используйте HTTPS в production
- Регулярно ротируйте access keys
- Используйте принцип минимальных привилегий в bucket policies
- Не храните credentials в коде — используйте переменные окружения или секреты
Эта шпаргалка покрывает основные концепции и практические примеры работы с MinIO S3, которые важны для Senior Java Backend разработчика.
Cassandra
Коротко: Cassandra — распределённая (данные хранятся на многих узлах), отказоустойчивая (переживает падения узлов), горизонтально масштабируемая колонночная СУБД. Работает по принципу tunable consistency (настраиваемый уровень согласованности). Данные проектируем от запросов, без JOIN и агрегатов на стороне БД — используем денормализацию.
Оглавление
- 1. Архитектура и базовые сущности
- 2. Модель данных и CQL
- 3. Консистентность (CL) и репликация (RF)
- 4. Запись/чтение: путь данных, TTL и tombstones
- 5. Компакция и компрессия (STCS/LCS/TWCS)
- 6. Индексы, Materialized Views, поиск
- 7. Проектирование «от запросов» (паттерны/антипаттерны)
- 8. Эксплуатация: nodetool, бэкапы, repair
- 9. Конфигурация и топология (DC/Rack/Snitch)
- 10. Производительность и наблюдаемость
- 11. Клиенты/драйверы (Java пример)
- 12. Безопасность (аутентификация, шифрование)
- 13. Частые рецепты (CQL-шпаргалка)
- 14. Словарь терминов и аббревиатур
- Мини‑чек‑лист для продакшена
1) Архитектура и базовые сущности
- Кластер → DC (Data Center, датацентр) → Rack → Нода (узел) → Keyspace (логический «база данных») → Таблица.
- Кольцо (ring): все ноды равноправны (masterless). Данные распределяются по токенам (диапазонам хэш‑пространства).
- Partitioner — функция, которая превращает ключ партиции в токен. По умолчанию: Murmur3Partitioner.
- vnodes (виртуальные ноды) — каждая физическая нода держит много небольших диапазонов токенов (обычно
num_tokens: 256
) для равномерного баланса. - Replication Factor (RF) — сколько копий (реплик) каждой партиции хранится в кластере/в DC.
- Consistency Level (CL) — сколько реплик должны подтвердить операцию чтения/записи (см. §3).
- SSTable — неизменяемый («immutable») файл на диске с данными таблицы (формат LSM‑дерева).
- Memtable — память, куда пишут новые данные до сброса («flush») на диск в SSTable.
- Commit Log — журнал предзаписи (WAL), куда попадает каждая запись перед фиксацией в memtable для восстановления после сбоев.
- LSM‑дерево (Log‑Structured Merge‑Tree) — подход хранения: быстрые последовательные записи, периодические слияния файлов (compaction).
- Gossip — протокол обмена метаданными между нодами (кто жив, где находится, версия и т. п.).
- Seed nodes — стартовые адреса для госсип‑обмена при запуске ноды (не «мастера», просто точки входа).
2) Модель данных и CQL
Ключи
PRIMARY KEY ((partition_key), clustering1, clustering2, ...)
- Partition key — распределяет данные по нодам; минимальная единица чтения/хранения — партиция.
- Clustering columns — сортируют строки внутри партиции (по умолчанию ASC; можно задать DESC).
Правила моделирования
- Начинаем с списка запросов: каждую выборку нужно уметь выполнить по ключам (без фильтров по неключевым колонкам).
- Одна таблица = один/несколько близких запросов. Денормализация — норма.
- Избегаем гига‑партиций: разумно держать размер партиции — десятки МБ, а число строк — сотни тысяч, но не миллионы. Для временных рядов делаем бакеты (см. ниже).
- Нет JOIN/AGG как в SQL‑СУБД → считаем/агрегируем вне Cassandra или поддерживаем отдельные материализованные представления (с оговорками).
Пример: события пользователя с бакетами по дню
CREATE TABLE app.events_by_user_day (
user_id uuid,
day_bucket date, -- YYYY-MM-DD
event_ts timestamp,
event_type text,
payload text,
PRIMARY KEY ((user_id, day_bucket), event_ts)
) WITH CLUSTERING ORDER BY (event_ts DESC);
3) Консистентность (CL) и репликация (RF)
-
RF (Replication Factor) — сколько копий партиции хранится в кластере (или в каждом DC при
NetworkTopologyStrategy
). -
CL (Consistency Level) — сколько реплик должны участвовать в операции:
ONE
,TWO
,THREE
— подтверждение от 1/2/3 реплик.QUORUM
— кворм всех реплик (больше половины).LOCAL_QUORUM
— кворм в пределах текущего DC.ALL
— все реплики.LOCAL_ONE
— одна реплика в текущем DC.
-
Правило сильного чтения: если
CLread + CLwrite > RF
(в рамках одного DC), то чтения увидят последнюю успешную запись (пример: RF=3, записьLOCAL_QUORUM
, чтениеLOCAL_QUORUM
). -
Типовой прод‑выбор: RF=3 в DC и
LOCAL_QUORUM
для чтений/записей.
4) Запись/чтение: путь данных, TTL и tombstones
Запись: координатор (любой узел, принявший запрос) → Commit Log (диск) → Memtable (RAM) → периодический flush → новые SSTable.
Чтение: Bloom‑фильтр (проверка «может быть есть/точно нет») → индекс/summary → чтение нужных SSTable + memtable → слияние результата.
- TTL (Time To Live) — время жизни строки/ячейки. По истечении создаётся tombstone (маркёр удаления).
- Tombstones накапливаются до тех пор, пока компакция их не вычистит после периода
gc_grace_seconds
(по умолчанию 10 дней). - Избыток tombstones повышает латентность и риск OOM при широких сканах.
- Read Repair — ленивое исправление расхождений при чтении.
- Hinted Handoff — «подсказки» для временно недоступной реплики, чтобы потом доставить пропущенные записи.
5) Компакция и компрессия (STCS/LCS/TWCS)
Compaction — слияние SSTable для удаления дубликатов и tombstones.
- STCS (Size‑Tiered Compaction Strategy) — по умолчанию, сгруппированные по размеру файлы сливаются; универсально, но может держать больше дубликатов.
- LCS (Leveled Compaction Strategy) — уровни фиксированного размера; идеально для запросов по узким диапазонам (меньше дубликатов, предсказуемое чтение), но дороже по IO/CPU.
- TWCS (Time‑Window Compaction Strategy) — для временных рядов с TTL/Retention; файлы группируются по окнам времени (дни/часы).
ALTER TABLE app.events_by_user_day
WITH compaction = {
'class': 'TimeWindowCompactionStrategy',
'compaction_window_unit': 'DAYS',
'compaction_window_size': '1'
};
Compression — обычно LZ4Compressor
снижает IO на чтениях/записях за счёт CPU.
6) Индексы, Materialized Views, поиск
- Secondary Index (встроенный вторичный индекс) — годится, когда:
- уже сузили партицию по ключу, и
- индексируемая колонка низкой кардинальности.
Для глобальных запросов по высокой кардинальности — не подходит.
- Materialized Views (MV) — автоматическое поддержание альтернативного ключа чтения. Плюсы — удобство; минусы — накладные расходы и ограничения согласованности/отказоустойчивости. Использовать осознанно.
- Полнотекстовый/сложный поиск — выносить в Elasticsearch/OpenSearch или спец‑хранилища.
7) Проектирование «от запросов» (паттерны/антипаттерны)
- Составьте список конкретных
SELECT
: фильтры, сортировки, пределы, объёмы, частоты. - Под каждый важный запрос — своя таблица/MV.
- Избегайте «горячих» партиций — вводите бакетирование (например,
(user_id, day_bucket)
), либо хэш‑соль. - Пагинация — за счёт clustering‑порядка и
LIMIT
/paging (не «OFFSET»). - Агрегации/счётчики — либо counters (см. рецепт), либо вне БД.
Антипаттерны:
ALLOW FILTERING
в проде.- LOGGED BATCH на десятки/сотни разных партиций (это не транзакция между партициями).
- Гига‑партиции (например, без бакетов по времени).
- Массовые
UPDATE/DELETE
→ шторм tombstones.
8) Эксплуатация: nodetool
, бэкапы, repair
Частые команды:
nodetool status # состояние кластера (UN/UJ/DN/...)
nodetool ring # распределение токенов
nodetool info # инфо по ноде
nodetool tpstats # thread pools
nodetool cfstats # статистика таблиц
nodetool compactionstats # компакции
nodetool flush [ks] [tbl] # форс-flush
nodetool repair [ks] [tbl] --full|--incremental # антиэнтропия
nodetool cleanup # после уменьшения RF/перебалансировки
nodetool decommission # корректно удалить ноду
nodetool removenode <id> # убрать мёртвую ноду
nodetool rebuild # подтянуть данные из DC
nodetool snapshot [ks] # снапшот (бэкап)
Бэкапы:
- Снапшоты (жёсткие ссылки на SSTable) +
incremental_backups: true
для дельт. - Восстановление: остановить ноду → вернуть файлы →
nodetool refresh
. - Массовая загрузка:
sstableloader
.
Repair:
- Делайте инкрементальный repair регулярно (дни/недели) + периодический
--full
. Иначе риск «воскрешения» удалённых данных послеgc_grace_seconds
.
9) Конфигурация и топология (DC/Rack/Snitch)
Файл cassandra.yaml
(важное):
cluster_name
,partitioner
(обычноMurmur3Partitioner
),num_tokens
(примерно 256).- Сети:
listen_address
,rpc_address
,seed_provider
(несколько seed на DC). - Snitch — определяет DC/Rack‑привязку: обычно
GossipingPropertyFileSnitch
+ файлcassandra-rackdc.properties
(dc=...
,rack=...
). - IO/память:
concurrent_reads/writes
,memtable_flush_writers
,commitlog_segment_size_in_mb
. - Диски: разнести Commit Log и Data на разные устройства. Для Data — несколько каталогов под разные диски (JBOD).
- Выбор replication strategy на keyspace:
NetworkTopologyStrategy
для прод (задаём RF по каждому DC).
10) Производительность и наблюдаемость
Что мониторить:
- Латентности p50/p95/p99 (медиана/95‑й/99‑й перцентиль) по чтению/записи.
- Таймауты: ReadTimeout, WriteTimeout, Unavailable.
- Кол‑во tombstones на запрос (предупреждения в логах, в
cqlsh
приTRACING ON
). - Очереди компакции, бэкт‑прешер на flush/compaction.
- Heap/GC паузы JVM, hit‑rate page cache.
- Давность и длительность repair.
Грубые ориентиры по ресурсам:
- Память (RAM): heap 8–16 ГБ (слишком большие heap ведут к длинным GC‑паузам), остальное — под page cache.
- Диски: NVMe/SSD дают наибольшую пользу. Коммит‑лог — отдельный быстрый диск.
- CPU: больше ядер помогает при компакции/потоках чтения.
11) Клиенты/драйверы (Java пример)
Рекомендации:
- Включайте Token‑Aware routing (запросы летят на владельца партиции).
- Балансировка DC‑local (не ходим меж DC без нужды).
- Настраивайте paging (по умолчанию ~5k строк/страница) и таймауты.
- Помечайте запросы как idempotent (идемпотентные), чтобы драйвер мог безопасно ретраить.
Пример (Java Driver 4.x):
CqlSession session = CqlSession.builder()
.withLocalDatacenter("dc1") // DC для LOCAL_*
.build();
// Пейджинг:
SimpleStatement st = SimpleStatement.builder(
"SELECT * FROM app.events_by_user_day WHERE user_id=? AND day_bucket=?")
.addPositionalValues(userId, day)
.setPageSize(5000)
.setIdempotence(true)
.build();
ResultSet rs = session.execute(st);
12) Безопасность (аутентификация, шифрование)
-
Аутентификация: включить
authenticator: PasswordAuthenticator
иauthorizer: CassandraAuthorizer
, создать роли/пользователей, выдать минимально необходимые права. -
Шифрование трафика:
- Клиентское:
client_encryption_options
(TLS между драйвером и нодой). - Межузловое:
server_encryption_options
(TLS между нодами).
- Клиентское:
-
Секреты (пароли, ключи) — хранить вне конфигов (Vault и т. п.).
13) Частые рецепты (CQL‑шпаргалка)
Keyspace с RF по DC:
CREATE KEYSPACE app
WITH REPLICATION = {
'class': 'NetworkTopologyStrategy',
'dc1': '3',
'dc2': '2'
} AND DURABLE_WRITES = true;
Таблица заказов по клиенту (последние N):
CREATE TABLE app.orders_by_customer (
customer_id uuid,
created_at timestamp,
order_id uuid,
status text,
amount decimal,
PRIMARY KEY ((customer_id), created_at, order_id)
) WITH CLUSTERING ORDER BY (created_at DESC);
SELECT * FROM app.orders_by_customer
WHERE customer_id = ?
LIMIT 50;
Лёгкие транзакции (LWT, Paxos) — уникальный ник:
INSERT INTO app.unique_usernames (username, user_id)
VALUES ('sergey', 123)
IF NOT EXISTS; -- вернёт применилось/нет
BATCH — только для одной партиции (оптимизация сети):
BEGIN BATCH
INSERT INTO app.orders_by_customer (...) VALUES (...);
INSERT INTO app.orders_by_customer (...) VALUES (...);
APPLY BATCH; -- те же (customer_id), иначе не используйте LOGGED BATCH
Counters (счётчики):
CREATE TABLE app.likes_counter (
post_id uuid PRIMARY KEY,
likes counter
);
UPDATE app.likes_counter SET likes = likes + 1 WHERE post_id = ?;
TTL и автоснос старых данных:
ALTER TABLE app.events_by_user_day
WITH default_time_to_live = 86400; -- сутки
-- либо построчный TTL:
INSERT INTO app.events_by_user_day (...) VALUES (...) USING TTL 3600;
Материализация альтернативного ключа чтения:
CREATE TABLE app.orders_by_status_day (
status text,
day_bucket date,
created_at timestamp,
order_id uuid,
customer_id uuid,
amount decimal,
PRIMARY KEY ((status, day_bucket), created_at, order_id)
) WITH CLUSTERING ORDER BY (created_at DESC);
14) Словарь терминов и аббревиатур
- CQL (Cassandra Query Language) — язык запросов Cassandra, похож на SQL, но без JOIN/AGG.
- RF (Replication Factor) — число копий каждой партиции.
- CL (Consistency Level) — сколько реплик должны подтвердить запрос (например,
LOCAL_QUORUM
). - DC (Data Center) — логическая группа нод (обычно физически разнесённых).
- Rack — подгруппа нод внутри DC для отказоустойчивости.
- Partition key — ключ, определяющий распределение данных по нодам и границы партиции.
- Clustering columns — колонки, задающие порядок строк в партиции.
- SSTable — неизменяемый файловый сегмент данных таблицы на диске.
- Memtable — в памяти структура для новых/изменённых данных до записи в SSTable.
- Commit Log (WAL) — журнал предзаписи для устойчивости к сбоям.
- LSM‑дерево — структура хранения на основе последовательных записей и периодических слияний.
- Compaction — процесс слияния SSTable с удалением дублей и tombstones.
- STCS/LCS/TWCS — стратегии компакции: Size‑Tiered / Leveled / Time‑Window.
- Bloom‑фильтр — вероятностная структура, быстро отвечающая «точно нет / может быть есть».
- TTL (Time To Live) — «срок жизни» данных; по истечении возникает tombstone (маркёр удаления).
- Tombstone — специальная запись‑«надгробие», означающая удаление/истечение TTL до окончательной чистки компакцией.
gc_grace_seconds
— период, в течение которого tombstones хранятся для корректной репликации/repair, чтобы не «воскресить» удалённые данные.- Repair (антиэнтропия) — выравнивание расхождений между репликами (по Merkle‑деревьям).
- Read Repair — исправление расхождений при чтении данных.
- Hinted Handoff — временное хранение «подсказок» для упавшей реплики с последующей доставкой.
- Gossip — протокол обмена состоянием между нодами.
- Snitch — компонент, сообщающий Cassandra о топологии DC/Rack для правильной репликации/маршрутизации.
- Seed nodes — адреса для начального госсип‑обмена при запуске ноды.
- vnodes (virtual nodes) — множественные диапазоны токенов на одной ноде для балансировки.
- Murmur3Partitioner — стандартный хэш‑партиционер.
- Materialized View (MV) — таблица, автоматически поддерживаемая из исходной с альтернативным ключом.
- Secondary Index — встроенный индекс по неключевой колонке; применим в узких случаях.
- Idempotent (идемпотентный) запрос — повтор безопасен, результат не меняется.
- Paging — постраничная выборка: сервер отдаёт данные частями (страницами).
- Per‑centile p50/p95/p99 — 50/95/99‑й перцентили латентности (медиана и «хвосты»).
- Backoff — стратегия задержек между повторами (ретраями).
- LOGGED/UNLOGGED BATCH — батч с/без журнала; LOGGED над многими партициями — антипаттерн.
Мини‑чек‑лист для продакшена
-
NetworkTopologyStrategy
, RF≥3 в каждом DC, запросыLOCAL_QUORUM
- Регулярный incremental repair + периодический
--full
- Раздельные диски для Commit Log и Data
- Модель данных покрывает все запросы без
ALLOW FILTERING
- Контроль размеров партиций; time‑series — через бакеты +
default_time_to_live
- Драйверы: token‑aware, DC‑local, paging, таймауты, ретраи только для idempotent
Виды хранилищ
Классификация хранилищ данных
По типу доступа к данным
- Файловые системы - доступ через файловые операции
- Блочные хранилища - доступ к блокам данных напрямую
- Объектные хранилища - доступ к объектам через HTTP API
По расположению
- Локальные - на том же сервере что и приложение
- Сетевые - доступ через сеть (NAS, SAN)
- Облачные - управляемые облачными провайдерами
Реляционные базы данных (RDBMS)
Характеристики
- ACID-совместимость - Atomicity, Consistency, Isolation, Durability
- SQL-запросы - стандартизированный язык запросов
- Строгая схема - структура данных определена заранее
- Нормализация - устранение избыточности данных
Популярные RDBMS
- PostgreSQL - расширяемая, поддержка JSON, массивов
- MySQL - высокая производительность, простота
- Oracle - корпоративные функции, высокая надежность
- SQL Server - интеграция с Microsoft ecosystem
Применение
- Транзакционные системы (банковские операции)
- ERP, CRM системы
- Системы с сложными связями между данными
// Пример работы с PostgreSQL через JPA
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private BigDecimal amount;
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;
}
NoSQL базы данных
Document-based (Документные)
MongoDB
- Хранение в формате BSON (Binary JSON)
- Гибкая схема - документы могут иметь разную структуру
- Горизонтальное масштабирование через шардирование
- Индексирование по любым полям
// MongoDB с Spring Data
@Document(collection = "products")
public class Product {
@Id
private String id;
private String name;
private Map<String, Object> attributes; // Гибкая структура
}
@Repository
public interface ProductRepository extends MongoRepository<Product, String> {
List<Product> findByAttributesContaining(String key, Object value);
}
CouchDB
- RESTful HTTP API
- Multi-Version Concurrency Control (MVCC)
- Репликация master-master
- MapReduce для агрегации данных
Key-Value (Ключ-значение)
Redis
- In-memory хранилище с персистентностью
- Структуры данных: strings, lists, sets, sorted sets, hashes
- Pub/Sub messaging
- Lua скриптинг для атомарных операций
// Redis с Spring Boot
@Service
public class CacheService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void cacheUser(String userId, User user) {
redisTemplate.opsForValue().set("user:" + userId, user, Duration.ofHours(1));
}
public User getUser(String userId) {
return (User) redisTemplate.opsForValue().get("user:" + userId);
}
}
Amazon DynamoDB
- Управляемая NoSQL от AWS
- Автоматическое масштабирование
- Eventual consistency по умолчанию
- Global Secondary Indexes
Column-Family (Колоночные)
Apache Cassandra
- Децентрализованная архитектура (нет single point of failure)
- Eventual consistency
- CQL (Cassandra Query Language) похожий на SQL
- Tunable consistency - можно настроить уровень согласованности
// Cassandra с Spring Data
@Table("sensor_data")
public class SensorData {
@PrimaryKey
private SensorDataKey key;
@Column("temperature")
private Double temperature;
@Column("humidity")
private Double humidity;
}
@PrimaryKeyClass
public class SensorDataKey {
@PrimaryKeyColumn(name = "sensor_id", type = PrimaryKeyType.PARTITIONED)
private String sensorId;
@PrimaryKeyColumn(name = "timestamp", type = PrimaryKeyType.CLUSTERED)
private Date timestamp;
}
HBase
- Построена поверх Hadoop HDFS
- Строго консистентная
- Автоматическое шардирование
- Оптимизирована для чтения/записи больших объемов данных
Graph (Графовые)
Neo4j
- Native graph storage
- Cypher Query Language
- ACID транзакции
- Эффективный поиск связей
// Neo4j с Spring Data
@Node
public class Person {
@Id @GeneratedValue
private Long id;
private String name;
@Relationship(type = "FRIEND_OF")
private Set<Person> friends;
}
@Query("MATCH (p:Person)-[:FRIEND_OF*2]-(friendOfFriend) WHERE p.name = $name RETURN friendOfFriend")
List<Person> findFriendsOfFriends(@Param("name") String name);
Файловые хранилища
Локальные файловые системы
- ext4 - стандартная FS для Linux
- NTFS - Windows файловая система
- APFS - Apple File System
Сетевые файловые системы
- NFS - Network File System для Unix-систем
- SMB/CIFS - Windows сетевые папки
- GlusterFS - распределенная файловая система
Объектные хранилища
Amazon S3
- Практически неограниченная емкость
- REST API для доступа
- Различные классы хранения (Standard, IA, Glacier)
- Версионирование объектов
// S3 с AWS SDK
@Service
public class S3Service {
private final AmazonS3 s3Client;
public void uploadFile(String bucketName, String key, File file) {
s3Client.putObject(new PutObjectRequest(bucketName, key, file));
}
public S3Object downloadFile(String bucketName, String key) {
return s3Client.getObject(bucketName, key);
}
}
MinIO
- S3-совместимое объектное хранилище
- Можно развернуть on-premise
- Высокая производительность
- Kubernetes native
Специализированные хранилища
Time Series (Временные ряды)
InfluxDB
- Оптимизирована для метрик и событий с временными метками
- Встроенная агрегация и даунсэмплинг
- Retention policies для автоматического удаления старых данных
- Flux query language
// InfluxDB клиент
@Service
public class MetricsService {
private final InfluxDBClient client;
public void writeMetric(String measurement, Map<String, String> tags,
Map<String, Object> fields) {
Point point = Point.measurement(measurement)
.addTags(tags)
.addFields(fields)
.time(Instant.now(), WritePrecision.MS);
client.getWriteApiBlocking().writePoint(point);
}
}
Apache Druid
- Аналитическая OLAP база данных
- Колоночное хранение
- Реальное время + batch обработка
- Высокая производительность агрегации
Search Engines (Поисковые системы)
Elasticsearch
- Построена на Apache Lucene
- RESTful API
- Полнотекстовый поиск
- Агрегации и аналитика
// Elasticsearch с Spring Data
@Document(indexName = "products")
public class Product {
@Id
private String id;
@Field(type = FieldType.Text, analyzer = "standard")
private String title;
@Field(type = FieldType.Nested)
private List<Category> categories;
}
@Repository
public interface ProductSearchRepository extends ElasticsearchRepository<Product, String> {
@Query("{\"bool\": {\"must\": [{\"match\": {\"title\": \"?0\"}}]}}")
List<Product> findByTitleContaining(String title);
}
Apache Solr
- Также основана на Lucene
- Мощные возможности фасетирования
- Встроенная административная панель
- Хорошая производительность
Кэширование
In-Memory кэши
Redis (уже описан выше)
- Персистентность через RDB снапшоты или AOF лог
- Кластеризация для высокой доступности
- Множество структур данных
Memcached
- Простой key-value кэш
- Только строковые ключи и значения
- Распределенный кэш через consistent hashing
- Меньше функций, но выше производительность
// Memcached с Spring Boot
@Service
public class MemcachedService {
private final MemcachedClient client;
public void set(String key, Object value, int expiration) {
client.set(key, expiration, value);
}
public Object get(String key) {
return client.get(key);
}
}
Embedded кэши
Caffeine
- Высокопроизводительный Java кэш
- Размер-основанные и время-основанные eviction политики
- Асинхронная загрузка
- Статистика использования
// Caffeine кэш
@Configuration
public class CacheConfig {
@Bean
public Cache<String, User> userCache() {
return Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.recordStats()
.build();
}
}
Ehcache
- Многоуровневое кэширование (heap, off-heap, disk)
- Интеграция с Spring Cache
- Cluster поддержка
- JMX мониторинг
Выбор хранилища
Факторы выбора
-
Консистентность данных
- Строгая консистентность → RDBMS
- Eventual consistency → NoSQL
-
Масштабируемость
- Вертикальная → RDBMS
- Горизонтальная → NoSQL
-
Структура данных
- Структурированные → RDBMS
- Полуструктурированные → Document DB
- Неструктурированные → Object Storage
-
Паттерны доступа
- Сложные запросы → RDBMS/Graph DB
- Простые операции → Key-Value
- Полнотекстовый поиск → Search Engine
-
Производительность
- Низкая латентность → In-Memory (Redis)
- Высокая пропускная способность → Column-Family
Типичные архитектурные решения
Polyglot Persistence - использование разных типов хранилищ для разных задач:
- PostgreSQL для транзакционных данных
- Redis для кэширования
- Elasticsearch для поиска
- S3 для файлов
- InfluxDB для метрик
CQRS (Command Query Responsibility Segregation)
- Отдельные хранилища для записи и чтения
- Например: PostgreSQL для команд, Elasticsearch для запросов
Event Sourcing
- Хранение событий вместо текущего состояния
- Часто с Apache Kafka + Event Store