← Назад к вопросам

Какие проблемы бывают у баз данных?

3.0 Senior🔥 111 комментариев
#Базы данных (SQL)

Комментарии (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