Django Restframework性能优化的选择
在用DRF做后端开发时,在数据量一增多后就会发现api的返回实在是慢,装了django-debug-toolbar来检查行为表现以确定瓶颈时,有如下发现:
1. DB Query时间正常,但是有重复的单个sql比如分别以id来查询多次,于是多出来的查询会增加时间。
2. ModelSerializer是真的慢。在view中的queryset是惰性计算,并不会实际操作,而在serializer序列化数据时,则发现执行了比较多的sql查询,因为在重写to_representation时取了外键的数据。
因此可以通过如下变化来优化后端性能表现:
1. 除非需要,尽量不要增加nested_serializer。比如Book里有Author这个外键,但是不一定需要把AuthorSerializer的数据也展示出来,这样会造成额外的sql。在没有额外要求下,只序列化当前model里的数据。比如这里面的:https://www.django-rest-framework.org/api-guide/relations/#nested-relationships
2. 当有需要序列化foreignkey或者manytomany的字段的数据时,考虑使用prefetch_related和select_related来优化sql查询。比如如果不用这两种,实际情况是取了一个Publisher,但是又额外执行了多次取通过publisher_id来取book的sql。这篇是不错的说明:http://ses4j.github.io/2015/11/23/optimizing-slow-django-rest-framework-performance/
select_realted可用于OneToOneFiled, ForeignKey字段,相当于sql中使用INNER JOIN,这样可以把publisher表和book表联合起来查询一次,避免多余sql查询,避免多个多余的以id查询的sql;
prefetch_related可用于ManyToManyField, OneToManyField(即ForeignKey的反向查询),它优化的方式是把以id分别查询的sql放在一起变成了id in (1, 2, 3)这种,也避免了多余sql查询。
3. 当业务逻辑和数据模型确实没有可优化的地方了,还可以用缓存。可参考https://www.django-rest-framework.org/api-guide/caching/
比如可以用下面的方式:
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie
class AuthorDetail(generics.RetrieveUpdateAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
@method_decorator(cache_page(60 * 60 * 2)) # Cache requested url for each user for 2 hours
@method_decorator(vary_on_cookie)
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)