Zurück zur Startseite

Django-Optimierung: von 30s auf 142ms

Django-Monolith-Optimierungsfall: Eliminierung von N+1, Hinzufügen von Indizes und Implementierung von DDD reduzierte die Report-Zeit von 30 s auf 142 ms. DB-CPU sank um 60%, Abfragen — von 2800 auf 3. Use-Cases-Struktur beschleunigte die Feature-Entwicklung.

Wie man Django-Reports 200-mal beschleunigt: Fall
Advertisement 728x90

Optimierung eines Django-Monolithen: Von 30 Sekunden auf 142 ms bei Berichten

In einem veralteten Django-Projekt benötigten Filialberichte 30 Sekunden zum Laden, verursacht durch einen vollständigen sequenziellen Scan der Bestellungstabelle mit 280.000 Datensätzen. EXPLAIN ANALYZE zeigte:

Seq Scan on orders (cost=0.00..18420.00 rows=2841 width=847)
Filter: (branch_id = 42)
Rows Removed by Filter: 284100
Execution Time: 28340 ms

Das ORM generierte über 2800 Abfragen aufgrund von N+1 bei verknüpften Objekten. Geschäftslogik in Controllern verstärkte die Last.

Ursprüngliche Abfrage:

Google AdInline article slot
SELECT * FROM orders
JOIN order_items ON orders.id = order_items.order_id
JOIN products ON order_items.product_id = products.id
WHERE orders.branch_id = 42;

Beseitigung von N+1

N+1 verursachte exponentielles Lastwachstum. Vorher:

orders = Order.objects.filter(branch_id=branch_id)
for order in orders:
    items = order.order_items.all()  # N+1
    for item in items:
        product = item.product  # N+1

Nachher mit Prefetch:

orders = Order.objects.filter(
    branch_id=branch_id
).select_related('customer').prefetch_related('order_items__product')

Abfrageanzahl sank auf 3. select_related nutzt JOIN für ForeignKey, prefetch_related verwendet IN-Abfragen für ManyToMany und Reverse-Relations.

Google AdInline article slot

Indizierung von Tabellen

Nach N+1 blieb der sequenzielle Scan. Indizes wurden ohne Downtime hinzugefügt:

CREATE INDEX CONCURRENTLY idx_orders_branch_created ON orders(branch_id, created_at DESC);
CREATE INDEX CONCURRENTLY idx_products_search ON products USING GIN(to_tsvector('english', name));

Neuer Plan:

Index Scan using idx_orders_branch_created on orders
Index Cond: (branch_id = 42)
Execution Time: 142 ms

Ausführungszeit sank von 28 Sekunden auf 142 ms. CONCURRENTLY verhindert Tabellensperren im Produktivbetrieb.

Google AdInline article slot

Wichtige Indizierungsempfehlungen:

  • Zusammengesetzte Indizes auf (Filter + Sortierung)
  • GIN für Volltextsuche
  • Immer EXPLAIN ANALYZE vor dem Hinzufügen prüfen

Refactoring zu DDD

Geschäftslogik in Controllern erzeugte technische Schulden. Domain und Use Cases wurden unabhängig vom Framework extrahiert.

Beispiel-Use-Case:

class GetBranchReportUseCase:
    def __init__(self, repo: OrderRepository):
        self._repo = repo

    def execute(self, branch_id, period) -> BranchReport:
        orders = self._repo.get_by_branch_and_period(branch_id, period)
        return BranchReport.from_orders(orders)

View wurde schlank:

class BranchReportView(APIView):
    def get(self, request, branch_id):
        use_case = GetBranchReportUseCase(DjangoOrderRepository())
        report = use_case.execute(branch_id, DateRange.from_request(request))
        return Response(BranchReportSerializer(report).data)

Use Cases werden mit Mock-Repositories ohne Django und Datenbank getestet. Time-to-Market für neue Features halbierte sich.

Code-Qualitätskontrolle

Hinzugefügt:

  • Mypy strict mode:
[mypy]
strict = true
disallow_untyped_defs = true
warn_return_any = true
  • Pytest mit Coverage ≥87%, blockierende Quality Gate in GitLab CI.
  • MTTD sank um 40% durch frühe Fehlererkennung.

Verbesserungsmetriken

| Metrik | Vorher | Nachher |

|--------|--------|---------|

| Berichtszeit | 30 s | 1,5 s |

| DB CPU | 80% | 32% |

| Abfragen/Seite | 2800+ | 3 |

| Feature TTM | X | X/2 |

| MTTD | - | -40% |

Wichtige Erkenntnisse

  • EXPLAIN ANALYZE ist der erste Schritt bei Performance-Problemen
  • N+1 zerstört Performance unter Last, prefetch_related/select_related sind Pflicht
  • Zusammengesetzte Indizes auf (branch_id, created_at) bringen 200-fache Beschleunigung
  • DDD isoliert Geschäftslogik, beschleunigt Entwicklung und Tests
  • Mypy + pytest + CI Gate reduzieren MTTD ohne Overhead

— Editorial Team

Advertisement 728x90

Weiterlesen