🪞 Иллюзия скорости: почему обычный VIEW не ускорит запросы
Спрятал огромный JOIN в представление, чтобы база работала быстрее? Плохие новости: обычный VIEW вообще ничего не кэширует.
⚙️ Как это работает на самом деле
Многие путают представление с физической таблицей. Но обычный VIEW — это просто текстовый макрос.
🔹 Каждый раз, когда ты делаешь
🔹 Никакие результаты на диск не сохраняются. Все тяжёлые вычисления будут происходить заново при каждом обращении.
⚠️ Опасность «матрёшки»
Самая частая ошибка — строить одни вьюшки поверх других.
🔸 База вынуждена разворачивать этот клубок в один гигантский запрос.
🔸 Планировщик запросов теряется в абстракциях и может выбрать худший план выполнения. То, что должно работать секунду, выполняется часами.
🚀 Что делать?
🔹 Для реального ускорения и сохранения результата на диск используй:
🔹 Обычный VIEW оставляй только для удобства: чтобы скрыть сложную логику от других разработчиков или упростить чтение кода.
💡 Обычное представление создано для чистоты кода, а не для производительности.
Спрятал огромный JOIN в представление, чтобы база работала быстрее? Плохие новости: обычный VIEW вообще ничего не кэширует.
⚙️ Как это работает на самом деле
Многие путают представление с физической таблицей. Но обычный VIEW — это просто текстовый макрос.
🔹 Каждый раз, когда ты делаешь
SELECT * FROM my_view, база берёт исходный запрос и выполняет его с нуля.🔹 Никакие результаты на диск не сохраняются. Все тяжёлые вычисления будут происходить заново при каждом обращении.
⚠️ Опасность «матрёшки»
Самая частая ошибка — строить одни вьюшки поверх других.
🔸 База вынуждена разворачивать этот клубок в один гигантский запрос.
🔸 Планировщик запросов теряется в абстракциях и может выбрать худший план выполнения. То, что должно работать секунду, выполняется часами.
🚀 Что делать?
🔹 Для реального ускорения и сохранения результата на диск используй:
CREATE MATERIALIZED VIEW my_cache AS
SELECT ...
🔹 Обычный VIEW оставляй только для удобства: чтобы скрыть сложную логику от других разработчиков или упростить чтение кода.
💡 Обычное представление создано для чистоты кода, а не для производительности.
❤7👍6🔥1
♾️ Дырявый UNIQUE: как индекс пропускает дубликаты
Ты создал ограничение UNIQUE и уверен, что база под надёжной защитой. Но она радостно пропустит в таблицу хоть миллион строк с пустыми значениями.
⚙️ Почему так происходит
🔹 По стандарту SQL значение
🔹 Равна ли одна неизвестность другой? Нет. Правило гласит:
🔹 Раз пустые значения не равны друг другу, ограничение уникальности их пропускает. PostgreSQL, MySQL и Oracle позволят создавать бесконечные пустые профили.
⚠️ Сюрприз от SQL Server
В MS SQL Server логика другая. Он считает
🚀 Что делать
🔸 Если поле всегда должно быть заполнено — добавляй ограничение
🔸 В PostgreSQL 15+ можно явно запретить дубликаты пустых строк правилом
💡 Уникальность защищает только известные данные, а для пустых нужны свои правила.
Ты создал ограничение UNIQUE и уверен, что база под надёжной защитой. Но она радостно пропустит в таблицу хоть миллион строк с пустыми значениями.
⚙️ Почему так происходит
🔹 По стандарту SQL значение
NULL означает «неизвестность».🔹 Равна ли одна неизвестность другой? Нет. Правило гласит:
NULL != NULL.🔹 Раз пустые значения не равны друг другу, ограничение уникальности их пропускает. PostgreSQL, MySQL и Oracle позволят создавать бесконечные пустые профили.
CREATE TABLE users (
email VARCHAR(100) UNIQUE
);
-- База сохранит обе строки:
INSERT INTO users (email) VALUES (NULL), (NULL);
⚠️ Сюрприз от SQL Server
В MS SQL Server логика другая. Он считает
NULL конкретным значением и разрешает добавить его только один раз. Вторая пустая строка выдаст ошибку.🚀 Что делать
🔸 Если поле всегда должно быть заполнено — добавляй ограничение
NOT NULL.🔸 В PostgreSQL 15+ можно явно запретить дубликаты пустых строк правилом
UNIQUE NULLS NOT DISTINCT.💡 Уникальность защищает только известные данные, а для пустых нужны свои правила.
❤8👍7🤯2
⏳ Конец хаосу: почему UUIDv7 — хороший выбор для Primary Key
Обычный автоинкремент (ID=1, 2, 3), если показывать его наружу, позволяет конкурентам легко высчитать ваши продажи. А популярный случайный UUIDv4 незаметно снижает производительность базы.
⚠️ Две крайности старых подходов
🔷 Автоинкремент: быстрый и компактный, но раскрывает объёмы. Если зарегистрироваться дважды с разницей в день, конкурент узнает точное число ваших новых клиентов.
🔷 UUIDv4: абсолютно случаен. Каждая новая запись попадает в случайное место индекса. Это как вписывать новые контакты на случайные страницы бумажной телефонной книги: страницы приходится постоянно делить, индекс раздувается, а
🚀 Магия UUIDv7
Свежий стандарт (RFC 9562, 2024) решает обе проблемы. В начало ключа записывается текущее время (Timestamp) с точностью до миллисекунды, а остальная часть остаётся случайной.
В версиях до 18 нужно расширение
📈 Что мы получаем
🔶 Новые записи ложатся в B-дерево почти последовательно, в конец индекса.
🔶 Индекс меньше раздувается, и вставка заметно быстрее, чем с UUIDv4.
🔶 Идентификаторы уникальны, и посчитать по ним объёмы нельзя. Но время создания записи из них читается.
💡 UUIDv7 — хороший компромисс: вставка почти как у счётчика, и объёмы не утекают. Но он вдвое больше
Обычный автоинкремент (ID=1, 2, 3), если показывать его наружу, позволяет конкурентам легко высчитать ваши продажи. А популярный случайный UUIDv4 незаметно снижает производительность базы.
⚠️ Две крайности старых подходов
🔷 Автоинкремент: быстрый и компактный, но раскрывает объёмы. Если зарегистрироваться дважды с разницей в день, конкурент узнает точное число ваших новых клиентов.
🔷 UUIDv4: абсолютно случаен. Каждая новая запись попадает в случайное место индекса. Это как вписывать новые контакты на случайные страницы бумажной телефонной книги: страницы приходится постоянно делить, индекс раздувается, а
INSERT на больших таблицах замедляется.🚀 Магия UUIDv7
Свежий стандарт (RFC 9562, 2024) решает обе проблемы. В начало ключа записывается текущее время (Timestamp) с точностью до миллисекунды, а остальная часть остаётся случайной.
-- PostgreSQL 18+
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT uuidv7(),
amount NUMERIC
);
В версиях до 18 нужно расширение
pg_uuidv7 с функцией uuid_generate_v7().📈 Что мы получаем
🔶 Новые записи ложатся в B-дерево почти последовательно, в конец индекса.
🔶 Индекс меньше раздувается, и вставка заметно быстрее, чем с UUIDv4.
🔶 Идентификаторы уникальны, и посчитать по ним объёмы нельзя. Но время создания записи из них читается.
💡 UUIDv7 — хороший компромисс: вставка почти как у счётчика, и объёмы не утекают. Но он вдвое больше
bigint и не секрет: доступ к данным всё равно проверяйте на сервере.🔥10❤7👍3🏆1