← Назад к вопросам
Насколько хорошо владеешь PostgreSQL
1.0 Junior🔥 191 комментариев
#Базы данных и SQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Мой уровень владения PostgreSQL
Я имею глубокое знание PostgreSQL на уровне Senior/Lead разработчика. Использовал его во всех production приложениях и решал сложные архитектурные задачи.
Уровень компетенции: Senior
Я работал с PostgreSQL на приложениях, обрабатывающих миллионы запросов в день, с требованиями высокой доступности и консистентности.
Основные операции
1. Подключение и базовые запросы
-- Подключение
psql -U user -d database -h localhost
-- Создание таблицы
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) NOT NULL UNIQUE,
name VARCHAR(255),
age INT CHECK (age >= 0),
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- Основные операции
SELECT * FROM users WHERE age > 18;
INSERT INTO users (email, name, age) VALUES ('john@example.com', 'John', 30);
UPDATE users SET name = 'Jane' WHERE id = '123';
DELETE FROM users WHERE age < 18;
2. Индексирование (критическое для production)
-- Простой индекс
CREATE INDEX idx_users_email ON users(email);
-- Составной индекс
CREATE INDEX idx_users_status_created ON users(status, created_at DESC);
-- Уникальный индекс
CREATE UNIQUE INDEX idx_users_email_unique ON users(email);
-- Partial index (для фильтрации)
CREATE INDEX idx_active_users ON users(id) WHERE deleted_at IS NULL;
-- GIN индекс для полнотекстового поиска
CREATE INDEX idx_products_search ON products USING GIN(to_tsvector('english', description));
-- BRIN индекс для временных рядов
CREATE INDEX idx_events_time ON events USING BRIN(created_at);
-- Анализ индекса
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'john@example.com';
-- Просмотр индексов
SELECT * FROM pg_indexes WHERE tablename = 'users';
-- Удаление неиспользуемых индексов
SELECT schemaname, tablename, indexname, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0;
3. Транзакции и ACID
BEGIN;
UPDATE users SET balance = balance - 100 WHERE id = 'user1';
UPDATE users SET balance = balance + 100 WHERE id = 'user2';
COMMIT; -- или ROLLBACK если ошибка
-- Уровни изоляции
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
-- операции
COMMIT;
-- Savepoints
BEGIN;
UPDATE users SET status = 'active';
SAVEPOINT sp1;
UPDATE orders SET status = 'processed'; -- Может ошибиться
ROLLBACK TO sp1; -- Откатиться к point
COMMIT; -- Применить первый UPDATE
4. JOINs и отношения
-- Внутреннее соединение
SELECT u.name, o.total
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- Левое соединение
SELECT u.name, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id;
-- Несколько JOIN
SELECT u.name, p.title, c.text
FROM users u
INNER JOIN posts p ON u.id = p.user_id
LEFT JOIN comments c ON p.id = c.post_id
WHERE u.status = 'active';
-- Self-join (иерархия)
SELECT e1.name as employee, e2.name as manager
FROM employees e1
LEFT JOIN employees e2 ON e1.manager_id = e2.id;
5. Aggregation и GROUP BY
-- Основное группирование
SELECT status, COUNT(*) as count
FROM orders
GROUP BY status;
-- С условием
SELECT status, SUM(total) as total_revenue
FROM orders
WHERE created_at > NOW() - INTERVAL '30 days'
GROUP BY status
HAVING SUM(total) > 10000;
-- Window functions (мощные инструменты)
SELECT
id,
name,
salary,
AVG(salary) OVER (PARTITION BY department) as dept_avg,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) as rank,
ROW_NUMBER() OVER (ORDER BY salary DESC) as row_num,
LEAD(salary) OVER (ORDER BY hire_date) as next_salary
FROM employees;
-- LAG и LEAD для сравнения рядов
SELECT
user_id,
date,
revenue,
LAG(revenue) OVER (PARTITION BY user_id ORDER BY date) as prev_revenue,
revenue - LAG(revenue) OVER (PARTITION BY user_id ORDER BY date) as change
FROM daily_stats;
6. Миграции и Goose
# Создание миграции
goose create add_users_table sql
# Файл миграции: 00001_add_users_table.sql
-- +goose Up
CREATE TABLE users (
id UUID PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- +goose Down
DROP TABLE users;
# Выполнение миграций
goose up
goose down # Откатить последнюю
goose status
7. Оптимизация запросов
-- EXPLAIN для анализа запроса
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM users WHERE email = 'john@example.com';
-- Видеть план выполнения
EXPLAIN SELECT u.*, COUNT(o.id)
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id;
-- Плохой запрос: N+1
SELECT * FROM users; -- 100 запросов
-- затем для каждого user:
SELECT * FROM orders WHERE user_id = ?; -- 100 раз
-- Хороший запрос: JOIN
SELECT u.*, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id;
8. Полнотекстовый поиск
-- Создание GIN индекса
CREATE INDEX idx_posts_search ON posts USING GIN(
to_tsvector('english', title || ' ' || content)
);
-- Поиск
SELECT title, content
FROM posts
WHERE to_tsvector('english', title || ' ' || content) @@ plainto_tsquery('english', 'typescript nodejs')
ORDER BY ts_rank(to_tsvector('english', title || ' ' || content),
plainto_tsquery('english', 'typescript nodejs')) DESC;
9. Функции и процедуры
-- Функция на PL/pgSQL
CREATE FUNCTION get_user_with_posts(user_id UUID)
RETURNS TABLE (
user_id UUID,
email VARCHAR,
post_count INT
) AS $$
BEGIN
RETURN QUERY
SELECT u.id, u.email, COUNT(p.id)::INT
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
WHERE u.id = user_id
GROUP BY u.id;
END;
$$ LANGUAGE plpgsql;
-- Вызов
SELECT * FROM get_user_with_posts('123'::UUID);
10. Типы данных
-- UUID вместо INT для id
ALTER TABLE users ADD COLUMN id UUID PRIMARY KEY DEFAULT gen_random_uuid();
-- JSONB для структурированных данных
CREATE TABLE user_settings (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
settings JSONB DEFAULT '{}'::jsonb
);
INSERT INTO user_settings (user_id, settings)
VALUES ('123'::UUID, '{"theme": "dark", "notifications": true}'::jsonb);
-- Запрос JSONB
SELECT settings->>'theme' as theme FROM user_settings WHERE user_id = '123';
-- Arrays
CREATE TABLE posts (
id UUID PRIMARY KEY,
tags TEXT[] DEFAULT ARRAY[]::TEXT[]
);
SELECT * FROM posts WHERE 'typescript' = ANY(tags);
-- ENUM для статусов
CREATE TYPE order_status AS ENUM ('pending', 'processing', 'completed', 'cancelled');
CREATE TABLE orders (
id UUID PRIMARY KEY,
status order_status DEFAULT 'pending'
);
11. Производительность
Query optimization:
- Всегда используй индексы на columns, которые фильтруешь
- Используй EXPLAIN для анализа плана выполнения
- Избегай N+1 запросов
- Используй batch операции вместо одного за раз
Connection pooling:
- PgBouncer для управления connections
- PostgreSQL может обработать только определённое количество connections
- В high-load нужен пулер
Мониторинг:
-- Долгие запросы
SELECT pid, usename, query, query_start
FROM pg_stat_activity
WHERE state = 'active' AND query NOT LIKE '%pg_stat_activity%'
ORDER BY query_start;
-- Размер таблиц
SELECT tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename))
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
-- Индексы которые не используются
SELECT indexname FROM pg_indexes WHERE schemaname = 'public'
EXCEPT
SELECT indexname FROM pg_stat_user_indexes;
12. Практический пример: High-Load приложение
-- Денормализация для скорости чтения
CREATE TABLE user_stats (
user_id UUID PRIMARY KEY,
post_count INT DEFAULT 0,
follower_count INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Trigger для обновления stats
CREATE OR REPLACE FUNCTION update_user_stats()
RETURNS TRIGGER AS $$
BEGIN
UPDATE user_stats
SET post_count = (SELECT COUNT(*) FROM posts WHERE user_id = NEW.user_id),
updated_at = CURRENT_TIMESTAMP
WHERE user_id = NEW.user_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER posts_insert_trigger
AFTER INSERT ON posts
FOR EACH ROW
EXECUTE FUNCTION update_user_stats();
Мой опыт на production
Проблемы, которые решал:
- Slow queries — анализ через EXPLAIN, добавление индексов
- Connection exhaustion — внедрение PgBouncer
- Bloat таблиц — регулярная VACUUM и ANALYZE
- Deadlocks — правильный order of operations в транзакциях
- Масштабирование — replication и failover
Размеры баз данных:
- От 100 MB в development
- До 500 GB в production
- С миллионами записей в день
Уровень владения
Знаю на отлично:
- SQL синтаксис и оптимизация
- Индексирование и query planning
- Транзакции и ACID
- Window functions и aggregation
- Миграции и versioning
- Мониторинг и performance tuning
Неплохо знаю:
- Replication и HA
- Partitioning для очень больших таблиц
- Плагины и расширения
Что бы улучшил:
- Deeper в logical replication
- Advanced security features
- Sharding стратегии
Best Practices на production
- Всегда используй UUID для id — не INT
- Индексируй правильно — составные индексы для часто используемых фильтров
- VACUUM и ANALYZE — регулярно автоматизировать
- Backup strategy — минимум daily backups, желательно непрерывная репликация
- Мониторинг — query performance, connections, disk space
- Миграции — используй Goose или Flyway, не hand-crafted
- Connection pooling — обязателен для production
Мой уровень владения PostgreSQL: Senior / Lead — я могу спроектировать и оптимизировать БД архитектуру для high-load приложений.