2014-06-19 42 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