4669. Приложение для онлайн-тестирования должно записывать каждый ответ пользователя на вопрос. Транзакции частые, но не критичны к миллисекундной задержке. Как лучше управлять растущим объемом основной таблицы user_answers?
Anonymous Quiz
2%
Регулярно удалять старые данные (DELETE FROM ... WHERE created_at < ...).
69%
Использовать партиционирование таблицы по диапазону дат (например, по месяцам).
25%
Создать отдельную «историческую» БД и перемещать туда данные.
3%
Начать сжимать (COMPRESS) старые, редко используемые строки.
👩🏫Объяснение:
Партиционирование по диапазону — стандартное решение для управления жизненным циклом данных на основе времени. Оно позволяет:
* Быстро «отсекать» старые данные: удаление целой партиции (DROP PARTITION) — мгновенная операция, в отличие от тяжелого DELETE.
* Улучшать производительность запросов по актуальным данным: оптимизатор может читать только нужные партиции (партициональная прунинг).
* Упрощать архивацию: целую партицию можно выгрузить в архивный файл.
Удаление (A) нагружает БД и приводит к фрагментации. Отдельная БД (C) усложняет архитектуру. Сжатие строк (D) обычно встроено в СУБД и не решает проблему управления таблицей.
* Быстро «отсекать» старые данные: удаление целой партиции (DROP PARTITION) — мгновенная операция, в отличие от тяжелого DELETE.
* Улучшать производительность запросов по актуальным данным: оптимизатор может читать только нужные партиции (партициональная прунинг).
* Упрощать архивацию: целую партицию можно выгрузить в архивный файл.
Удаление (A) нагружает БД и приводит к фрагментации. Отдельная БД (C) усложняет архитектуру. Сжатие строк (D) обычно встроено в СУБД и не решает проблему управления таблицей.
4671. Для отчета необходимо быстро подсчитать общее количество уникальных активных пользователей за каждый день последнего месяца. Таблица user_sessions огромна. Какой индекс создаст максимально быстрый и компактный план запроса ?
Anonymous Quiz
3%
Индекс на (date).
34%
Индекс на (user_id, date).
43%
Индекс на (date, user_id).
20%
Отдельные индексы на date и на user_id.
👩🏫Объяснение:
Составной индекс (date, user_id) является покрывающим (covering) для этого конкретного запроса. В данном порядке:
1. date находится в начале, что позволяет эффективно отфильтровать данные по диапазону (WHERE).
2. user_id следует сразу за date, поэтому данные для каждой даты уже сгруппированы по user_id в индексе. Это позволяет СУБД выполнить «loose index scan» или, как минимум, читать только индекс (без обращения к таблице), быстро подсчитывая уникальных user_id для каждой даты. Индекс (user_id, date) (B) будет бесполезен для фильтрации по дате, а отдельные индексы (D) не обеспечат нужной группировки.
1. date находится в начале, что позволяет эффективно отфильтровать данные по диапазону (WHERE).
2. user_id следует сразу за date, поэтому данные для каждой даты уже сгруппированы по user_id в индексе. Это позволяет СУБД выполнить «loose index scan» или, как минимум, читать только индекс (без обращения к таблице), быстро подсчитывая уникальных user_id для каждой даты. Индекс (user_id, date) (B) будет бесполезен для фильтрации по дате, а отдельные индексы (D) не обеспечат нужной группировки.
4672. В унаследованной системе была таблица с полем category_id. Решили внедрить многоуровневую иерархию категорий. Как лучше расширить модель, чтобы эффективно отвечать на запросы «найти все товары в категории X и всех ее подкатегориях любого уровня»?
Anonymous Quiz
56%
Добавить рекурсивную связь: в таблицу categories добавить parent_id.
31%
Ввести дополнительную таблицу category_closure, которая хранит все пути в иерархии.
12%
Хранить путь (path) в виде строки с разделителями в самой категории, например, "1.5.12".
2%
Ограничиться фиксированной глубиной и добавить parent_id, grandparent_id и т.д.
👩🏫Объяснение:
Для работы с иерархическими данными произвольной глубины (деревьями) паттерн Closure Table (таблица замыканий) является одним из наиболее эффективных и универсальных. Таблица хранит все пары «предок-потомок», включая транзитивные связи. Это позволяет одним простым JOIN найти всех потомков категории, без рекурсивных запросов (которые могут быть медленными, вариант A) и без сложных строковых операций LIKE (вариант C). Вариант D не масштабируется и хрупок. Closure Table — это компромисс, позволяющий очень быстро читать иерархию ценой увеличения объема служебных данных и некоторой сложности при модификации дерева.
4670. В системе электронного документооборота у сущности «Договор» есть несколько взаимозависимых статусов: status_system, status_lega, status_financial. Как лучше смоделировать это в БД, чтобы легко добавлять новые аспекты статусов?
Anonymous Quiz
4%
Хранить все в одном поле status типа VARCHAR со значением, например, "active_approved_paid".
53%
Создать отдельную таблицу со связью «многие-ко-многим» к справочнику возможных статусов.
28%
Использовать несколько BOOLEAN полей или ENUM/маленьких INT для каждого независимого аспекта.
16%
Хранить статус как JSONB, где ключами будут аспекты: {"system": "active", "legal": "approved"}.
👩🏫Объяснение:
Независимые логические атрибуты (аспекты статуса) лучше хранить в отдельных колонках. Это дает максимальную простоту и эффективность: фильтрация (WHERE status_legal = 'approved') и индексация по каждому аспекту тривиальны, целостность данных легко контролируется. Хранение в одной строке (A) делает запросы неэффективными. Связь многие-ко-многим (B) избыточна для простых атрибутов. JSONB (D) гибок, но сложнее в валидации и обычно медленнее для частой фильтрации. Подход C следует принципу «одна колонка — один атомарный факт».
После обновления приложения в продекшене появилась ошибка, из-за которой в таблицу записывались платежи с нулевой суммой. Ошибку исправили. Как безопасно удалить ~10 000 некорректных записей из таблицы с 50 миллионами строк, не нарушая работу операций?
Anonymous Quiz
28%
Выполнить DELETE FROM payments WHERE amount = 0 AND created_at > ...
34%
Скопировать корректные данные во временную таблицу, сделать rename, пересоздать индексы в фоне.
32%
Пометить записи как удаленные, а физически удалить ночью.
6%
Использовать логическое удаление и фильтровать на уровне приложения.
👩🏫Объяснение:
На самом деле, для удаления 10к строк из 50 млн можно смело использовать осторожный DELETE с ограничением по времени (A), выполнив его в периоды низкой нагрузки, возможно, батчами (LIMIT 1000), чтобы не блокировать таблицу надолго. Однако, если бы речь шла об удалении миллионов строк, то вариант B (создание новой таблицы) был бы предпочтительнее, так как DELETE при огромном объеме — долгая, тяжелая операция, генерирующая много WAL-логов и потенциально блокирующая. Варианты C и D не очищают данные, а лишь маскируют проблему. В реалистичном кейсе с 10к записей правильнее A, но понимание альтернативы B для больших объемов — ключевое.
4674. Вы проектируете аналитическую систему для ритейла, где ключевые отчеты строятся по итогам дня, недели, месяца. Какой наиболее правильный и распространенный архитектурный подход?
Anonymous Quiz
16%
Реплицировать OLTP-базу на отдельный сервер и выполнять запросы там.
16%
Настроить индексы в операционной БД так, чтобы они покрывали все аналитические запросы.
56%
Реализовать механизм ETL, который будет переносить и преобразовывать данные в отдельное хранилище.
11%
Выполнять агрегации в реальном времени, используя кэширующий слой с предвычисленными значениями.
👩🏫Объяснение:
Это классический паттерн разделения на OLTP (операционная обработка транзакций) и OLAP (аналитическая обработка). Прямые аналитические запросы к OLTP-системе (A, B) блокируют операционные транзакции и убивают производительность. ETL-процесс (C) переносит данные в специально спроектированное хранилище (DWH), где данные денормализованы, структурированы по темам (схема «звезда»/«снежинка») и используют эффективные для чтения форматы хранения (колоночные СУБД). Кэш (D) хорош для простых метрик, но не для гибкой, многомерной аналитики.
4675. В таблице documents есть поле version (целое число). Как должен выглядеть SQL-запрос для обновления документа, который гарантированно предотвратит «потерянное обновление» (lost update), если два пользователя редактируют один документ?
Anonymous Quiz
22%
UPDATE documents SET content='новый текст', version = version + 1 WHERE id = 123;
48%
UPDATE documents SET content='новый текст', version = version + 1 WHERE id = 123 AND version
21%
SELECT ... FOR UPDATE; затем UPDATE documents ... WHERE id=123;
10%
UPDATE documents SET content='новый текст' WHERE id = 123;
👩🏫Объяснение:
Суть оптимистичной блокировки — позволить всем читать данные, но проверять, изменились ли они с момента чтения, в момент записи. Ключевой элемент — проверка версии в условии WHERE. Если другой процесс уже успел обновить документ, то его version изменился, и запрос текущего процесса (B) не затронет ни одной строки (affected rows = 0). Приложение, увидев 0 измененных строк, поймет, что произошел конфликт и должно предложить пользователю разрешить его. Вариант A увеличит версию всегда, потеряв первое обновление. Вариант C — это пессимистическая блокировка. Вариант D не использует версионирование вовсе.