Лучшая база данных для Django: PostgreSQL, MySQL, SQLite — честное сравнение
Короткий ответ: PostgreSQL для продакшена, SQLite для локальной разработки и тестирования. MySQL — если уже есть готовая инфраструктура. Всё остальное — детали. Разберём почему.
Какие базы данных поддерживает Django
Django «из коробки» поддерживает четыре СУБД:
- PostgreSQL — рекомендуется как основной выбор
- MySQL / MariaDB — поддерживается, широко используется в legacy-проектах
- SQLite — default при
startproject, для разработки и тестирования - Oracle — для корпоративных legacy-систем
Через сторонние пакеты:
- Microsoft SQL Server — через
mssql-django - MongoDB — через
djongoилиmongoengine(неофициально, лучше не надо) - CockroachDB — совместим с PostgreSQL API
SQLite: с чего начинает каждый Django-проект
Когда вы создаёте Django-проект через django-admin startproject, в settings.py уже настроен SQLite:
# settings.py — Django default
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
Никаких зависимостей, никакой установки — просто файл db.sqlite3 в корне проекта.
Когда SQLite — правильный выбор
- Локальная разработка: быстрый старт, не нужен Docker или отдельный сервис
- Юнит-тесты: SQLite в памяти работает быстрее PostgreSQL
- Маленькие проекты: личный блог, портфолио, внутренний инструмент с 1-2 пользователями
- CI/CD pipeline: тесты без внешних зависимостей
# Быстрый in-memory SQLite для тестов
# settings_test.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
}
}
Ограничения SQLite в Django
SQLite не поддерживает:
django.contrib.postgres(ArrayField, JSONField до Django 3.1, HStoreField)- Одновременные записи из нескольких процессов (WAL-mode решает частично)
- Высокий конкурентный трафик
- Некоторые типы ALTER TABLE при миграциях
SQLite и Django в продакшене — рабочий вариант для небольших проектов. SQLite + WAL + litestream (репликация) — жизнеспособная архитектура для инди-проектов с умеренной нагрузкой.
PostgreSQL: стандарт для Django в продакшене
PostgreSQL — это первоклассная СУБД для Django. Команда Django разработчиков использует PostgreSQL как основную платформу, многие специфические фичи Django ORM реализованы только для PostgreSQL.
Установка и настройка
pip install psycopg2-binary # Для разработки
# или
pip install psycopg2 # Для продакшена (компилируется из C)
# Django 4.2+: psycopg v3 (быстрее)
pip install "psycopg[binary]"
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'myapp_db',
'USER': 'myapp_user',
'PASSWORD': 'secure_password',
'HOST': 'localhost',
'PORT': '5432',
'CONN_MAX_AGE': 60, # Пул соединений
'OPTIONS': {
'connect_timeout': 5,
}
}
}
Через переменную окружения (рекомендуется):
pip install dj-database-url
import dj_database_url
DATABASES = {
'default': dj_database_url.config(
default='postgresql://user:password@localhost:5432/myapp_db',
conn_max_age=600
)
}
PostgreSQL-специфичные возможности Django ORM
Именно ради этих фич выбирают PostgreSQL:
JSONField (нативный, с индексами)
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=200)
attributes = models.JSONField(default=dict) # Нативный JSONB в PostgreSQL
# Поиск по JSON-полю
Product.objects.filter(attributes__brand='Apple')
Product.objects.filter(attributes__ram__gte=16)
Product.objects.filter(attributes__tags__contains=['M3'])
ArrayField
from django.contrib.postgres.fields import ArrayField
class Article(models.Model):
title = models.CharField(max_length=200)
tags = ArrayField(models.CharField(max_length=50), default=list)
# Запросы
Article.objects.filter(tags__contains=['python'])
Article.objects.filter(tags__overlap=['django', 'postgresql'])
Article.objects.filter(tags__len__gte=3)
Full-text Search
from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank
# Полнотекстовый поиск с рангом
vector = SearchVector('title', weight='A') + SearchVector('content', weight='B')
query = SearchQuery('база данных')
results = Article.objects.annotate(
rank=SearchRank(vector, query)
).filter(rank__gte=0.1).order_by('-rank')
HStoreField (ключ-значение)
from django.contrib.postgres.fields import HStoreField
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
settings = HStoreField(default=dict)
profile.settings['theme'] = 'dark'
profile.save()
Window Functions
from django.db.models import Window, F
from django.db.models.functions import Rank
# Ранжирование пользователей по баллам
User.objects.annotate(
rank=Window(
expression=Rank(),
order_by=F('score').desc()
)
)
Constraint с исключениями (ExclusionConstraint)
from django.contrib.postgres.constraints import ExclusionConstraint
from django.contrib.postgres.fields import RangeOperators
# Запрет пересечения временных слотов (для бронирования)
class Booking(models.Model):
room = models.ForeignKey(Room, on_delete=models.CASCADE)
time_range = DateTimeRangeField()
class Meta:
constraints = [
ExclusionConstraint(
name='no_overlapping_bookings',
expressions=[
('room', RangeOperators.EQUAL),
('time_range', RangeOperators.OVERLAPS),
]
)
]
MySQL / MariaDB: для legacy и специфических требований
Настройка
pip install mysqlclient
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'myapp_db',
'USER': 'myapp_user',
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '3306',
'OPTIONS': {
'charset': 'utf8mb4',
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
}
}
}
Важные нюансы MySQL с Django
Всегда используйте utf8mb4, не utf8 — иначе нельзя хранить эмодзи:
CREATE DATABASE myapp_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
MySQL в режиме STRICT_TRANS_TABLES — обязательно. Без него Django-мигарции могут вести себя непредсказуемо.
Что не работает в MySQL (по сравнению с PostgreSQL)
ArrayField,HStoreField— нет поддержкиdjango.contrib.postgres.search— нет- Некоторые агрегатные функции и window functions ограничены
- JSON поддержка есть (MySQL 5.7+), но менее функциональна
- Ограничения длины для unique полей (767 байт на индекс)
Сравнительная таблица
| Критерий | PostgreSQL | MySQL/MariaDB | SQLite | |---|---|---|---| | Продакшен | Да | Да | Только малые проекты | | Django-фичи | Полный набор | Частичный | Минимальный | | JSONField | JSONB (нативный, быстрый) | JSON (медленнее) | Да (хранит как текст до Django 3.1) | | Full-text Search | Встроенный, мощный | Встроенный | Нет | | Параллельные записи | MVCC (отлично) | Хорошо | Ограниченно | | Транзакции | ACID | ACID | ACID | | Горизонтальное масштабирование | Через расширения | MySQL Cluster | Нет | | Лицензия | PostgreSQL (свободная) | GPL | Public Domain | | Российский хостинг | Везде | Везде | Везде | | Сложность настройки | Средняя | Средняя | Нет |
Workflow: SQLite локально, PostgreSQL в продакшене
Стандартная практика — разные настройки для разных окружений:
# settings/base.py
import os
import dj_database_url
# settings/development.py
from .base import *
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# settings/production.py
from .base import *
DATABASES = {
'default': dj_database_url.config(
default=os.environ.get('DATABASE_URL'),
conn_max_age=600,
ssl_require=True
)
}
Но Django ORM гарантирует: тот же Python-код работает с обеими базами. Разница только в настройках.
Пул соединений: важно для продакшена
По умолчанию Django открывает новое соединение при каждом запросе. В продакшене используйте пул:
pgBouncer (внешний пул для PostgreSQL)
# Устанавливается отдельно
apt-get install pgbouncer
# /etc/pgbouncer/pgbouncer.ini
[databases]
myapp_db = host=127.0.0.1 port=5432 dbname=myapp_db
[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20
# Подключение через pgBouncer
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'HOST': 'localhost',
'PORT': '6432', # Порт pgBouncer, не PostgreSQL
'NAME': 'myapp_db',
...
}
}
django-db-geventpool или Psycopg3
pip install "psycopg[binary,pool]"
# settings.py — пул через psycopg3
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'OPTIONS': {
'pool': {
'min_size': 2,
'max_size': 20,
'reconnect_timeout': 5,
}
}
}
}
Индексы и оптимизация Django ORM
class Article(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True) # Автоматический индекс
author = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
status = models.CharField(max_length=20, db_index=True)
class Meta:
indexes = [
# Составной индекс
models.Index(fields=['author', 'status'], name='author_status_idx'),
# Частичный индекс (только опубликованные)
models.Index(
fields=['created_at'],
name='published_articles_idx',
condition=models.Q(status='published')
),
]
Проблема N+1 запросов
# ПЛОХО: N+1 запрос для каждой статьи
articles = Article.objects.all()
for article in articles:
print(article.author.name) # Отдельный SELECT для каждой статьи!
# ХОРОШО: select_related для ForeignKey
articles = Article.objects.select_related('author').all()
# ХОРОШО: prefetch_related для ManyToMany
articles = Article.objects.prefetch_related('tags').all()
Бэкапы PostgreSQL для Django-проектов
# Быстрый бэкап перед деплоем
pg_dump -h localhost -U myapp_user -Fc myapp_db > backup_before_deploy.dump
# Восстановление
pg_restore -h localhost -U myapp_user -d myapp_db backup_before_deploy.dump
Автоматические бэкапы для Django с PostgreSQL — используйте dbsend.ru. Одна строка настройки, бэкап PostgreSQL уходит в облако (Яндекс.Диск, S3, Google Drive) по расписанию. Работает с любым Django-хостингом.
Рекомендации по типу проекта
Для прототипа и MVP:
- SQLite локально + PostgreSQL на Railway или Render.com
Для SaaS-приложения:
- PostgreSQL с pgBouncer, JSONB для гибких данных, pg_trgm для поиска
Для e-commerce:
- PostgreSQL, обязательно транзакции, возможно TimescaleDB для аналитики
Для контентного сайта (много чтения, мало записи):
- PostgreSQL + Redis для кэша, полнотекстовый поиск через PostgreSQL FTS или Elasticsearch
Для инди-проекта одного разработчика:
- SQLite + Litestream для репликации в S3 — дёшево и достаточно для начала
FAQ
Почему Django по умолчанию использует SQLite, если PostgreSQL лучше?
SQLite — нулевая настройка. Для начинающих это важно: python manage.py migrate работает сразу. PostgreSQL требует установки сервера и создания базы. Django выбирает простоту старта.
Можно ли мигрировать с SQLite на PostgreSQL?
Да, но с нюансами. Django ORM поддерживает оба, но схемы могут отличаться. Используйте python manage.py dumpdata > data.json, пересоздайте базу PostgreSQL, создайте схему через migrate, восстановите данные через loaddata.
MySQL или PostgreSQL для Django? PostgreSQL. Если нет конкретной причины использовать MySQL (legacy-система, требование корпоративного IT) — PostgreSQL предпочтительнее из-за полной поддержки Django-специфичных фич.
Как ускорить тесты в Django?
Используйте --keepdb для повторного использования тестовой базы, или SQLite в памяти для тестов. Отдельный settings_test.py с in-memory SQLite даст максимальную скорость.
Django работает с несколькими базами данных?
Да. Django поддерживает multiple databases через DATABASES словарь с несколькими ключами и database routers. Полезно для разделения read/write replicas или шардирования.
Как настроить read replica в Django?
Через DATABASE_ROUTERS. Создайте router-класс, который направляет SELECT на реплику, а INSERT/UPDATE/DELETE — на основной сервер. Подробнее в документации Django.
Нужен ли Redis вместе с PostgreSQL для Django? Redis — это кэш, очередь задач (Celery) и сессии. PostgreSQL — основное хранилище данных. Они не конкурируют — используются вместе. Типичный продакшен-стек: Django + PostgreSQL + Redis + Celery.