Лучшая база данных для Django в 2026 — сравнение PostgreSQL, MySQL, SQLite

Лучшая база данных для 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.