← К содержанию навыка

Поиск данных и индексы

20 минут
Раздел 4 из 8

4. Поиск данных и индексы

4.1. Стратегии чтения

Query

Самая важная операция в DynamoDB. 90% ваших запросов должны быть Query.

  • Аналогия: Вы знаете, что книги Толстого стоят на полке "Т" (Partition Key). Вы подходите к этой конкретной полке и берете все книги, написанные после 1860 года (Sort Key). Вы не бегаете по всей библиотеке, вы работаете в рамках одной секции (партиции).
  • Что делает: Достает группу элементов, у которых одинаковый Partition Key.
  • Суперсила: Вы можете фильтровать по Sort Key (диапазоны).
  • Стоимость: Платите только за тот объем книг, который сняли с полки. Это очень эффективно.
from boto3.dynamodb.conditions import Key

# Дай мне все заказы Алисы (Полка), начиная с 2024 года (Фильтр по порядку)
response = table.query(
    KeyConditionExpression=Key('PK').eq('USER#alice') & Key('SK').begins_with('ORDER#2024')
)
items = response['Items']

Scan

Операция, которую нужно избегать в повседневной работе.

  • Аналогия: Вам нужно найти книгу, в которой на 5-й странице есть слово "синхрофазотрон". Вы не знаете ни автора, ни названия. Вам приходится брать каждую книгу в библиотеке, открывать её и читать. Библиотека парализована, библиотекари в мыле, вы платите огромные деньги за аренду зала.
  • Что делает: Читает ВСЮ таблицу. Строка за строкой. С первой по последнюю.
  • Фильтрация: Вы можете передать FilterExpression, но DynamoDB применит фильтр ПОСЛЕ того, как прочитает данные с диска и снимет с вас деньги.
  • Стоимость: Огромная. Вы платите за прочтение всей таблицы, даже если найдете 0 книг.
  • Когда использовать: Только для миграций или редких админских скриптов. Никогда на горячем пути пользователя.

4.2. Вторичные индексы

Ограничения Primary Key

Итак, вы построили идеальное хранилище (таблицу Users), где все лежит по user_id (PK). "Дай мне юзера user_123" - мгновенный ответ.

Но тут приходит менеджер и просит:

  • "А найди мне пользователя по email?"
  • "А покажи мне всех пользователей из city='Pavlodar'?"

В SQL вы бы сказали "без проблем" и добавили CREATE INDEX ix_users_email ON users (email).

В DynamoDB ваш единственный способ запроса - это Query по ключу. Если у вас нет ключа для email, вы не можете сделать по нему запрос (кроме Scan, который мы не рассматриваем, так как это медленно и дорого).

Вторичный индекс (Secondary Index) - это копия вашей таблицы (или ее части), но с другим Primary Key. Это ваш "альтернативный паттерн доступа". Это как карточный каталог в библиотеке.

Global Secondary Index (GSI)

GSI - это индекс, у которого Partition Key (а опционально и Sort Key) отличается от основного ключа таблицы.

Пример: Таблица Users.

  • Primary Key: user_id (PK)
  • Бизнес-задача: "Найти пользователя по email".
  • Решение: Мы создаем GSI (назовем его EmailIndex) с ключом:
    • GSI PK: email

Архитектура и ограничения

  1. Это копия: GSI - это, по сути, новая таблица, которой DynamoDB управляет за вас. Когда вы пишете в основную таблицу, DynamoDB асинхронно реплицирует эти данные в GSI.
  2. Асинхронность: Репликация занимает время (обычно < 1 секунды). Это значит, что GSI всегда Eventually Consistent. Вы не можете делать Strongly Consistent Read из GSI. (Про виды консистености будет рассказано ниже.)
  3. Проекция (Projections): GSI не обязан хранить весь элемент. Вы можете выбрать:
    • KEYS_ONLY: GSI хранит только ключи (основной таблицы и GSI). Дешево по хранению, но чтобы получить user_name, вам придется сделать 2-й запрос к основной таблице.
    • INCLUDE: Хранить ключи + некоторые атрибуты (указанные вами).
    • ALL: Хранить полную копию элемента. Дорого по хранению, но быстро по чтению (не нужен 2-й запрос).
  4. Свой RCU/WCU: У GSI есть свой собственный бюджет RCU и WCU. Вы платите за хранение GSI и за его RCU/WCU отдельно.

Стоимость и производительность

  • Запись: Каждая PutItem в основную таблицу теперь потребляет WCU основной таблицы И WCU индекса (если атрибуты GSI изменились). GSI удваивает (или утраивает, ...) стоимость записи.
  • Чтение: Query(IndexName='EmailIndex', KeyConditionExpression=...) потребляет RCU индекса, а не таблицы.

Редкий индекс (Sparse Index)

Это мощный паттерн. GSI копирует элемент только если в элементе присутствует атрибут Partition Key этого GSI.

  • Пример: У вас есть Orders. Вы хотите GSI для "открытых" заказов.
  • Решение: Вы создаете GSI с Partition Key open_order_status. Вы записываете этот атрибут open_order_status = 'OPEN' только если status = 'OPEN'.
  • Теперь ваш GSI хранит только 1% данных (открытые заказы). Он маленький, дешевый и быстрый.

Но вы должны помнить, что Primary Key (Partition Key + Sort Key) должен быть уникальным и в индексе тоже. Поэтому, когда у вас Partition Key одинаковый, вы должны так подобрать Sort Key, чтобы их комбинация была уникальной в каждом элементе.

Local Secondary Index (LSI)

LSI - это более редкий и специфичный зверь.

Сценарий: Таблица GameScores.

  • Primary Key: user_id (PK), game_id (SK).
  • Это позволяет мне делать Query(user_id='alice') и получать все игры, в которые играла Алиса, отсортированные по game_id.
  • Проблема: А что, если я хочу получить все игры Алисы, но отсортированные по score (счету), чтобы построить ее личный топ?

Локальный вторичный индекс (LSI) - это индекс, у которого Partition Key тот же самый, а Sort Key - другой.

Таблица GameScores:

  • Primary Key: user_id (PK), game_id (SK)
  • LSI (назовем ScoreIndex):
    • LSI PK: user_id (тот же!)
    • LSI SK: score (другой!)

Свойства LSI

  1. Общая партиция: Данные LSI хранятся на той же партиции, что и основные данные.
  2. Общий RCU/WCU: LSI не имеет своего бюджета. Он ест RCU/WCU из бюджета основной таблицы.
  3. Создание: LSI нельзя добавить к существующей таблице. Его можно создать только в момент создания таблицы. (GSI можно добавлять и удалять на лету).
  4. Лимит 10 ГБ: Суммарный размер всех элементов для одного PK (включая все его LSI) не может превышать 10 ГБ.

Когда LSI предпочтительнее GSI?

  • Строгая консистентность: Так как LSI живет на той же партиции, вы можете делать Strongly Consistent Read из LSI.
  • Транзакции: LSI можно использовать в TransactWriteItems. (Про транзакции будет рассказано ниже.)
  • Экономия: Если у вас и так read-heavy таблица, LSI "бесплатен" (он просто делит RCU/WCU).

Вывод:

  • Нужен другой Partition Key? -> GSI.
  • Нужна другая сортировка для того же Partition Key? -> LSI.

Query, Scan и вторичные индексы

Вопросов: 7

4.3. Практика

Реализация поиска данных и вторичных индексов для конкурента Twitter

Вы продолжаете работу как CTO стартапа-конкурента Twitter. Вместе с Назаром разберите стратегии чтения данных в DynamoDB: Query vs Scan, проектирование Global Secondary Index для поиска твитов по хэштегам, Local Secondary Index для сортировки ленты, и оптимизация паттернов доступа для высоконагруженного приложения.

Назар - ваш персональный ИИ-наставник. Он поможет закрепить материал через практику и ответит на ваши вопросы.

💡 Все обсуждения с ИИ могут быть прочитаны администратором для улучшения качества обучения.