SQL #
1. Что такое транзакции? #
Транзакция - это набор логически связанных запросов, выполняемых атомарно (либо выполняются все - commit транзакции, либо откат всего - rollback)
2. Расскажите про ACID #
ACID - это набор свойств, гарантирующих надёжность транзакции:
- Atomicity (атомарность/неделимость операций) - все операции внутри транзакции либо выполняются полностью, либо не выполняются совсем
- Consistency (согласованность данных) - после транзакции данные соблюдают все правила целостности (правила целостности определяются бизнес-логикой)
- Isolation (изоляция) - во время выполнения транзакции параллельные транзакции не должны оказывать влияние на ее результат
- Durability (долговечность/живучесть) - гарантирует, что закоммиченные изменения не могут быть потеряны ни при каких сбоях (поломка сервера, отключение электроэнергии и т.д.)
3. Проблемы параллельных транзакций #
- Грязное чтение (dirty read) - возможность чтения одной транзакцией незакоммиченных данных другой транзакции. Транзакция A обновляет значение в базе данных, но еще не закоммитила их. Транзакция B считывает это значение, не зная, что изменения могут быть отменены. Если транзакция A отменяется, значение, прочитанное транзакцией B, было “грязным”, так как оно не отражает окончательное состояние базы данных. Решается уровнем изоляции read committed и выше
- Потерянное обновление (lost update) - при одновременном изменении одних и тех же данных разными транзакциями в силу вступают только изменения в последней транзакции. Решается repeatable read и выше
- Неповторяющееся чтение (non-repeatable read) - возникает при изменении самих данных. Т.е. когда в рамках одной транзакции два чтения одних и тех же данных с промежутком времени дают два разных результата. Решается repeatable read и выше
- Фантомное чтение (phantom read) - возникает при изменении объёма данных / строк. Т.е. во время одной транзакции другая транзакция добавила несколько строк в таблицу и закоммитила изменения. Во время повторного запроса данных первая транзакция получит больше данных (исходные + “фантомные” записи из другой транзакции)
В Postgres Repeatable Read реализован через MVCC настолько строго, что фантомное чтение там фактически не возникает (хотя стандарт SQL это допускает)
MVCC (Multiversion Concurrency Control) - это метод управления конкурентным доступом к данным в БД, который позволяет нескольким транзакциям работать с данными одновременно без конфликтов. MVCC поддерживает высокую производительность и изоляцию транзакций, минимизируя блокировки и улучшая параллелизм.
- Многоверсионность: MVCC использует механизм многоверсионности для управления изменениями данных. Вместо того чтобы изменять данные в месте их хранения, MVCC создает новые версии данных. Каждая транзакция видит данные в том состоянии, в котором они были на момент начала транзакции, а не в текущем состоянии. При записи данных создается новая версия строки. Новая версия включает информацию о том, какая транзакция создала ее, и становится видимой для транзакций, которые начинаются после ее создания. Благодаря тому, что в версии указано, какая транзакция создала её и когда данные были созданы или удалены, можно определить, какая версия доступна для конкретной транзакции. Время от времени СУБД очищает старые версии данных, которые больше не нужны (т. е. которые уже не используются ни одной транзакцией). Это называется сбором мусора и помогает поддерживать эффективность системы.
- Изоляция транзакций: Транзакции видят только те данные, которые были зафиксированы до их начала. Это предотвращает грязное чтение, неповторяющееся чтение и фантомное чтение.
- Отсутствие блокировок для чтения: Чтение данных не блокирует запись, а запись не блокирует чтение. Это повышает производительность системы, поскольку транзакции могут работать с данными параллельно.
- Управление конфликтами: Конфликты между транзакциями, например, две транзакции, пытающиеся изменить одну и ту же строку, решаются при коммите транзакций. Если одна из транзакций не может быть закоммичена из‑за конфликта, её можно откатить.
4. Уровни изолированности (read uncommitted, read committed, repeatable read, serializable) #
Статья на хабре про уровни изоляции
От менее строгого к самому строгому:
- Read uncommited (незащищённое чтение) - транзакции могут читать данные, которые еще не зафиксированы другими транзакциями (использовать, когда все транзакции на чтение)
- Read commited (защищённое чтение) - транзакции могут читать только те данные, которые уже зафиксированы другими данными (решает потерянное обновление и грязное чтение)
- Repeatable read (повторяющееся чтение) - в рамках одной транзакции многократный запрос данных всегда возвращает одинаковый набор данных, независимо от изменений, сделанных другими транзакциями (решает потерянное обновление, грязное чтение и неповторяющееся чтение)
- Serializable - самый строгий уровень изоляции. Транзакции выполняются так, как будто происходят последовательно одна за другой (решает всё, но бьёт по производительности)
Какой уровень изолированности стоит по умолчанию в MySQL? В MySQL - Repeatable read, в PostgreSQL - Read commited
Зависимость скорости работы БД и уровня изоляции
| Уровень изоляции | Описание | Влияние на производительность |
|---|---|---|
| Read Uncommitted | Транзакции могут читать данные, которые ещё не зафиксированы | Самый быстрый, но данные могут быть неконсистентными |
| Read Committed | Транзакции читают только зафиксированные данные | Средняя производительность, баланс между скоростью и консистентностью |
| Repeatable Read | Гарантирует, что данные, прочитанные транзакцией, не изменятся, пока транзакция не завершена | Снижает производительность из-за блокировок, но обеспечивает более высокую консистентность |
| Serializable | Самый высокий уровень изоляции, предотвращает все виды аномалий | Самый медленный, так как использует полные блокировки |
5. Виды JOIN. Как работает каждый JOIN? #
Оператор, позволяющий связать данные из двух таблиц. Бывают:
- (INNER) JOIN - выдаст общее для левой и правой таблицы (может быть заменён WHERE)
- LEFT (OUTER) JOIN - выдаст все значения с левой таблицы и все подходящие с правой
- RIGHT (OUTER) JOIN - выдаст все значения с правой таблицы и все подходящие с левой
- FULL (OUTER) JOIN - выдаст все записи, которые присутствуют в таблицах
- CROSS JOIN - создаст декартово произведение двух таблиц (если в первой таблице 5 строк, а во второй 3, то вернет 5 x 3 = 15)
- NATURAL JOIN - объединяет таблицы по столбцам с одинаковыми именами, если значения в этих столбцах совпадают
- SELF JOIN - соединяет таблицу с самой собой
6. Индексы в SQL. Что это? #
Индексы в БД - это инструмент оптимизации/ускорения запросов путём создания упорядоченного набора указателей на строки таблицы. Ускорение происходит за счёт того, что структура индекса оптимизирована под поиск (например, b-tree). При этом, чем больше дубликатов в столбце - тем хуже работает индекс
В индексе ссылка на данные или в самом индексе кешируем данные? В индексе хранятся ссылки на реальные данные, которые находятся в таблице базы данных. Эти ссылки могут быть представлены в виде:
- указателей на строки в таблице, где хранятся данные
- идентификаторов записей, которые позволяют найти данные в таблице
В Postgres в индексе хранятся ссылки на реальные данные, которые находятся в таблице базы данных. Например, в B-tree:
- промежуточные узлы дерева содержат только ключи для направления поиска
- в листьях дерева хранятся не сами данные, а ссылки на данные в таблице (или другие индексы)
Однако, можно создать покрывающий индекс - индекс, который включает не только ключи, но и значение колонки(ок). Такие индексы позволяют извлекать данные не обращаясь к таблице. Пример:
CREATE INDEX idx_price ON products(price) INCLUDE (name);
-- Теперь индекс хранит значения price + колонку name, что позволяет выполнить запрос
SELECT price, name FROM products WHERE price > 50;
-- не обращаясь к таблице
В MySQL (InnoDB) поведение индексов отличается в зависимости от их типа:
- PRIMARY KEY (кластерный индекс) - индекс содержит сами данные
- Обычный (SECONDARY INDEX) индекс хранит ссылки (ключи PRIMARY KEY) на данные
Какие индексы по умолчанию в разных СУБД? По-умолчанию в большинстве CУБД стоит B-tree
Сравнение сложности поиска
| Тип поиска | Структура | Сложность | Когда применять? |
|---|---|---|---|
| Без индекса | Полный перебор | O(N) | Маленькие таблицы |
| B-Tree индекс | Сбалансированное дерево | O(log N) | Частый поиск по диапазонам |
| Hash-индекс | Хеш-таблица | O(1) | Быстрый поиск по = |
Как добавить новый индекс в большую, активно используемую таблицу в продакшене, не блокируя доступ и не приостанавливая работу приложения, и какие стратегии можно применить для минимизации влияния на систему?
- PostgreSQL -
CREATE INDEX CONCURRENTLY/ pg_repack / gh-ost - MySQL -
ALTER TABLE … ALGORITHM=INPLACE, LOCK=NONE/ pt-online-schema-change
Как определить, какой индекс будет использован в запросе с фильтрацией по столбцу с низкой селективностью (например, пол), и как селективность влияет на эффективность индексов?
- EXPLAIN ANALYZE покажет, будет ли
Index ScanилиSeq Scan - pg_stats.n_distinct даёт оценку числа уникальных значений
7. Почему когда много индексов, это плохо? #
- Усложнение работы планировщика - СУБД может не выбрать лучший индекс для запроса, если их слишком много. Некоторые индексы могут быть избыточными, что не только не ускоряет запросы, но и увеличивает нагрузку на систему
- Утилизация места на жёстком диске - индексы занимают дополнительное место на диске пропорционально объёму данных и кол-ву выбранных столбцов
- Увеличение времени записи - при вставке, обновлении или удалении данных индексы нужно обновлять, что замедляет операции записи
- Усложнение управления - управление большим количеством индексов становится сложным, включая их отслеживание и обслуживании
8. Плюсы и минусы индексов #
Плюсы:
- Улучшают производительность для select и сортировки по определенным полям
Минусы:
- Ухудшается производительность, когда нужно вставлять, обновлять или удалять данные
- Требуется дополнительное место и чем больше/длиннее ключ - тем больше размер индекса
9. Какие виды индексов знаешь? #
| Тип индекса | Описание | Использование |
|---|---|---|
| B-дерево (B-tree) | Стандартный индекс, используемый в большинстве СУБД. Данные хранятся в сбалансированном дереве | Для поиска, сортировки, диапазонных запросов |
| Хеш-индекс (Hash Index) | Использует хеш-функцию для поиска точных совпадений | Для быстрого поиска по уникальным значениям (например, ID) |
| Индекс с несколькими столбцами (Composite Index) | Индекс, включающий несколько столбцов. Ускоряет запросы, использующие несколько фильтров | Когда запросы фильтруют по нескольким столбцам одновременно |
| Индекс с уникальными значениями (Unique Index) | Индекс, обеспечивающий уникальность значений в столбце | Для обеспечения уникальности значений (например, email) |
| Индекс полного текста (Full-Text Index) | Индекс для быстрого поиска по тексту, включая ключевые слова и фразы | Поиск по длинным текстовым полям, например, в статьях или документах |
| Индекс обратного списка (Bitmap Index) | Индекс с битовыми картами для колонок с малым числом уникальных значений | Для столбцов с ограниченным числом уникальных значений, например, пол |
| Инкрементный (Clustered Index) | Индекс, при котором данные таблицы физически упорядочены в соответствии с индексом | Когда необходимо хранить данные в порядке индекса, например, по ID |
| Индекс по выражению (Functional Index) | Индекс, основанный на вычисленных значениях или функциях над столбцами | Для запросов, использующих функции или выражения в фильтрах |
| Индекс на основе дерева (GiST — Generalized Search Tree) | Общий тип дерева, используемый для индексации сложных типов данных | Для индексации географических данных или других сложных типов |
| Индекс на основе префикса (Prefix Index) | Индекс, созданный на части строки, например, на первых символах | Для текстовых полей, где важно индексировать только часть строки (например, первые несколько символов) |
| GIN (Generalized Inverted Index) | Инвертированная структура: GIN создает карту, связывающую значения (ключи) с идентификаторами строк (TID), в которых эти значения встречаются. B-tree под капотом | Идеален дляJSONB (операторы @>, ?), ARRAY (операторы <@, @>, &&) |
| Покрывающий индекс (Covering Index) | Схож с композитным, но INCLUDE-колонки не участвуют в поиске/фильтрацииCREATE INDEX idx_users_coverON users(email)INCLUDE(name, age) | Для определённого набора часто запрашиваемых полей, не участвующих в фильтрации |
10. Как правильно выбрать индекс? #
| Тип индекса | Когда использовать | Где доступен |
|---|---|---|
| B-Tree | Универсальный, для =, <, >, ORDER BY | Везде |
| Hash | Только = (поиск по точному значению) | PostgreSQL, MySQL MEMORY |
| Bitmap | Колонка с малым числом уникальных значений (status) | Oracle, Postgres ext |
| Full-text/GIN/GiST | Поиск по тексту, JSON, массивам, гео-данным | PostgreSQL, MySQL |
| Clustered | Primary key, диапазоны | SQL Server, MySQL InnoDB |
| Non-clustered | Дополнительные индексы | SQL Server, MySQL |
| Partial | Индекс только по условию WHERE | PostgreSQL, SQL Server |
11. Нормализация и денормализация. Перечислите формы #
Нормализация - это процесс преобразования отношений базы данных к виду без избыточной информации. Избыточность - это ситуация, когда одни и те же данные хранятся в базе в нескольких местах. Нормализация проходит через несколько форм:
- 1NF (1-я нормальная форма). Одна ячейка - одно значение.
- 2NF (2-я нормальная форма). Все не ключевые колонки зависят от всего ключа.
- 3NF (3-я нормальная форма). Все не ключевые колонки не зависят друг от друга.
Денормализация - процесс обратный нормализации. Обычно это необходимо для повышения производительности и скорости извлечения данных, за счет увеличения избыточности данных (влечёт уменьшение сложности запросов)
12. Что такое внешний ключ? #
Внешний ключ - это столбец-ссылка на первичный ключ (или уникальную колонку) другой таблицы. Может быть null.
13. Чем WHERE отличается от HAVING? Можем ли мы их использовать в одном запросе? #
WHERE отвечает за фильтр всех записей в запросе (до применения агрегатных функций), HAVING применяет фильтры к группам, сформированным GROUP BY
14. Чем отличается MySQL от PostgreSQL? #
MySQL - проще, быстрее в базовых операциях, хорош для приложений с высокой нагрузкой на чтение PostgreSQL - мощнее, с расширенными функциями и более строгим соблюдением стандартов, лучше подходит для сложных приложений
15. Оптимизация запросов. Какие существуют инструменты? #
- EXPLAIN - показывает теоретический план выполнения запроса, что позволяет увидеть, как СУБД планирует извлекать данные
- EXPLAIN ANALYZE - выполняет запрос и показывает реальный план выполнения, включая затраченные ресурсы (время, память)
- Indexes - индексы ускоряют доступ к данным, особенно при использовании в условиях WHERE
- pg_stat_statements (для PostgreSQL) - модуль для сбора статистики выполнения запросов
SQL-запрос у тебя в норме, но выполнение всё равно долгое. Твои действия?
- Оптимизация плана выполнения - воспользоваться EXPLAIN и EXPLAIN ANALYZE для анализа плана выполнения запроса и поиска проблемных мест
- Использование индексов - проверить, правильно ли используются существующие индексы. При необходимости создать или изменить текущие
- Рефакторинг запроса - изменить структуру запроса, если есть более эффективный способ получить те же данные. Например, объединение нескольких подзапросов или использование более простых JOIN
- Ресурсные ограничения - проверить отсутствие ограничений по железу (нехватка CPU, памяти, диска)
16. EXPLAIN vs EXPLAIN ANALYZE #
EXPLAIN показывает план выполнения запроса до его фактического выполнения. Он выводит, как СУБД собирается получить данные. Это включает:
- какой тип сканирования будет применяться (Seq Scan, Index Scan)
- сортировки и объединения (Nested Loop, Hash Join)
- предполагаемое количество строк и стоимость выполнения на каждом этапе
Explain analyze. Как устроен? На что обращать внимание? EXPLAIN ANALYZE не только показывает план выполнения, но и выполняет запрос, выводя фактическое время выполнения каждой операции. Полезные моменты:
- фактическое время (Actual Time) - сколько времени заняла операция
- ROWS - реальное количество строк, прошедших через каждый этап
- LOOPS - количество раз, когда этот шаг был выполнен (важно для вложенных циклов)
EXPLAIN ANALYZE SELECT * FROM employees WHERE salary > 100000;
17. Инструмент для сбора статистики в PostgreSQL #
В PostgreSQL используется встроенный модуль pg_stat_statements для сбора статистики по запросам. Сохраняет информацию о запросах, частоте и времени их выполнения, а также сколько ресурсов потребляют
18. Что такое селективность? #
Селективность - это характеристика избирательности запроса. Если значения в колонке варьируются в небольшом промежутке, то селективность запроса, выбирающего значения по этой колонке - низкая. Высокая селективность позволяет сильно ускорить процесс вычитывания данных
19. Ключевое слово LATERAL #
LATERAL позволяет использовать данные из каждой строки внешней таблицы в подзапросе (аналог “для каждой строки”)
Пример:
Допустим, у нас есть таблица users и мы хотим для каждого пользователя взять его последние 3 транзакции из таблицы transactions
SELECT u.id, u.name, t.*
FROM users u,
LATERAL (
SELECT *
FROM transactions t
WHERE t.user_id = u.id
ORDER BY t.date DESC
LIMIT 3
) t;
- Для каждой строки таблицы
usersвыполняется подзапрос с ограничением (LIMIT 3) - Без
LATERALподзапрос не мог бы ссылаться на внешнюю таблицуu
20. Оконные функции. PARTITION BY #
Оконные функции позволяют делить выборку на непересекающиеся группы (партиции) по какому-либо столбцу и работать с ними независимо
Синтаксис:
SELECT <оконная_функция>(<поле_таблицы>)
OVER (
[PARTITION BY <столбцы_для_разделения>]
[ORDER BY <столбцы_для_сортировки>]
[ROWS|RANGE <определение_диапазона_строк>]
)
PARTITION BY <столбцы_для_разделения>делит выборку на непересекающиеся подмножества, где каждое подмножество содержит строки с одинаковыми значениями в одном или нескольких столбцах, образуются партиции. АналогGROUP BY, но не агрегирует строки, а позволяет работать с ними индивидуально в пределах группыORDER BY <столбцы_для_сортировки>устанавливает порядок строк внутри окна, особо важную роль играет в оконных функциях ранжированияROWS|RANGE <определение_диапазона_строк>формирует диапазоны строк. С помощью этого параметра можно указать сколько строк брать до и после текущей в окно
Синтаксиc ROW | RANGE
ROWS|RANGE BETWEEN <начало границы окна> AND <конец границы окна>
- UNBOUNDED PRECEDING — все строки, предшествующие текущей
- N PRECEDING - N строк до текущей строки
- CURRENT ROW - текущая строка
- N FOLLOWING - N строк после текущей строки
- UNBOUNDED FOLLOWING - все последующие строки
Пример:
Пусть есть таблица sales с колонками region, salesperson и sales. Мы хотим вычислить сумму продаж в каждом регионе и ранжировать сотрудников внутри регионов
SELECT
region,
salesperson,
sales,
SUM(sales) OVER (PARTITION BY region) AS total_region_sales,
RANK() OVER (PARTITION BY region ORDER BY sales DESC) AS rank_in_region
FROM sales;
PARTITION BY region- группировка по регионамSUM(sales)- выполняется для каждой строки, но только в рамках регионаRANK()- присваивает ранг на основе продаж внутри каждого региона

21. cube, rollup, groupBy #

CUBE Создаёт все возможные комбинации группировок.
SELECT region, product, SUM(sales)
FROM sales
GROUP BY CUBE(region, product);
Результат:
- Группировка по
region, product - Группировка только по
region - Группировка только по
product - Общая сумма по всем продажам
ROLLUP Создаёт иерархические уровни группировки (сверху вниз)
SELECT region, product, SUM(sales)
FROM sales
GROUP BY ROLLUP(region, product);
Результат:
- Группировка по
region, product - Группировка только по
region - Общая сумма по всем продажам
GROUPING SETS Явно указывает на комбинации группировки
SELECT region, product, SUM(sales)
FROM sales
GROUP BY GROUPING SETS ((region, product), (region), ());
Результат идентичен ROLLUP, но группы можно задать произвольно
GROUP BY используется для группировки строк по определённым столбцам и применения агрегатных функций к каждой группе. Это позволяет выполнять расчёты по группам данных, например, подсчёт суммы, среднего значения и т.д.
Пример:
Есть таблица sales с колонками region и revenue. Чтобы найти суммарный доход по каждому региону:
SELECT region, SUM(revenue) AS total_revenue
FROM sales
GROUP BY region;
region- группирующий столбецSUM(revenue)вычисляется для каждой группы
Если не использовать GROUP BY, SQL будет рассматривать все строки как одну группу
22. Хранимые процедуры #
Хранимая процедура (по сути аналог функции в Java) - это единожды созданный и сохранённый в базе данных набор SQL-инструкций (PL/pgSQL, PL/Python), выполняющий определённую задачу. Служит для повторного использования кода и оптимизации
Ключевые особенности:
- Входные параметры - передача данных в процедуру
- Выходные параметры - возврат результата
- Операторы управления потоком исполнения - циклы, условные конструкции (
IF,WHILE)
Пример создания и вызова хранимой процедуры:
CREATE PROCEDURE CalculateBonus (IN employee_id INT, OUT bonus_amount DECIMAL(10,2))
BEGIN
SELECT salary * 0.1 INTO bonus_amount
FROM employees
WHERE id = employee_id;
END;
Вызов:
CALL CalculateBonus(101, @bonus);
SELECT @bonus;
Преимущества:
- снижение трафика между клиентом и сервером
- лучшая организация кода
23. Как работает Serializable? #
Serializable (последовательный) - самый строгий уровень изоляции транзакций. Обеспечивает такое выполнение транзакций, как если бы они происходили последовательно (по одной за раз), даже если фактически выполняются параллельно. Использует блокировки или механизм версионирования (MVCC) для управления конкурентным доступом Полностью предотвращает все 4 типа транзакционных проблем
Может приводить к снижению производительности из-за увеличения времени ожидания между транзакциями
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
-- Первая транзакция
SELECT SUM(balance) FROM accounts WHERE account_type = 'savings';
-- Вторая транзакция (ожидание завершения первой)
INSERT INTO accounts (account_type, balance) VALUES ('savings', 500);
COMMIT;
24. View, materialized view. Разница между обычным и материализованным представлением? #
View (представление) - это сохранённый SQL-запрос, который служит для удобного (без необходимости повторно писать запрос) получения определённой выборки данных. Не хранит данные
CREATE VIEW ActiveUsers AS
SELECT id, name, email
FROM users
WHERE status = 'active';
-- Использование представления
SELECT * FROM ActiveUsers;
Materialized View (материализованное представление) - это непосредственно выборка, полученная по какому-то запросу и сохранённая в памяти. Может ускорить выполнение сложных запросов за счёт того, что не перечитывает данные. Не актуализируется автоматически (требует REFRESH), а потому данные могут быть не консистентными
CREATE MATERIALIZED VIEW SalesSummary AS
SELECT region, SUM(revenue) AS total_revenue
FROM sales
GROUP BY region;
-- Обновление данных
REFRESH MATERIALIZED VIEW SalesSummary;
25. C помощью SQL блокировка строки? - SELECT FOR UPDATE #
SELECT FOR UPDATE используется для блокировки строк в таблице, участвующих в запросе до окончания транзакции, чтобы предотвратить их изменение другими транзакциями
BEGIN;
-- Блокировка строки с id = 1
SELECT * FROM accounts
WHERE id = 1
FOR UPDATE;
-- Делаем обновление
UPDATE accounts
SET balance = balance - 100
WHERE id = 1;
COMMIT;
- В данном случае, строка с
id = 1заблокирована для других транзакций, пока текущая не завершится - Если другая транзакция попытается заблокировать ту же строку, она будет ожидать завершения текущей транзакции
26. UNION vs UNION ALL #
UNION и UNION ALL объединяют результаты двух или более запросов. Разница в том, что UNION осуществляет поиск и исключение дублирующихся строк, а UNION ALL - нет. В следствие этого UNION может работать медленнее. Оба оператора требуют, чтобы количество и типы столбцов в объединяемых выборках совпадали
SELECT city FROM customers
UNION [ALL]
SELECT city FROM suppliers;
Если оба запроса возвращают city = 'New York', то в результате UNION эта строка появится только один раз. В случае UNION ALL - дважды
27. Расскажи про ключевое слово DISTINCT? #
DISTINCT - это ключевое слово SQL, служащее для выбора только уникальных записей, т.е. устраняет дублирующиеся строки из результата запроса
28. Что такое табличные выражения? #
Обобщённое табличное выражение или CTE (Common Table Expressions) - это именованный временный результирующий набор данных, к которому можно обращаться в последующих запросах. Реализовано через ключевое слово WITH
WITH recent_sales AS (
SELECT *
FROM sales
WHERE sale_date > '2023-01-01'
)
SELECT * FROM recent_sales WHERE amount > 1000
Временная таблица recent_sales доступна только внутри текущего запроса
CTE могут быть материализованными и нематериализованными. По умолчанию это решает планировщик на основании анализа использования CTE в последующих запросах. Но можно явно указать этот параметр
WITH recent_sales AS [NOT] MATERIALIZED (
SELECT *
FROM sales
WHERE sale_date > '2023-01-01'
)
29. Для чего используется Database Connection Pool? (Hikari) Исчерпание connection pool #
Database Connection Pool - это пул подключений к базе данных, который управляет ограниченным числом соединений, чтобы уменьшить нагрузку на сервер базы данных и повысить производительность. Hikari - один из самых популярных Connection Pool
Почему нужен пул?
- Уменьшает накладные расходы на создание/закрытие соединений
- Повышает производительность многопоточных приложений
Так как открытие и закрытие подключений к базе данных - дорогостоящая операция, то выгоднее держать пул из открытых подключений, и выдавать их по запросу. Долго простаивающие подключения закрываются.
Более того, ограничение кол-ва подключений в пуле позволяет исключить перегрузку базы данных множественными подключениями. При отсутствии свободных подключений желающий обратиться к БД обязан ждать освобождения одного из подключений пула.
Исчерпание Connection Pool происходит, когда приложению требуется больше соединений, чем разрешено пулом. Возможные причины:
- утечка соединений (не закрываются после использования)
- неправильная настройка
maxPoolSize - избыточная нагрузка на приложение
Решения:
- Закрывать соединения через
try-with-resources - Увеличить
maxPoolSize, если это оправданно
30. Блокировки и локи. Оптимистичная и Пессимистичная #
Блокировки (или локи) в базе данных - это механизм, управляющий конкурентным доступом к данным, чтобы избежать конфликтов и ошибок. Есть два типа блокировок
Оптимистическая блокировка - при завершении транзакции сверяется текущая версия данных и версия, полученная в начале транзакции. Если они не совпадают, то транзакция откатывается. Подразумевает, что конфликты будут происходить нечасто
SELECT version
FROM orders
WHERE id = 1;
-- Обновляем, если версия не изменилась
UPDATE orders
SET status = 'processed', version = version + 1
WHERE id = 1 AND version = 5;
Пессимистическая блокировка - при начале транзакции изменяемые данные блокируются для всех остальных, пока текущая транзакция не закончится
SELECT *
FROM orders
WHERE id = 1
FOR UPDATE;
Сравнение
| Характеристика | Оптимистичные локи | Пессимистичные локи |
|---|---|---|
| Конфликты | Редкие | Частые |
| Механизм | Проверка версии данных | Блокировка ресурса |
| Производительность | Выше при редких конфликтах | Ниже из-за блокировок |
| Обработка конфликтов | Требует доп. кода | Не требуется |
| Применимость | Чтение превалирует над записью | Высококонкурентная запись |
31. БД. Constraint. Какие существуют? #
Constraint (ограничение) — это правило, накладываемое на столбцы или таблицы, чтобы обеспечить целостность и корректность данных
| Ограничение | Описание | Применимость |
|---|---|---|
| NOT NULL | Поле обязательно для заполнения | Идентификаторы, ключевые данные |
| UNIQUE | Значения в поле уникальны | Email, username |
| PRIMARY KEY | Уникально идентифицирует строку | Основной идентификатор записи |
| FOREIGN KEY | Обеспечивает целостность связи между таблицами | Таблицы с зависимыми данными |
| CHECK | Устанавливает условия для значений | Бизнес-правила |
| DEFAULT | Значение по умолчанию | Даты, статусы |
| EXCLUSION (для постгрес) | Обобщённый случай уникальности, позволяет писать кастомные условия для сравнения двух столбцов, которые должны соблюдаться при добавлении |
Пример EXCLUDE
CREATE TABLE reservations
(
id serial PRIMARY KEY,
room_id INTEGER,
booking_status TEXT,
start_date TIMESTAMP,
end_date TIMESTAMP,
EXCLUDE USING GIST (TSRANGE(start_date, end_date) WITH &&)
);
The EXCLUDE USING GISTclause creates an exclusion constraint that ensures there are no conflicting bookings for the same date range. The TSRANGE(start_date, end_date) WITH && condition checks for overlapping date ranges.
32. Primary Key #
Primary Key (PK) - это уникальный идентификатор записи в таблице. Без него сложно эффективно работать с данными, особенно в больших таблицах
- Гарантия уникальности - обеспечивает, что ни одна запись не будет полностью дублироваться, а значит может быть безошибочно идентифицирована
- Связь между таблицами - используется как основа для создания внешних ключей (FK)
- Ускорение поиска - PK автоматически индексируется, что ускоряет выборку данных
PK индексирован? Да, Primary Key автоматически индексируется базой данных. Этот индекс включает UNIQUE и NOT NULL ограничения
- Ускоряет поиск, сортировку и фильтрацию по PK
- Гарантирует уникальность значений в столбце PK
33. Foreign Key #
Foreign Key (внешний ключ) - это колонка (или набор колонок) таблице, ссылающаяся на первичный ключ (или любую UNIQUE колонку) другой таблицы. Обеспечивает ссылочную целостность.
CREATE TABLE users (
id INT PRIMARY KEY
);
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_id INT,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
Foreign Key может быть составным? Да, Foreign Key может быть составным (Composite Foreign Key)
| Характеристика | Primary Key (PK) | Foreign Key (FK) |
|---|---|---|
| Назначение | Гарантирует уникальность строк | Обеспечивает ссылочную целостность |
| Уникальность | Всегда уникален | Может повторяться |
| NULL? | Нельзя | Можно |
| Количество | Один на таблицу (может быть составным) | Может быть несколько |
| Удаление | Нельзя менять без CASCADE | ON DELETE CASCADE - удалит зависимые записи |
34. DELETE vs TRUNCATE #
DELETE - оператор DML, удаляет записи из таблицы, которые удовлетворяют условиям WHERE. Ведёт журналирование. Не сбрасывает последовательности. Медленнее. Есть возможность восстановить данные
TRUNCATE - DDL оператор, удаляет все строки из таблицы, быстро высвобождая занятую память. Не ведёт журналирование, не сбрасывает последовательности. Нет возможности восстановить данные