Привет, Хабр!

Правильно настроенные индексы могут значительно ускорить доступ к данным и улучшить производительность запросов. В 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":

Комментарии (1)


  1. dyadyaSerezha
    13.08.2024 20:27

    А как насчёт составных индекса и поиску по неполному значению индекса?