2015-10-14 44 views
1

我有一個Django 1.8應用程序,我使用的是MSSQL數據庫,用pyodbc爲DB後端(使用「Django的pyodbc-蔚藍」模塊)。Django的性能問題排除

我有以下型號:

class Branch(models.Model): 
    name = models.CharField(max_length=30) 
    startTime = models.DateTimeField() 

class Device(models.Model): 
    uid = models.CharField(max_length=100, primary_key=True) 
    type = models.CharField(max_length=20) 
    firstSeen = models.DateTimeField() 
    lastSeen = models.DateTimeField() 

class Session(models.Model): 
    device = models.ForeignKey(Device) 
    branch = models.ForeignKey(Branch) 
    start = models.DateTimeField() 
    end = models.DateTimeField(null=True, blank=True) 

我需要查詢會話模式,我要排除一些記錄與特定設備的值。因此,我發出以下查詢:

sessionCount = Session.objects.filter(branch=branch) 
          .exclude(device__in=badDevices)            
          .filter(end__gte=F('start')+timedelta(minutes=30)).count() 

badDevices是一個預先填充的約60個項目的設備ID列表。

badDevices = ['id-1', 'id-2', ...] 

該查詢大約需要1.5秒才能完成。如果我從查詢中刪除排除,則需要大約250毫秒。

我打印了這個查詢集的生成的sql,並在我的數據庫客戶端嘗試了它。在那裏,兩個版本在大約250毫秒內執行。

這是生成的SQL:

SELECT [session].[id], [session].[device_id], [session].[branch_id], [session].[start], [session].[end] 
FROM [session] 
WHERE ([session].[branch_id] = my-branch-id AND 
NOT ([session].[device_id] IN ('id-1', 'id-2', 'id-3',...)) AND 
DATEPART(dw, [session].[start]) = 1 
AND [session].[end] IS NOT NULL AND 
[session].[end] >= ((DATEADD(second, 600, CAST([session].[start] AS datetime))))) 

因此,使用在數據庫級別排除似乎並沒有被影響的查詢性能,但在Django,查詢運行較慢的6倍,如果我加入排除部分。什麼可能導致這個?

+0

您可能會指出您正在使用的數據庫後端。 (即pyodbc實現或Django-mssql) – Ringil

+0

@Ringil感謝您的建議,使用該信息更新了問題 –

回答

2

一般的問題似乎是,Django是做一些額外的工作來準備exclude條款。在這一步之後,當SQL產生併發送到數據庫時,django端沒有任何有趣的事情發生,這可能導致如此嚴重的延遲。

在你的情況,一件事,可能會引起這是某種badDevices預處理。例如,如果badDevicesQuerySet,那麼django可能正在執行badDevices查詢來準備實際查詢的SQL。在device具有非默認主鍵的情況下,可能有類似的情況發生。

另一件可能延遲SQL準備當然是django-pyodbc-azure。在編譯查詢時它可能會做一些奇怪的事情,並且變成瓶頸。

儘管如此,如果你仍然遇到這個問題,那麼還應該發佈DeviceBranch模型以及badDevices的確切內容以及查詢生成的SQL。那麼也許有些場景至少可以消除。

編輯:我認爲它一定是Device.uid字段。可能django或pyodbc會被非默認主鍵弄糊塗,並在生成查詢的同時獲取所有設備。要做兩件事情:

  • 更換device__indevice_id__indevice__pk__indevice__uid__in,並再次檢查各一個。也許一個更明確的查詢將更容易django翻譯成SQL。以防萬一,您甚至可以用branch_id替換branch

  • 如果上述不行,請嘗試使用原始的SQL替換排除表達where子句:

    # add quotes (because of the hyphens) & join 
    badDevicesIdString = ", ".join(["'%s'" % id for id in badDevices]) 
    
    # Replaces .exclude() 
    ... .extra(where=['device_id NOT IN (%s)' % badDevicesIdString]) 
    

如果沒有工作,那麼最有可能的問題是整個查詢和不只是exclude。在這種情況下還有更多的選擇,但嘗試上面的第一個,如果有必要,我會稍後更新我的答案。

+0

badDevices是一個python列表,而不是查詢集合,因此很可能不會影響查詢性能。設備具有非默認主鍵,這將如何影響查詢性能?我編輯了這個問題來添加其他模型和生成的sql。 –

+0

@OzgurAkcali查看更新的答案 – gbs

+0

替換device__in條款與您的建議沒有什麼區別,但你的第二個建議解決了問題,查詢的性能是現在應該是,謝謝。任何想法,爲什麼這有效? –