Khắc phục vấn đề truy vấn N+1 trong Django

By khoanc, at: 10:52 Ngày 01 tháng 6 năm 2024

Thời gian đọc ước tính: __READING_TIME__ minutes

Tackling the N+1 Queries Problem in Django
Tackling the N+1 Queries Problem in Django

Là một nhà phát triển dày dặn kinh nghiệm, tôi hiểu sự khó chịu của vấn đề truy vấn N+1. Đó là kẻ giết hiệu năng âm thầm, nơi mã của bạn chạy một truy vấn cho một danh sách và sau đó là các truy vấn bổ sung cho mỗi mục trong danh sách đó. Hãy cùng tìm hiểu cách bạn có thể phát hiện và khắc phục điều này trong Django, giúp ứng dụng của bạn chạy mượt mà và nhanh hơn.

 

Hiểu về Truy vấn N+1

 

Hãy tưởng tượng bạn có một danh sách các phường, mỗi phường thuộc về một quận. Việc lấy dữ liệu phường và thông tin quận của chúng có thể trông như thế này:

 

wards = Ward.objects.all()
for ward in wards:
    name = ward.district.name


Điều này dẫn đến một truy vấn cho các phường và một truy vấn cho mỗi tên quận của phường, dẫn đến vấn đề N+1.

 

Phát hiện Truy vấn N+1

 

Các công cụ như Django Debug Toolbar, SentryScout APM có thể giúp phát hiện các vấn đề này.

Django Debug Toolbar, ví dụ, làm nổi bật các truy vấn quá mức trên các trang của bạn. Đội ngũ Glinteco của chúng tôi đã chỉ ra nhiều gói Django hữu ích trong bài viết này.

 

Giải quyết với select_related()

 

Đối với các mối quan hệ khóa ngoại, hãy sử dụng select_related() để thực hiện nối SQL và lấy các đối tượng liên quan trong một truy vấn duy nhất:

 

wards = Ward.objects.select_related('district').all()
for ward in wards:
    print(ward.district.name)


Điều này tải trước trường district cho mỗi phường, giảm số lượng truy vấn.

 

Giải quyết với prefetch_related()

 

Đối với các mối quan hệ nhiều-đối-nhiều và khóa ngoại ngược, prefetch_related() là người bạn của bạn. Nó thực hiện một tìm kiếm riêng biệt và thực hiện 'kết nối' trong Python:

 

districts = District.objects.prefetch_related('wards').all()
for district in district:
    wards = district.wards.all()


Điều này lấy tất cả các phường cho tất cả các quận trong hai truy vấn, bất kể số lượng quận.

 

Ví dụ thực tế

 

Giả sử bạn có các tác giả và sách của họ. Dưới đây là cách lấy chúng một cách hiệu quả:

 

authors = Author.objects.prefetch_related('book_set').all()
for author in authors:
    books = author.book_set.all()

 

Kết luận

 

Bằng cách sử dụng select_related()prefetch_related(), bạn có thể giảm thiểu vấn đề truy vấn N+1 và cải thiện đáng kể hiệu năng của ứng dụng Django. Hãy nhớ rằng, các công cụ như Django Debug Toolbar, Sentry, và Scout APM vô cùng quý giá trong việc phát hiện các vấn đề này.

Tag list:
- Django
- Django Queryset
- Django Manager
- Django Query
- Django N+1 Queries
- Django Query Optimization
- Query Optimization

Liên quan

Theo dõi

Theo dõi bản tin của chúng tôi và không bao giờ bỏ lỡ những tin tức mới nhất.