Индексы, транзакции, ORM pitfalls, миграции
Ошибки в работе с БД дорого исправлять: миграции назад, потеря данных, простой продакшена. Code review — ваш шанс поймать их до прода.
# ❌ Медленно: полный скан таблицы
# SELECT * FROM users WHERE email = 'test@example.com'
# Без индекса на email — O(n)
# ✅ Быстро: индекс
# CREATE INDEX idx_users_email ON users(email);
# O(log n) поиск
# ✅ В ORM (Django)
class User(models.Model):
email = models.EmailField(db_index=True)
class Meta:
indexes = [
models.Index(fields=['email']),
models.Index(fields=['last_name', 'first_name']), # Составной
]Что проверять:
# ❌ N+1 запросов
posts = Post.objects.all() # 1 запрос
for post in posts:
print(post.author.name) # N запросов!
# ✅ select_related для ForeignKey
posts = Post.objects.select_related('author').all()
# 1 запрос с JOIN
# ✅ prefetch_related для ManyToMany
posts = Post.objects.prefetch_related('tags').all()
# 2 запроса: посты + теги# ❌ Неатомарно: может рассинхронизироваться
def transfer_money(from_acc, to_acc, amount):
from_acc.balance -= amount
from_acc.save()
to_acc.balance += amount # Может упасть здесь!
to_acc.save()
# from_acc списал, to_acc не получил
# ✅ Транзакция
from django.db import transaction
@transaction.atomic
def transfer_money(from_acc, to_acc, amount):
from_acc.balance -= amount
from_acc.save()
to_acc.balance += amount
to_acc.save()
# При ошибке — rollback всего# ✅ Явный уровень изоляции
@transaction.atomic(isolation_level='REPEATABLE READ')
def process_payment(order_id):
order = Order.objects.select_for_update().get(id=order_id)
# select_for_update блокирует строку до конца транзакции
order.process()Что проверять:
# ❌ Медленно: N INSERT запросов
for item in items:
OrderItem.objects.create(order=order, product=item)
# ✅ Bulk create: 1 INSERT
OrderItem.objects.bulk_create([
OrderItem(order=order, product=item)
for item in items
])# ❌ Медленно: N UPDATE запросов
for user in users:
user.is_active = True
user.save()
# ✅ Bulk update: 1 UPDATE
User.objects.filter(id__in=[u.id for u in users]).update(is_active=True)# ❌ Медленно: загрузка всех данных
total = sum(order.total for order in Order.objects.all())
# ✅ Агрегация в БД
from django.db.models import Sum
total = Order.objects.aggregate(Sum('total'))['total__sum']# ❌ Опасно: блокировка таблицы на большой таблице
class Migration(migrations.Migration):
operations = [
migrations.AddField(
'User',
'new_field',
field=models.CharField(default='', max_length=100),
# Блокирует таблицу при ALTER TABLE
),
]
# ✅ Безопасно для прода:
# 1. Добавить поле nullable
# 2. Заполнить фоном
# 3. Сделать NOT NULLЧто проверять:
Ключевая мысль: Ошибки с БД проявляются под нагрузкой. Проверяйте индексы, N+1, транзакции и bulk операции в каждом PR.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.