2014-06-19 41 views
0

因此,我在SQL Server 2008 R2中有一個查詢(我用它作爲存儲過程)。 它的工作原理,但我不能相信沒有更有效的方法。SQL查詢需要改進的性能(1個表)

數據在表'ServiceInstance'中。這是一個完全平坦的表,每個IPAddress包含'實例',它們中的每一個都具有用於該IP地址的唯一TCP端口。

列'isRestarting'和'isInuse'對這個系統並不重要; '二者isRestarting' 或 'isInUse' 是中, 'IsAvailable' 是當

列 'CPUID' 是;每個服務器有4個CPU - 並且我運行的Delhpi應用程序只能同時在一個CPU上有1個應用程序。因此,如果服務器上的IP爲'192.168.4.151'的CPU#1正在使用,則該IP上的該CPUId不允許從查詢返回。 (有4個核心一臺服務器上16個實例)

The table with data

因此,SP必須做到以下幾點:

獲取可用的情況下,它必須符合:

  • SP應該總是返回1行。
    • 這是一種低PRIO我大氣壓 - 如果需要,
  • 的ServerInstance我可以重新運行SP「的IsEnabled必須是真實的
  • 當返回ServerInstance,它必須被設置爲」 IsAvailable」 =假,所以它不會再次,直到工作完成(復位由我的應用程序邏輯進行)
  • 沒有對IP相同的CPUID必須拾起‘IsAvailable =假’
    • 這可確保該服務器上的CPU#空閒
  • 尚未使用時間最長的ServerInstance是首選。
    • 因爲我有一個大水池,並希望實現流量負載均衡
  • 在「發現」 ServerInstance要立即更新。
    • 的 'LastRequestDate' 要加蓋
    • 的 'IsInuse' 設置爲true
    • 的 'IsAvailable' 設置爲

所以。有了這些信息,我創造了這個怪物:

UPDATE top(1) ServiceInstance 
    SET 
     LastRequestDate=GETDATE() 
     ,IsInUse=1 
     ,IsAvailable=0 
    OUTPUT 
     inserted.ServiceInstanceId, 
     inserted.IpAddress, 
     inserted.TcpPort, 
     inserted.LastRequestDate, 
     inserted.IsInUse 
    WHERE 
    ServiceInstanceId IN 
    (
    SELECT Top (1) ServiceInstanceId FROM ServiceInstance 
    WHERE 
    (ServiceInstance.IsAvailable = 1 AND ServiceInstance.IsEnabled = 1) 
    AND ServiceInstanceId NOT IN 
     (
     SELECT NGI1.ServiceInstanceId 
     FROM 
      (SELECT CpuId,IpAddress 
      FROM [ServiceInstance] NGI 
      WHERE IsInUse=1) a 
      INNER JOIN ServiceInstance AS NGI1 ON a.IpAddress = NGI1.IpAddress AND a.CpuId = NGI1.CpuId 
     ) 
    ORDER BY LastRequestDate ASC 
    ) 

但是,我覺得這不可能是最有效的方式去做事情。 這個查詢應該在peek小時每秒運行〜10次,因此目前會給我的SQL Server帶來一些沉重的CPU壓力。

歡迎任何提示!我覺得我應該能夠使用PARTITION OVER或加入到我自己的桌子,但我似乎無法成功創建它...!

好了,所以,表結構如下:

  • ServiceInstanceId INT NOT NULL
  • IPAddres VARCHAR(20)NOT NULL
  • 的TCPPort VARCHAR(5)NOT NULL
  • LastRequestDate日期時間NOT NULL
  • IsEnabled BIT NOT NULL
  • IsAvailable BIT NOT NULL
  • IsRestarting BIT NOT NULL
  • IsInuse BIT NOT NULL
  • CPU INT NOT NULL

在這一刻,我沒有索引。這是因爲表被每一個ServerInstance是 '使用' 3或4次 (1 = 使用,使用後2 = 重啓表變異時間突變很多(,3 =設定IsAvailable,4 = 重新啓動失敗) 我的猜測是,如果我做索引,這將有充分的突變進行更新不知道,但我覺得這會降低性能:)

Exec的計劃: Execution plan for my query:

重要的一些loadtests後添加:

我真的需要使用Exec @RC =sp_getapplock @Resource='MyLock', @LockMode='Exclusive', @LockOwner='Transaction', @LockTimeout = 1000對於這個StoredProcedure。如果沒有它,效果不好!

+0

您的查詢中有錯誤:SET IsAvailable = 0 - 必須爲1,如果您想'IsAvailable'設置爲True – MikkaRin

+0

不,只要從服務器中取出'ServerInstance',它就不可用。我的應用程序邏輯將在完成時將其設置爲'可用':)。編輯:ive用這個信息更新我的文章,謝謝 – Samjongenelen

+2

是不是'[NhgDocV40]。[dbo]。[ServiceInstance]'與ServiceInstance'不同?在第二個情節中,你的意思是16個實例是對的?而在最後一顆子彈中,你的意思是''IsAvailable'設置爲False'? – NickyvV

回答

1
WITH UnAvailableCpus AS (
    SELECT IpAddress 
      ,CpuId 
    FROM ServiceInstance 
    WHERE IsAvailable = 0 
    GROUP BY IpAddress 
      ,CpuId 
) 
,AvailableInstances AS (
    SELECT ServiceInstanceId 
      ,LastRequestDate 
    FROM ServiceInstance 
     LEFT JOIN UnavailableCpus 
      ON UnavailableCpus.IpAddress = ServiceInstance.IpAddress 
       AND UnavailableCpus.CpuId = ServiceInstance.CpuId 
    WHERE ServiceInstance.IsAvailable = 1 
      AND ServiceInstance.IsEnabled = 1 
      AND UnavailableCpus.IpAddress IS NULL 
) 
,PreferredInstance AS (
    SELECT TOP 1 ServiceInstanceId 
    FROM AvailableInstances 
    ORDER BY LastRequestDate 
) 
UPDATE ServiceInstance 
SET 
    LastRequestDate=GETDATE() 
    ,IsInUse=1 
    ,IsAvailable=0 
OUTPUT 
    inserted.ServiceInstanceId 
    ,inserted.IpAddress 
    ,inserted.TcpPort 
    ,inserted.LastRequestDate 
    ,inserted.IsInUse 
WHERE ServiceInstanceId IN (SELECT ServiceInstanceId FROM PreferredInstance) 
+0

我一直在運行這個T SQL的第一個測試就像一個魅力!我應該索引一列,當我有36個IpAddresses(36 * 16 = 576記錄)? – Samjongenelen

+0

576記錄應該只有3-4頁。不要認爲索引會有任何顯着差異 – adrianm

+0

重要附加: – Samjongenelen

0

嘗試此查詢,以取代您的檢索查詢:

select top 1 tbl.serviceinstanceid from 
(select 
ipaddress, 
cpuid, 
sum(case when isavailable = 1 then 1 else 0 end) 
over (partition by ipaddress, cpuid order by ipaddress, cpuid) s 
from tbl 
where isenabled = 1 
) t 
inner join serviceinstance tbl on tbl.ipaddress = t.ipaddress and tbl.cpuid = t.cpuid 
where t.s = 4 
and tbl.isinuse = 0 
order by lastrequestdate 

假設:4場與1個0值bit類型。

擊穿:

  • 總和是用來檢查有多少每個IP地址的4個CPUIDs可供選擇。我們只對那些sum = 4的人感興趣。
  • 所有必須啓用。
  • 選擇時不能使用。
  • 必須是最後一次請求日期的最小值。
+0

你是對的,我更新了帖子 – Samjongenelen

+1

沒問題。我的查詢是否檢索您期望的結果?我不是很清楚這個要求。 –

1

它可以與表的結構(如果你將它張貼我們可以進一步闡述)有關。

最好的猜測是爲用於連接任何列創建索引,如果沒有這樣做:

  • CPUID
  • Ip地址
  • IsInUse

也不會差到哪創建索引用於何處的條件:

  • IsAvailable
  • 的IsEnabled

無論如何,這是你目前是一個「規劃」算法的像一個分佈式操作系統的流程執行。這種算法總是被編程爲在內存中保存所有信息,並且計劃的執行(選擇哪個執行什麼以及哪裏執行)被編譯爲本地代碼。例如,在Linux內核的情況下,在C++中。如果您將信息委託給數據庫引擎,並且規劃爲三層嵌套的SQL,則性能將變差。數據庫更適合於處理大量的結果信息,或者爲大量客戶同時查詢信息,或者快速記錄和存儲大量數據。

也許你應該嘗試在Delphi中使用面向對象的方法編程所有東西,而不使用數據庫和SQL。

+0

嗯,這個SUM()是一個不錯的方法。我會嘗試和玩 – Samjongenelen

+0

我完全同意。不幸的是,這個代碼是在webfarm中實現的。服務器場的節點1不知道節點2的內存和表狀態。感謝您的信息,但我喜歡瞭解這些過程 – Samjongenelen

+0

好的,但所有節點都依賴於中央治理節點:數據庫。也許你可以製作自己的程序來控制它們作爲中心節點。集中式和分散式的多節點操作系統無論如何都是一個複雜的問題,這將是很多簡單使用數據庫的工作。也許在分佈式操作系統上尋找不同的設計方法:http://en.wikipedia.org/wiki/Distributed_operating_system#Distributed_computing_models – vicenteherrera