← Назад к вопросам
Хорошо ли знаешь PostgreSQL
2.2 Middle🔥 131 комментариев
#Stream API и функциональное программирование#Многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Знание PostgreSQL: Практический уровень senior разработчика
Честный ответ
Да, я хорошо знаю PostgreSQL. Более 8 лет работаю с Postgres как основной базой данных, участвовал в оптимизации систем с 100GB+ данных. Вот что входит в мой уровень знаний.
Базовые навыки (которые каждый должен знать)
1. ACID и транзакции
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; // или ROLLBACK если ошибка
// Isolation levels
// READ COMMITTED → default, видимы только закоммиченные данные
// REPEATABLE READ → снимок транзакции
// SERIALIZABLE → полная изоляция, самая дорогая
2. Индексы и их типы
// B-tree (default, для сравнений)
CREATE INDEX idx_user_email ON users(email);
SELECT * FROM users WHERE email = 'test@mail.com'; // быстро
// GiST/GIN (для полнотекстового поиска)
CREATE INDEX idx_fulltext ON documents USING GIN(to_tsvector(content));
// BRIN (Block Range Index, для очень больших таблиц)
CREATE INDEX idx_brin ON events USING BRIN(created_at);
// Эффективнее B-tree для таблиц с миллиардами строк
3. Query Planning и EXPLAIN
EXPLAIN ANALYZE SELECT * FROM users WHERE id > 1000;
// Читаем план:
// Seq Scan = полное сканирование (плохо для больших таблиц)
// Index Scan = поиск по индексу (хорошо)
// Nested Loop (медленно), Hash Join (быстро для больших наборов)
Продвинутые навыки
1. Window Functions (аналитические функции)
SELECT
user_id,
amount,
ROW_NUMBER() OVER (ORDER BY amount DESC) as rank
FROM purchases;
// Moving average
SELECT
date,
revenue,
AVG(revenue) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) as avg_7days
FROM daily_sales;
2. Common Table Expressions (CTE)
WITH user_purchases AS (
SELECT user_id, SUM(amount) as total FROM purchases GROUP BY user_id
)
SELECT users.name, up.total
FROM users
JOIN user_purchases up ON users.id = up.user_id
WHERE up.total > 1000;
// Рекурсивный CTE (для иерархий)
WITH RECURSIVE category_tree AS (
SELECT id, name, parent_id, 1 as level
FROM categories WHERE parent_id IS NULL
UNION ALL
SELECT c.id, c.name, c.parent_id, ct.level + 1
FROM categories c JOIN category_tree ct ON c.parent_id = ct.id
WHERE ct.level < 10
)
SELECT * FROM category_tree;
3. JSON и JSONB операции
CREATE TABLE documents (id BIGSERIAL PRIMARY KEY, data JSONB NOT NULL);
SELECT
data->>'user' as name,
(data->>'views')::INT as views
FROM documents
WHERE data @> '{"status": "published"}';
// Индексирование JSON
CREATE INDEX idx_doc_status ON documents USING GIN (data jsonb_path_ops);
Оптимизация и Performance Tuning
1. Проблема: N+1 запросы
// ПЛОХО: N+1 запросы
List<User> users = userRepository.findAll(); // Query 1
users.forEach(user -> {
List<Order> orders = orderRepository.findByUserId(user.getId()); // Query N
});
// ХОРОШО: один запрос с JOIN
@Query("SELECT DISTINCT u FROM User u LEFT JOIN FETCH u.orders WHERE u.active = true")
List<User> getUsersWithOrdersEager();
2. Vacuum и Analyze
VACUUM ANALYZE users;
// VACUUM удаляет мёртвые строки, ANALYZE обновляет статистику
VACUUM FULL users; // блокирует таблицу, использовать редко
SELECT schemaname, tablename, last_vacuum, last_autovacuum
FROM pg_stat_user_tables
ORDER BY last_autovacuum DESC;
3. Connection pooling в Java
@Configuration
public class DataSourceConfig {
@Bean
public HikariDataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
config.setMaximumPoolSize(20); // 10-20 для типичного приложения
config.setMinimumIdle(5);
config.setConnectionTimeout(30000);
return new HikariDataSource(config);
}
}
Боевые примеры из реального опыта
Проблема 1: Таблица orders выросла до 500MB, запросы медленные
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123;
// Показала: Seq Scan (плохо)
CREATE INDEX idx_orders_user_id ON orders(user_id);
// После: Index Scan (хорошо), время упало с 2s на 50ms
Проблема 2: Мастер становится readonly, диск 100%
SELECT pg_size_pretty(pg_database_size('mydb'));
DELETE FROM logs WHERE created_at < NOW() - INTERVAL '6 months';
VACUUM FULL logs;
// Решение: партиционирование
CREATE TABLE logs_partitioned (...) PARTITION BY RANGE (created_at);
// Партиции удаляются целиком, без VACUUM
Что я использую в повседневной работе
// В Spring Data JPA:
// 1. Eager loading
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders WHERE u.id = ?1")
Optional<User> findByIdWithOrders(Long id);
// 2. Batch updates
@Modifying
@Query("UPDATE User u SET u.lastLogin = NOW() WHERE u.id IN ?1")
int updateLastLogin(List<Long> ids);
// 3. Кастомные JDBC запросы
@Repository
public class CustomUserRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
public List<UserStats> getComplexStats() {
String sql = "SELECT u.id, COUNT(o.id) as order_count, SUM(o.amount) as total_spent " +
"FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id";
return jdbcTemplate.query(sql, (rs, rowNum) -> new UserStats(
rs.getLong("id"),
rs.getInt("order_count"),
rs.getBigDecimal("total_spent")
));
}
}
Итоговый уровень
Я комфортно могу:
- Проектировать схемы БД с учётом масштабируемости
- Оптимизировать медленные запросы через индексы и переписывание SQL
- Разбираться с production проблемами (диск, память, slow logs)
- Настраивать репликацию и backup стратегии
- Писать сложные запросы с CTE и Window Functions
Я НЕ претендую на:
- Администратор БД (это отдельная специальность)
- Internals PostgreSQL (как работает storage engine)
- Advanced replication strategies (Patroni, failover)
Уровень: Senior Java Developer, который может самостоятельно решать 95% задач, связанных с Postgres.