Базы данных (Data Base)
8.01K subscribers
515 photos
452 videos
19 files
479 links
Базы данных (Data Base). По всем вопросам @evgenycarter
Download Telegram
Антипаттерн: UUID как PK без учёта последствий

Выглядит красиво: глобально уникальный идентификатор, можно генерировать на клиенте, удобно в распределённых системах. Но...

💣 Проблемы:
– Большой размер (16 байт vs 4 байта у INT)
– Плохая локальность: индекс B-Tree фрагментируется
– Медленнее вставки, особенно при высоких нагрузках
– Нагружает сеть, если часто передаёшь PK

📉 В PostgreSQL это особенно заметно: индекс на UUID-ключе может вести себя гораздо хуже, чем на BIGSERIAL.


Как делать правильно:

1. Если всё в одной БД: используй BIGINT или BIGSERIAL
2. Если нужен UUID:
– генерируй UUID v7 (появился в 2022, содержит компонент времени → лучше упорядочен)
– либо UUID v1 (временной, но с оговорками по безопасности)
– или комбинируй автоинкремент + случайный суффикс
3. Храни UUID как UUID, а не как VARCHAR(36) — это экономит место и CPU


🧠 UUID — мощный инструмент, но не серебряная пуля. Прежде чем делать его PRIMARY KEY, подумай: что ты реально выигрываешь?

Сохрани, чтобы не собирать фрагментированные индексы вручную 😅

#db

👉 @database_info
👍10👎32
Почему одна и та же БД летает на staging и тормозит в проде

Знакомо? На staging сервере — отклик 100мс, на проде — секундные таймауты. Хотя база одна и та же, схема такая же. Что не так?

Вот 5 частых причин:


1. Разный объём данных
На staging — 10k строк, на проде — 10 млн. Индексы, которые "и так нормально", внезапно перестают справляться.

2. Отсутствие/различие индексов
DevOps мог не раскатить нужные индексы в прод. Или, наоборот, staging набит экспериментальными индексами.

3. Параметры конфигурации БД
work_mem, shared_buffers, max_connections — часто в staging минимальны, но в проде тоже забывают подкрутить.

4. Статистика устарела
На проде реже делается ANALYZE, планировщик начинает строить неэффективные планы. Итог — ползёт.

5. Разное поведение приложения
Прод нагружается параллельно десятками потоков. Staging — ты и Postman.


🛠 Что делать:
– Сравни настройки сервера (SHOW ALL;)
– Проверь EXPLAIN ANALYZE
– Не доверяй staging — тестируй на продоподобных данных

#db

👉 @database_info
👍9🤣21
Антипаттерн: использование SELECT * в продакшене

Кажется безобидным, правда? Особенно на этапе прототипирования. Но как только ваш запрос с SELECT * уходит в прод, начинаются проблемы:

🔻 Почему это плохо:

Избыточные данные. Вы тянете всё, включая ненужные поля. Это бьёт по сети, памяти и CPU.
Ломкость кода. Добавили колонку в таблицу — и, внезапно, старый код падает, потому что ожидал другую структуру.
Плохая читаемость. Непонятно, какие поля реально нужны. Это мешает отладке и сопровождению.
Невозможно использовать covering index — индекс по нужным колонкам не спасёт, если вы вытаскиваете всё подряд.

📌 Как правильно:

Явно указывайте нужные поля:


SELECT id, name, created_at FROM users;


Работаете с ORM — настраивайте выборку полей в select() или .only() (в зависимости от фреймворка).

В аналитике? Даже при джойнах и CTE — указывайте, что реально используете.

🧠 Запомни: чем меньше данных ты запрашиваешь — тем быстрее и стабильнее работает твой код.

💬 А ты встречал SELECT * в проде? Или, может, сам когда-то писал так?

#db

👉 @database_info
👍123
🔴 Антипаттерн: игнорирование поведения NULL в SQL

Когда ты пишешь WHERE column != 'value', ты можешь думать, что фильтруешь всё, что не равно 'value'. Но если в колонке есть NULL, такие строки выпадут из выборки. Почему? Потому что NULL != 'value' даёт… UNKNOWN, а не TRUE.

Пример проблемы:


SELECT * FROM users
WHERE status != 'active';


Если status у кого-то NULL — он не попадёт в результат. Неочевидно, но критично.

✔️ Как избежать:

1. Явно учитывай NULL'ы:


SELECT * FROM users
WHERE status != 'active' OR status IS NULL;


2. Работай с COALESCE, если допустимо:


SELECT * FROM users
WHERE COALESCE(status, '') != 'active';


📌 То же касается и =: NULL = 'value'UNKNOWN
📌 Агрегации (COUNT, AVG`) тоже игнорируют `NULL — помни об этом при аналитике

Вывод:
NULL — не "ничего", а "неизвестно", и SQL строго это уважает. Не учитывая это, легко получить баг, который даже не заметишь сразу.

💡 Всегда проверяй поведение WHERE, JOIN и агрегатов с NULL. Пиши явный код, особенно в проде.

Сохрани, чтобы не ловить баги на ровном месте.

#db

👉 @database_info
👍132
📕 Практические кейсы использования ClickHouse для разработчиков, администраторов, специалистов по базам данных, Data engineers, Backend и FullStack-разработчиков

На открытом уроке 24 июля в 20:00 мск мы погрузимся в тонкости работы с ClickHouse:

📗 На вебинаре разберём:
1. Основные принципы работы, архитектура и преимущества использования ClickHouse;
2. Реальные кейсы использования ClickHouse для анализа веб-логов, IoT данных и финансовых транзакций;

📘 В результате на практике разберетесь в настройке и использовании ClickHouse для обработки больших объемов данных.

👉 Регистрация и подробности о курсе NoSQL: https://vk.cc/cNQL7R

Все участники открытого урока получат скидку на курс "NoSQL"

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
Как понять, что вашему проекту нужен полнотекстовый поиск, а не ILIKE

Часто разработчики в PostgreSQL начинают с простого:


SELECT * FROM articles WHERE title ILIKE '%postgres%';


Но уже при 10k+ строк и регулярных запросах начинаются тормоза. Значит, пора на следующий уровень — полнотекстовый поиск.

🔍 Когда ILIKE — плохо:
– Сложные запросы с несколькими ILIKE
– Не масштабируется: без индексов → full scan
– Нет нормализации слов: postgres, PostgreSQL, постгрес — всё разное

💡 Решение: to_tsvector + to_tsquery


SELECT * FROM articles
WHERE to_tsvector('russian', title) @@ to_tsquery('russian', 'postgres');


📈 Плюсы:
– Работают GIN-индексы
– Поддержка морфологии и синонимов
– Быстрее и точнее на больших объемах

⚠️ Подводные камни:
– Нужна настройка языкового словаря
tsquery не такая гибкая, как regex
– Требуется обновление индекса при INSERT/UPDATE

🛠 Как включить GIN-индекс:


CREATE INDEX idx_articles_title_search
ON articles USING GIN (to_tsvector('russian', title));


👉 Если пользователи ищут по тексту — не тормозите ILIKE, внедряйте полнотекст!

Сохрани, чтобы потом не мучиться с explain-ами 😉

#db

👉 @database_info
👍63👎1
Отказоустойчивость от двух нод в облачных базах данных
 
Если вам важна бесперебойная работа вашего проекта, самое время позаботиться об отказоустойчивости. Selectel поможет сделать это проще и выгоднее — у провайдера можно создать отказоустойчивый кластер баз данных всего от двух нод.
 
Выгода очевидна: использование двух нод в кластере вместо стандартных трех позволяет сократить расходы на 33%.
 
Почему это надежно?
 
▪️SLA отказоустойчивого кластера — 99,95% на запись и 99,99% на чтение.
▪️Доступно автоматическое резервное копирование кластера «из коробки» и без доплат. Ежедневно создаются инкрементальные бэкапы, а полные копии кластера — раз в неделю.
▪️Можно создавать реплики в разных сегментах пула для большей надежности. При этом серверы кластера размещаются на разных физических хостах и имеют разные контуры питания.
 
Разверните отказоустойчивые кластеры облачных баз данных в Selectel: https://slc.tl/80ecr

Реклама, АО «Селектел», ИНН: 7810962785, ERID: 2VtzqxjxEBa
🧱 Антипаттерн: использование UUID как Primary Key без оглядки

На первый взгляд, UUID — классный способ генерировать уникальные идентификаторы:
– не зависят от последовательности
– удобны для распределённых систем
– безопасны для внешнего экспонирования

Но если ты просто заменишь SERIAL или BIGINT на UUID в качестве PK — жди сюрпризов:

В чём подвох:
Производительность INSERT'ов резко падает: UUID случайные → нет locality → индекс (обычно B-Tree) постоянно фрагментируется
Индексы пухнут: UUID = 16 байт, BIGINT = 8 байт. Разница кажется небольшой, но на больших объёмах — это боль
Чтение медленнее: за счёт увеличенного размера индексов и меньшего кэш-хита

Как избежать:

1. Если нет жёсткой необходимости в UUID — не используй их как PK
2. Нужен UUID? Сделай его вторичным индексом, а PK оставь автоинкрементным
3. Или хотя бы используй UUID v7 (новый стандарт с time-based префиксом) — он улучшает локальность по сравнению с v4

Пример:


CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
public_id UUID DEFAULT gen_random_uuid() UNIQUE,
name TEXT
);


→ Внутри БД — быстрый BIGINT,
→ Для внешних API — UUID. Удобно и производительно.


#db

👉 @database_info
👍12👎2
This media is not supported in your browser
VIEW IN TELEGRAM
Хотите узнать секрет оптимизации SQL-запросов?

Очень важно понимать порядок выполнения.
В SQL-запросе операторы выполняются в следующем порядке:

1. FROM / JOIN
2. WHERE
3. GROUP BY
4. HAVING
5. SELECT
6. DISTINCT
7. ORDER BY
8. LIMIT / OFFSET

#db

👉 @database_info
👍6
Бесплатный курс по PostgreSQL от практиков рынка

Присоединяйтесь к бесплатному курсу по основам PostgreSQL от Selectel и Эльбрус Буткемп. Он будет полезен Junior- и Middle-специалистам: администраторам баз данных, разработчикам, DevOps-инженерам и аналитикам.
 
Вы научитесь:
🔹создавать и связывать таблицы,
🔹выполнять базовые операции с данными,
🔹работать с РСУБД.

Бонусы: вы можете получить сертификат о прохождении курса, а также промокоды для практики на мощностях Selectel.

Начните обучение уже сегодня.

Реклама. АО «Селектел», ИНН 7810962785, ERID: 2VtzqxH9jwE
6
Индексы в PostgreSQL: Часть 1 — B-Tree

Если ты создавал индекс в PostgreSQL по умолчанию, значит, это B-Tree.
Но как он работает и когда он реально полезен?

Что это такое?

B-Tree индекс — сбалансированное дерево поиска.
PostgreSQL автоматически использует его для:

=\` (равенство)
> < >= <= (сравнения)
BETWEEN
LIKE 'abc%' (только префикс, без %abc%).

Пример:


CREATE INDEX idx_users_email ON users (email);
SELECT * FROM users WHERE email = '[email protected]';


Запрос не будет сканировать всю таблицу — он сразу пойдёт по дереву.

Подводные камни:

1. Не работает для произвольных LIKE:
LIKE '%abc%' → индекс не поможет.
2. Осторожно с функциями:
WHERE LOWER(email) = 'abc' — индекс не используется. Нужен функциональный индекс:


CREATE INDEX idx_users_email_lower ON users (LOWER(email));

3. Многоколонковые индексы:
Порядок важен. (a, b) используется при фильтре по a или по a AND b, но не только по b.

Когда ставить?

- Уникальные поля (email, username).
- Часто используемые фильтры и JOIN-колонки.
- Сортировки (ORDER BY created_at DESC).

Вывод:
B-Tree — твой “универсальный солдат”. Но не пихай его на всё подряд. Перед добавлением — смотри EXPLAIN (ANALYZE).

Сохрани, чтобы не забыть!

#db

👉 @database_info
👍18
This media is not supported in your browser
VIEW IN TELEGRAM
Чем отличаются друг от друга блокировки баз данных?

В управлении базами данных блокировки — это механизмы, которые предотвращают одновременный доступ к данным, обеспечивая их целостность и согласованность.

Основные типы блокировок:

🔴Shared Lock: позволяет нескольким транзакциям одновременно читать ресурс, но не модифицировать его
🔴Exclusive Lock: позволяет транзакции как читать, так и модифицировать ресурс
🔴 Update Lock: используется для предотвращения взаимоблокировки, когда транзакция намеревается обновить ресурс
🔴 Schema Lock: используется для защиты структуры объектов базы данных
🔴 Bulk Update Lock: используется во время массовых вставок
🔴 Key-Range Lock: используется в индексированных данных для предотвращения фантомных чтений
🔴 Row-Level Lock: блокирует конкретную строку в таблице
🔴 Page-Level Lock: блокирует конкретную страницу (фиксированный блок данных) в базе данных
🔴 Table-Level Lock: блокирует всю таблицу

#db

👉 @database_info
Please open Telegram to view this post
VIEW IN TELEGRAM
10👍141
Почему Redis такой быстрый (несмотря на однопоточность)?

🔹 Хранение в памяти
Redis хранит все данные в оперативной памяти, где время доступа измеряется наносекундами, а не миллисекундами.

🔹 Однопоточный цикл событий
Redis обрабатывает команды в одном потоке, избегая блокировок, гонок и переключений контекста. Благодаря мультиплексированию ввода-вывода он эффективно обслуживает тысячи одновременных подключений через цикл событий.

🔹 Оптимизированные структуры данных
Redis предоставляет специализированные реализации списков, множеств, отсортированных множеств и хешей, оптимизированные для производительности и экономии памяти.

🔹 Эффективность ввода-вывода
Redis использует лёгкий текстовый протокол RESP для обработки сетевого I/O и поддерживает конвейеризацию, позволяя клиентам отправлять несколько команд в одном запросе.

🔹 Скрипты на стороне сервера
Встроенный движок Lua даёт возможность выполнять сложные многошаговые операции атомарно на сервере, убирая необходимость лишних сетевых запросов.

♻️ Сделай репост, чтобы помочь другим.

#db

👉 @database_info
👍14🔥2
Media is too big
VIEW IN TELEGRAM
Базы данных. Школа бэкенд-разработки 2025

На лекции обсудим основные понятия и принципы работы с базами данных. Рассмотрим факторы, влияющие на выбор подходящей базы данных для конкретной задачи. Познакомимся с индексами и их ролью в ускорении запросов. Поделимся советами по оптимальному использованию баз данных и рекомендациями для эффективной работы

источник

#db

👉 @database_info
👍32
Почему индекс в PostgreSQL не всегда спасает

Индексы - мощный инструмент, но не панацея. Иногда запрос с индексом работает медленнее, чем без него. Почему?

1️⃣ Маленькая выборка - да, полное сканирование - нет
Если таблица маленькая (до нескольких тысяч строк), PostgreSQL может решить, что быстрее прочитать всё целиком, чем прыгать по индексу.


EXPLAIN ANALYZE
SELECT * FROM users WHERE status = 'active';


План покажет Seq Scan, и это не баг.

2️⃣ Индекс не помогает с функциями в WHERE
Запрос вида:


SELECT * FROM orders WHERE DATE(created_at) = '2025-08-12';


не использует индекс по created_at. Решение — переписать условие:


WHERE created_at >= '2025-08-12' AND created_at < '2025-08-13'


3️⃣ Селективность
Если по условию отбирается больше ~5–10% строк, индекс становится невыгодным — чтение с диска и так почти сплошное.

4️⃣ Статистика устарела
PostgreSQL выбирает план по статистике. Если она старая - план может быть неэффективным.


ANALYZE table_name;


- и жизнь наладится.

💡 Вывод: Индекс - не магическая кнопка «ускорить». Следи за планами запросов (EXPLAIN), обновляй статистику и оптимизируй условия.

Сохрани, чтобы не наступить на этот грабельный индекс 🚀

#db

👉 @database_info
👍7