Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Проблемы баз данных: полный обзор
Базы данных — это критический компонент любого приложения. Вот основные проблемы, с которыми сталкиваются разработчики:
1. Проблемы производительности
N+1 Query Problem
# ❌ Плохо: N+1 запросов к БД
users = User.query.all() # 1 запрос
for user in users:
print(user.posts) # +N запросов для каждого пользователя
# Итого: 1 + N запросов!
# ✅ Хорошо: JOIN или eager loading
users = User.query.options(
joinedload(User.posts)
).all() # 1 запрос с JOIN
Медленные запросы
-- ❌ Плохо: полное сканирование таблицы
SELECT * FROM orders WHERE customer_name = 'John';
-- ✅ Хорошо: индекс на часто используемых полях
CREATE INDEX idx_orders_customer ON orders(customer_id);
SELECT * FROM orders WHERE customer_id = 123;
2. Проблемы конкурентности
Dirty Read (грязное чтение)
Транзакция 1 Транзакция 2
UPDATE balance = 100
SELECT balance -- Прочитает 100
ROLLBACK -- Оп! Транзакция откачена!
Данные были грязными
Lost Update (потеря обновления)
# ❌ Race condition
balance = get_balance(user_id) # 1000
balance += 100
update_balance(user_id, balance)
# Если два процесса выполнят одновременно:
# Процесс 1: 1000 + 100 = 1100
# Процесс 2: 1000 + 100 = 1100
# Результат: 1100 вместо 1200!
# ✅ Решение: SELECT FOR UPDATE
cursor.execute("""
SELECT balance FROM accounts
WHERE user_id = %s FOR UPDATE
""", (user_id,))
balance = cursor.fetchone()[0]
balance += 100
cursor.execute("""
UPDATE accounts SET balance = %s
WHERE user_id = %s
""", (balance, user_id))
Deadlock (тупик)
# Процесс 1 Процесс 2
BEGIN TRANSACTION BEGIN TRANSACTION
UPDATE users SET x = 1 UPDATE orders SET y = 2
WHERE id = 1 WHERE id = 1
UPDATE orders SET y = 2 UPDATE users SET x = 1
WHERE id = 1 WHERE id = 1
# DEADLOCK! # DEADLOCK!
# ✅ Решение: всегда обновляй в одном порядке
BEGIN TRANSACTION
UPDATE users SET x = 1 WHERE id = 1
UPDATE orders SET y = 2 WHERE user_id = 1
COMMIT
3. Проблемы масштабируемости
Растущий размер данных
День 1: 100 записей → быстро
День 30: 3,000 записей → медленнее
День 365: 36,500 записей → очень медленно
Год 2: 73,000 записей → КРИТИЧНО медленно
Решение: партиционирование
-- Разделить таблицу по дате
CREATE TABLE orders (
id SERIAL,
user_id INT,
created_at TIMESTAMP,
amount DECIMAL
) PARTITION BY RANGE (YEAR(created_at)) (
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026)
);
4. Проблемы консистентности данных
Foreign Key Constraint Violation
# ❌ Неправильно: нет связи
INSERT INTO orders (product_id) VALUES (99999);
# Успешно, но product_id 99999 не существует!
# ✅ Правильно: foreign key
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
product_id INT REFERENCES products(id)
);
# Теперь нельзя вставить несуществующий product_id
Каскадное удаление
# ❌ Неправильно: orphaned records
user = User.query.get(1)
user.delete() # orders остались без пользователя!
# ✅ Правильно: cascade delete
class User(Base):
__tablename__ = 'users'
orders = relationship(
'Order',
cascade='all, delete-orphan' # Удалить связанные orders
)
5. Проблемы хранения
Нехватка дискового пространства
# Проверить размер БД
SELECT pg_size_pretty(pg_database_size('mydb'));
# Если > 80% от доступного диска — критично!
# ✅ Решение: архивирование старых данных
DELETE FROM logs WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY);
Фрагментация индексов
-- Дефрагментация (PostgreSQL)
VACUUM ANALYZE;
REINDEX TABLE orders;
-- Дефрагментация (MySQL)
OPTIMIZE TABLE orders;
6. Проблемы резервного копирования
Потеря данных
# ❌ Без резервного копирования
rm -rf /var/lib/postgresql/ # Оп! Все данные потеряны
# ✅ Правильно: ежедневные резервные копии
# Настроить automated backups:
# - Физические: pg_basebackup
# - Логические: pg_dump
# - Облачные: AWS RDS, Google Cloud SQL
7. Проблемы с индексами
Неправильные индексы
-- ❌ Неправильно: индекс на поле, где 99% значений одинаковые
CREATE INDEX idx_is_active ON users(is_active);
-- Индекс не эффективен: почти все нулевые/единичные
-- ✅ Правильно: индекс на селективные поля
CREATE INDEX idx_users_email ON users(email); -- email уникален
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
Неиспользуемые индексы
-- Проверить неиспользуемые индексы (PostgreSQL)
SELECT indexname, idx_scan FROM pg_stat_user_indexes
WHERE idx_scan = 0 ORDER BY idx_tup_read DESC;
-- Удалить неиспользуемые
DROP INDEX idx_unused;
8. Проблемы с миграциями
Неправильный порядок миграций
# ❌ Плохо: миграции в неправильном порядке
# 0001_add_column.py
# 0003_create_index.py # Индекс на несуществующей колонке!
# 0002_add_another_column.py
# ✅ Правильно: последовательная нумерация
# 0001_add_column.py
# 0002_add_another_column.py
# 0003_create_index.py
Миграции без fallback
# ❌ Плохо: нет возможности откатиться
def up():
db.execute("ALTER TABLE users DROP COLUMN email")
# ✅ Правильно: всегда пиши down()
def up():
db.execute("ALTER TABLE users DROP COLUMN email")
def down():
db.execute("""
ALTER TABLE users ADD COLUMN email VARCHAR(255)
""")
9. Проблемы логирования и мониторинга
Недостаточное логирование
# ❌ Плохо: нет информации об ошибке
try:
cursor.execute(query)
except:
pass # Молча проигнорирован!
# ✅ Правильно: логируй всё
import logging
try:
cursor.execute(query)
except Exception as e:
logging.error(f"Query failed: {query}", exc_info=True)
raise
Отсутствие мониторинга
# ✅ Мониторь пулы соединений
pool_size = db_pool.size()
idle_connections = db_pool.idle()
if idle_connections < 2:
logging.warning("Low idle connections!")
# Мониторь slow queries
slow_query_log = "/var/log/mysql/slow.log"
# Анализируй с: pt-query-digest slow.log
10. Проблемы безопасности
SQL Injection
# ❌ Плохо: конкатенация строк
query = f"SELECT * FROM users WHERE id = {user_id}"
# Если user_id = "1; DROP TABLE users; --"
# SQL станет: SELECT * FROM users WHERE id = 1; DROP TABLE users; --
# ✅ Правильно: параметризованные запросы
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
Неправильные права доступа
-- ❌ Плохо: root пользователь для приложения
GRANT ALL ON *.* TO 'app'@'localhost' IDENTIFIED BY 'password';
-- ✅ Правильно: минимальные права
GRANT SELECT, INSERT, UPDATE ON mydb.* TO 'app'@'localhost';
GRANT DELETE ON mydb.orders TO 'app'@'localhost'; -- Если нужно
11. Проблемы репликации
Отставание реплики
Master: txn 1, txn 2, txn 3
Replica: txn 1, txn 2 (отстаёт на txn 3!)
Если читать из реплики, получишь устаревшие данные!
Решение: прочитай из master
# ❌ Плохо: можешь получить устаревшие данные
order = Order.query.using(replica_db).get(order_id)
# ✅ Правильно: критичные данные из master
order = Order.query.using(master_db).get(order_id)
Чеклист диагностики проблем БД
- Медленные запросы: включи slow query log
- High CPU: проверь индексы и EXPLAIN PLAN
- High Memory: посмотри размеры таблиц
- Connection timeouts: увеличь pool size
- Deadlocks: проверь порядок обновлений
- Data loss: проверь резервные копии
- Disk full: архивируй старые данные
- Replication lag: мониторь второго slave