18

私は自分の API に Django REST フレームワークを使用しています。昨日、それが大規模なデータに対してどのように機能するかを確認したかったのです。リクエストのプロファイリング方法に関するこのチュートリアル(Tom Christie 著) を見つけたところ、10,000 人のユーザーの場合、リクエストに驚くほど 2:20 分かかっていることがわかりました。

ほとんどの時間はオブジェクトのシリアル化 (約 65%) に費やされていたので、高速化するにはどうすればよいでしょうか?

私のユーザーモデルは実際にはデフォルトのdjangoモデルを拡張しているため、ネストされたモデルも取得していないため、.values()の使用は機能しません(はるかに高速ですが)。

どんな助けでも大歓迎です:)

編集

クエリセットを取得するときに .select_related() を既に使用しており、時間は短縮されましたが、数秒しか短縮されませんでした。合計クエリ数は 10 であるため、データベース アクセスに問題はありません。

また、このリクエストで不要なフィールドを避けるために、.defer() を使用しています。これもわずかな改善をもたらしましたが、十分ではありませんでした。

編集#2

Models

from django.contrib.auth.models import User
from django.db.models import OneToOneField
from django.db.models import ForeignKey

from userena.models import UserenaLanguageBaseProfile
from django_extensions.db.fields import CreationDateTimeField
from django_extensions.db.fields import ModificationDateTimeField

from mycompany.models import MyCompany


class UserProfile(UserenaLanguageBaseProfile):
    user = OneToOneField(User, related_name='user_profile')
    company = ForeignKey(MyCompany)
    created = CreationDateTimeField(_('created'))
    modified = ModificationDateTimeField(_('modified'))

Serializers

from django.contrib.auth.models import User

from rest_framework import serializers

from accounts.models import UserProfile


class UserSerializer(serializers.ModelSerializer):
    last_login = serializers.ReadOnlyField()
    date_joined = serializers.ReadOnlyField()
    is_active = serializers.ReadOnlyField()

    class Meta:
        model = User
        fields = (
            'id',
            'last_login',
            'username',
            'first_name',
            'last_name',
            'email',
            'is_active',
            'date_joined',
        )


class UserProfileSerializer(serializers.ModelSerializer):
    user = UserSerializer()

    class Meta:
        model = UserProfile
        fields = (
            'id',
            'user',
            'mugshot',
            'language',
        )

Views

class UserProfileList(generics.GenericAPIView,
                      mixins.ListModelMixin,
                      mixins.CreateModelMixin):

    serializer_class = UserProfileSerializer
    permission_classes = (UserPermissions, )

    def get_queryset(self):
        company = self.request.user.user_profile.company
        return UserProfile.objects.select_related().filter(company=company)

    @etag(etag_func=UserListKeyConstructor())
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)
4

3 に答える 3

12

ほとんどの場合、パフォーマンスの問題は N+1 クエリから発生します。これは通常、関連するモデルを参照しており、情報を取得するためにオブジェクトごとのリレーションシップごとに 1 つのクエリが生成されるためです。私の他のスタックオーバーフローの回答で説明されているように、メソッドで.select_relatedandを使用することでこれを改善できます。.prefetch_relatedget_queryset

Django がデータベースの最適化に関して提供しているのと同じヒントがDjango REST フレームワークにも当てはまるので、それらも調べることをお勧めします。

シリアル化中にパフォーマンスの問題が発生する理由は、Django がデータベースに対してクエリを実行するためです。

于 2015-03-12T18:07:17.867 に答える
8

私はこれが古いことを知っており、おそらくあなたの問題はすでに解決しています...しかし、この記事にたどり着いた他の人にとっては...

問題はあなたがブラインドをしていることです

select_related()

パラメータなしで、クエリに対してまったく何もしません。あなたが本当にしなければならないことは、

prefetch_related('user_profile')

詳細は省きますが、select_relatedは「対 1」の関係用であり、prefetch_relatedは「対多」の関係用です。あなたの場合、「to many」クエリで ある逆の関係を使用しています。

もう 1 つの問題は、逆の関係を正しく使用していなかったことです。シリアライザーの get_queryset() をこれに変更すると、必要なものが得られると思います。

def get_queryset(self):
    return UserProfile.objects.prefetch_related('user_profile').all()
于 2016-12-07T18:47:35.547 に答える