理解に苦しんでいる奇妙なクエリ パフォーマンスの問題に遭遇しました。
以下は、私が持っているモデル構造の簡略化されたバージョンです。うまくいけば、問題を説明するのに十分です。
class Note(models.Model):
...
name = models.CharField(max_length=50)
parentNote = models.ForeignKey('self', null=True)
form = models.ForeignKey('NoteForm', null=True)
...
class Event(Note):
...
startDate = models.DateField()
...
class Activity(Event):
...
Activity
モデルは、私が直面している問題の原因です。広範な継承階層があり、どれも抽象的ではありません。これが問題に寄与しているかどうかはわかりません。Activity
には ~280000 のレコードがあり、明らかに、その親にはそれ以上ではないにしても、少なくともそれだけの数のレコードがあります。
NoteForm モデルは上記では説明されていません。Activity
モデルの階層の外部にあり、含まれるレコードが 100 未満であることだけを知っておく必要があります。
Django バージョン 1.3 を使用しています。
この問題は、一部の親アクティビティの最新の「子」アクティビティを照会するときに発生します。クエリはフィールドでフィルター処理しparentNote
、「startDate」フィールド (降順) で並べ替え、Python のインデックス表記を使用して最初の結果を選択します (私の理解ではLIMIT 1
、生成された SQL に単純に追加されます)。コードについては、以下を参照してください。
結果が見つからない場合、このクエリの実行は予想外に遅くなります(10 秒以上)。結果が見つかった場合、期待どおりに実行されます - 1 秒未満です。
さらに調査した結果、次のことが明らかになりました。
- これが問題の原因となる制限です。最初の結果に限定せずにフィルタを実行するだけでは、結果が見つかるかどうかにかかわらず、遅くはありません。
- 注文は部分的に犯人です。順序を削除すると、問題が解決されます。
parentNote
フィルターは部分的に犯人です。form
またはname
フィールドを使用するようにフィルターを変更すると、問題が解消されます。
コード内:
# Original - SLOW
try:
latest = Activity.objects.filter(
parentNote=activity.pk
).order_by('-startDate')[0]
except IndexError:
latest = None
# FAST
# No limit
Activity.objects.filter(
parentNote=activity.pk
).order_by('-startDate')
# No ordering
try:
latest = Activity.objects.filter(
parentNote=activity.pk
)[0]
except IndexError:
latest = None
# Different filter
try:
latest = Activity.objects.filter(
form=activity.pk
).order_by('-startDate')[0]
except IndexError:
latest = None
# Different filter
try:
latest = Activity.objects.filter(
name=activity.pk
).order_by('-startDate')[0]
except IndexError:
latest = None
問題がデータベース レベルにある場合、私にはわかりません。django-debug-toolbar
上記の「 オリジナル」と「制限なし」の例を で実行しました debugsqlshell
。「オリジナル」は 16 秒、「ノー リミット」は 59 ミリ秒かかりました。によって出力された両方のクエリをコピー debugsqlshell
し、pgAdmin で実行しました。「オリジナル」は 1375 ミリ秒、「ノー リミット」は 94 ミリ秒かかりました。そのため、遅くなりますが、ORM を使用して見ているほどではありません。EXPLAIN ANALYZE
クエリアナライザーが異なるパスを取っていることを明確に示しています。これは完全に理解しています。しかし、SQL を直接使用して 16 秒のクエリを再現することはできません。
つまり、要約すると:
- LIMIT クエリは、LIMIT を使用しない同一のクエリよりもはるかに遅く実行されていますが、結果が見つからない場合に限られます。
- 結果を返すクエリの実行は遅くはなく、フィルターの値を除けば同じです。
- フィルターに含まれるフィールドと、クエリセットが順序付けられているかどうかの関数のようです。
- SQL を直接実行しても速度が遅くならないため、データベース レベルの問題ではないようです。
アップデート:
コメントで行われた提案を試している間、上記の例は突然この問題に悩まされなくなりました-原因に関する証拠を見つける前に、修正を実装することは言うまでもありません. 問題の原因はまだわかりませんが、さらに調査するために問題を再現する手段がありません。