一度のdjangoメモリ異常チェック

5061 ワード

の原因となるDjangoPythonとして有名なWebのフレームワークとして、多くの人が使っていると信じています.自分の仕事の中にもプロジェクトが使っていますが、ここ数日の使用の中で、Djangoプログラムを導入したサーバーにメモリの問題が発生していることがわかりました.現象は、しばらく実行した後、メモリの占有量が非常に高く、最終的にサーバーのメモリを消費することです.Python項目にメモリの問題が発生した場合、自分は前に一度処理したことがあるので、初めて解決した時の慌ただしさはなく、自分の前に解決方法もブログにまとめました.https://www.cnblogs.com/zhaof/p/10031945.html
しかし、私が考えているほど簡単ではないようで、自分で前の方法tracemallocライブラリで問題の調査を試みましたが、問題は実際のプロジェクトの中で100以上のインタフェースが来て、どのように調査しますか?1つ1つのインタフェースでテストチェックを行いますが、時間が緊急で、間に合わないかもしれません.前回より自分で解決したのは前回のプロジェクトが簡単で、相対的に位置決めの問題が簡単だったからですが、今回はどう処理しますか?
プロセス
一般的なPythonプロジェクトはメモリの問題が少なく、一般的には自分のコードが書かれていることに問題がありますが、今回の問題については、自分の調査の考え方(webインタフェースタイプのプロジェクトについて):
  • 呼び出しの比較的頻繁なインタフェース
  • を先にチェックする.
  • データ要約インタフェース(クエリが複雑)
  • 上記がまだ検出されていない場合、残りのインタフェース
  • を再確認する.
    今回の問題チェックでは,自分も大体この考え方で行い,頻繁に呼び出されるインタフェースをチェックしてもメモリの異常は認められず,メモリの問題はデータがまとめられた関連インタフェースにある.
    実はこのインタフェースは初級開発に対して問題が発生しやすい場所であり、まずこのインタフェースのクエリーのデータは他のインタフェースに比べて複雑であり、符号化の基礎が特によくなければ、これらのインタフェースにbugが発生する可能性がある.
    今回の調査では、最終的にデータをまとめるインタフェースにおいて、Django ORMの使用が不適切であることが問題となっていることが判明した.簡単なコードの例で説明します.
    class Student(models.Model):
        name = models.CharField(max_length=20)
        name2 = models.CharField(max_length=20)
        name3 = models.CharField(max_length=20)
        name4 = models.CharField(max_length=20)
        name5 = models.CharField(max_length=20)
        name6 = models.CharField(max_length=20)
        name7 = models.CharField(max_length=20)
        name8 = models.CharField(max_length=20)
        name9 = models.CharField(max_length=20)
        name10 = models.CharField(max_length=20)
        name11 = models.CharField(max_length=20)
        name12 = models.CharField(max_length=20)
        name13 = models.CharField(max_length=20)
        name14 = models.CharField(max_length=20)
        name15 = models.CharField(max_length=20)
        age = models.IntegerField(default=0)
    

    通常、私たちのテーブルフィールドは比較的多く、ここでは複数のnameでシミュレーションし、問題が発生したコードはこのテーブルに関するインタフェースに表示されます.
    def index(request):
        studets = Student.objects.filter(age__gt=20)
        if studets:
            pass
        return HttpResponse("test memory")
    

    メモリの問題を再現しやすいように、スクリプトを使用してStudioに20000個のデータを挿入しました.もちろん、ここではデータが多ければ多いほど、問題が明らかになります.
    テストスクリプトがこのインタフェースを同時に要求することで、メモリの状況を観察すると、メモリが瞬時に上昇し、データが多ければ多いほど、要求が多ければ多いほど、メモリがしばらく高くなり、徐々に上昇する可能性があります.問題はどこですか.
    実はとても简単で、问题はコードの中のif判断のところで出て、私达はfilterクエリを通じて(通って)返したのはQuerySetタイプのデータで、私达がフィルタリングした后のデータはとても多い时が存在するかもしれなくて、この时私达はifを通じて直接判断して、自分の理解のこの地方は全体のQuerySetをメモリの中にロードすることができて、それによってメモリの占有量が高すぎる问题が発生して、この場合、このインタフェースの応答速度も非常に遅くなりますが、このQuerySetのデータが多ければ多いほど、メモリの消費量が明らかになります.Djangoの文書で実際に説明しました
  • exists ()¶

  • Returns True if the QuerySet contains any results, and False if not. This tries to perform the query in the simplest and fastest way possible, but it does execute nearly the same query as a normal QuerySet query. exists() is useful for searches relating to both object membership in a QuerySet and to the existence of any objects in a QuerySet , particularly in the context of a large QuerySet .
    The most efficient method of finding whether a model with a unique field (e.g. primary_key ) is a member of a QuerySet is:
    entry = Entry.objects.get(pk=123)
    if some_queryset.filter(pk=entry.pk).exists():
        print("Entry contained in queryset")
    

    Which will be faster than the following which requires evaluating and iterating through the entire queryset:
    if entry in some_queryset:
       print("Entry contained in QuerySet")
    

    And to find whether a queryset contains any items:
    if some_queryset.exists():
        print("There is at least one object in some_queryset")
    

    Which will be faster than:
    if some_queryset:
        print("There is at least one object in some_queryset")
    

    … but not by a large degree (hence needing a large queryset for efficiency gains).
    Additionally, if a some_queryset has not yet been evaluated, but you know that it will be at some point, then using some_queryset.exists() will do more overall work (one query for the existence check plus an extra one to later retrieve the results) than using bool(some_queryset) , which retrieves the results and then checks if any were returned.
    だから私たちのコードについてif判断場所をif not studets.exists()に変更するだけで問題を解決することができます.
    これは小さな知識ですが、使用が間違っていると、メモリの問題が深刻になる可能性があります.
    まとめ
  • ユニットテストに加えて、ビッグデータ量テストも必要です.今回の問題はテストの時に一定のデータ量のテストをしたことがあれば、早く問題を発見できるかもしれません.
  • ベースのライブラリの使用については、
  • をより熟知する必要があります.
  • 問題を調査する構想は明確にしなければならない.そうしないと、
  • 手がつけられないかもしれない.
    拡張読書
  • https://docs.djangoproject.com/en/3.0/ref/models/querysets/
  • https://www.cnblogs.com/zhaof/p/10031945.html