Привет, Хабр!
Правильно настроенные индексы могут значительно ускорить доступ к данным и улучшить производительность запросов. В NoSQL разнообразие решений настолько велико, что порой можно заблудиться.
В статье разберемся, какие виды индексов существуют, какие задачи они помогают решать и как выбирать подходящий индекс.
Типы индексов в NoSQL
Первичные индексы:
Первичные индексы дают уникальную идентификацию каждой записи в базе данных и организуют данные в соответствии с определенным ключом. В NoSQL-базах данных первичные индексы играют основную роль в распределении данных между узлами.
Первичные индексы определяются автоматически при создании записи и не требуют доп. усилий для их поддержки. Например, в MongoDB _id
является первичным индексом по умолчанию, а в Cassandra используется первичный ключ, определяемый пользователем.
В MongoDB поле _id
автоматически индексируется:
// поиск документа по первичному индексу _id
db.collection.find({ _id: ObjectId("64bd1c9f4a4e19d8f84f06f9") })
В Cassandra первичный ключ определяется при создании таблицы, который автоматически индексируется:
-- cоздание таблицы с первичным ключом
CREATE TABLE users (
user_id UUID PRIMARY KEY,
name TEXT,
email TEXT
);
-- поиск по первичному индексу
SELECT * FROM users WHERE user_id = '550e8400-e29b-41d4-a716-446655440000';
Первичные индексы дают быстрый доступ к записям, т.к данные отсортированы по ключу. Но при этом подходят только для операций, связанных с ключом.
Вторичные индексы
Вторичные индексы дают возможность быстрого поиска по полям, отличным от первичного ключа. Они помогают выполнять сложные запросы, которые не могут быть реализованы с использованием только первичных индексов.
MongoDB:
Создание вторичного индекса для оптимизации поиска по полю name
:
// создание вторичного индекса на поле name
db.collection.createIndex({ name: 1 });
// поиск по вторичному индексу
db.collection.find({ name: "Artem" });
Cassandra:
Создание вторичного индекса для оптимизации поиска по полю email
:
-- создание вторичного индекса на поле email
CREATE INDEX ON users (email);
-- поиск по вторичному индексу
SELECT * FROM users WHERE email = 'artem@example.com';
Рассмотрим пример использования первичных и вторичных ключей, где идет работа с коллекцией пользователей в MongoDB и нужно оптимизировать поиск по нескольким полям:
// создание коллекции пользователей с автоматическим первичным индексом _id
db.users.insertMany([
{ name: "Ivan", email: "ivan@example.com", age: 28 },
{ name: "Artem", email: "artem@example.com", age: 32 },
{ name: "Kolya", email: "kolya@example.com", age: 25 }
]);
// создание вторичных индексов
db.users.createIndex({ name: 1 });
db.users.createIndex({ email: 1 });
// поиск пользователя по имени
db.users.find({ name: "Alice" });
// поиск пользователя по email
db.users.find({ email: "bob@example.com" });
Создали индексы на полях name
и email
, таким образом можно быстро находить пользователей по этим параметрам.
Индексы на основе диапазонов
Индексы на основе диапазонов позволяют обрабатывать запросы, где необходимо работать с интервалами значений: временные метки, числовые диапазоны или алфавитные строки.
Elasticsearch активно использует индексы на основе диапазонов для обработки временных данных и сложных запросов. Пример:
PUT /logs
{
"mappings": {
"properties": {
"timestamp": { "type": "date" },
"message": { "type": "text" }
}
}
}
// поиск по диапазону дат
GET /logs/_search
{
"query": {
"range": {
"timestamp": {
"gte": "2024-08-01",
"lte": "2024-08-31"
}
}
}
}
Cassandra поддерживает индексацию на основе диапазонов для числовых и временных данных:
-- создание таблицы с временными метками
CREATE TABLE events (
event_id UUID PRIMARY KEY,
timestamp TIMESTAMP,
description TEXT
);
-- поиск событий за определенный период
SELECT * FROM events WHERE timestamp >= '2024-08-01' AND timestamp <= '2024-08-31';
Представим задачу, в которой идет работа с логами событий и нужно быстро находить события за определенные временные промежутки:
// создание коллекции логов событий
db.logs.insertMany([
{ timestamp: new Date("2024-08-01T10:00:00Z"), message: "User login" },
{ timestamp: new Date("2024-08-02T12:00:00Z"), message: "Data backup" },
{ timestamp: new Date("2024-08-03T14:00:00Z"), message: "System update" }
]);
// создание индекса на поле timestamp
db.logs.createIndex({ timestamp: 1 });
// поиск событий за август 2024
db.logs.find({
timestamp: {
$gte: new Date("2024-08-01"),
$lte: new Date("2024-08-31")
}
});
Геопространственные индексы
Геопространственные индексы предназначены для работы с географическими данными, такими как координаты точек, линии и полигоны. Они позволяют выполнять географические запросы, например: "поиск ближайших" и "вхождение в область".
В MongoDB используется 2dsphere
индекс для поддержки сложных географических запросов:
// создание коллекции с географическими данными
db.places.insertMany([
{ name: "Central Park", location: { type: "Point", coordinates: [-73.9654, 40.7829] } },
{ name: "Golden Gate Park", location: { type: "Point", coordinates: [-122.4862, 37.7694] } }
]);
// создание геопространственного индекса
db.places.createIndex({ location: "2dsphere" });
// поиск мест в радиусе 10 км от заданной точки
db.places.find({
location: {
$near: {
$geometry: { type: "Point", coordinates: [-73.935242, 40.730610] },
$maxDistance: 10000
}
}
});
Elasticsearch поддерживает геопространственные индексы для быстрого поиска по координатам:
PUT /places
{
"mappings": {
"properties": {
"location": { "type": "geo_point" }
}
}
}
// Поиск ближайших мест
GET /places/_search
{
"query": {
"geo_distance": {
"distance": "10km",
"location": {
"lat": 40.730610,
"lon": -73.935242
}
}
}
}
Допустим, мы разрабатываем приложение для поиска ближайших кафе, используя MongoDB:
// создание коллекции с данными о кафе
db.cafes.insertMany([
{ name: "Cafe 1", location: { type: "Point", coordinates: [-73.935242, 40.730610] } },
{ name: "Cafe 2", location: { type: "Point", coordinates: [-73.985428, 40.748817] } }
]);
// создание геопространственного индекса
db.cafes.createIndex({ location: "2dsphere" });
// поиск кафе в радиусе 5 км от заданной точки
db.cafes.find({
location: {
$near: {
$geometry: { type: "Point", coordinates: [-73.961452, 40.768063] },
$maxDistance: 5000
}
}
});
Полнотекстовые индексы
Полнотекстовые индексы позволяют обрабатывать запросы, связанные с текстовым содержимым.
MongoDB поддерживает полнотекстовые индексы с использованием text
индекса для текстового поиска:
// создание коллекции с текстовыми данными
db.articles.insertMany([
{ title: "NoSQL: A Comprehensive Guide", content: "This article explains the key concepts of NoSQL databases." },
{ title: "Understanding MongoDB Indexes", content: "Indexes in MongoDB are crucial for performance optimization." }
]);
// создание полнотекстового индекса
db.articles.createIndex({ content: "text" });
// поиск по ключевым словам
db.articles.find({ $text: { $search: "NoSQL databases" } });
А вот Elasticsearch изначально проектировался для полнотекстового поиска:
PUT /articles
{
"mappings": {
"properties": {
"content": { "type": "text" }
}
}
}
// Поиск по ключевым словам
GET /articles/_search
{
"query": {
"match": {
"content": "NoSQL databases"
}
}
}
Представим, что хотим реализовать поиск по статьям с MongoDB:
// MongoDB: Создание коллекции статей
db.blog.insertMany([
{ title: "The Rise of NoSQL", content: "NoSQL databases have become increasingly popular..." },
{ title: "Indexing in MongoDB", content: "Indexes are essential for query performance in MongoDB..." }
]);
// создание полнотекстового индекса на поле content
db.blog.createIndex({ content: "text" });
// поиск статей, содержащих слово "NoSQL"
db.blog.find({ $text: { $search: "NoSQL" } });
Индексация — это мощный инструмент, который может улучшить производительность и гибкость проектов.
Если есть еще темы или вопросы, которые стоит рассмотреть, дайте знать!
В заключение напоминаю про ближайшие открытые уроки, которые пройдут в рамках онлайн-курса "NoSQL":
15 августа: «Практические кейсы использования ClickHouse». Запись по ссылке
22 августа: «Интеграция ClickHouse с системами ETL». Запись по ссылке
dyadyaSerezha
А как насчёт составных индекса и поиску по неполному значению индекса?