【发布时间】:2021-09-22 02:59:29
【问题描述】:
我正在监视一个系统,该系统将事件接收到队列中并按顺序/顺序/一次处理它们。这些事件由连续运行(一年 365 天、每天 24 天、每天 24 天)的物理设备生成。个别设备可能会因维护而下线,但该过程不会停止。
系统提供商正在对其进行更新,我正在尝试使用 2 个指标来分析一段时间内的性能:
- 事件在队列中停留的时间平均长度
- 每日平均队列长度(数据点是添加新事件时队列的长度)
一个事件以“活动”状态到达队列,如果成功处理,处理器将事件设置为“完成”,如果在 5 次重试尝试后失败,则将事件设置为“无效”。只有在一个事件被标记为“完成”或“无效”之后,处理器才会移动到下一个事件。该过程的结果是更新其他系统使用的事实表。
我们在 SQL Server 的表中记录了以下内容:
- 事件的 ID,生成为连续整数。逻辑上(但实际上不是)这就像
IDENTITY(1,1) - 事件的当前状态,外键,只能是:Active、Completed 或 Invalid
- 事件生成的日期和时间
- 状态设置为“已完成”或“无效”的日期和时间
- 重试尝试次数(详细信息或失败原因存储在其他位置)
日志表如下所示:
CREATE TABLE EventLog (
Id int NOT NULL,
StateId int NOT NULL,
Generated datetime NULL,
Modified datetime NOT NULL,
Retries int NULL,
PRIMARY KEY CLUSTERED ( Id ASC ))
我还有两个日期和 stateId 的索引
CREATE NONCLUSTERED INDEX EventLog_DatesState ON EventLog
(
Generated ASC,
Modified ASC,
StateId ASC
)
我可以使用DATEDIFF(SECOND, Generated, Modified )/60.0 来计算事件在队列中花费的时间(以分钟为单位)。然后我可以平均每天的数据,看看它是如何随时间变化的。
计算一天的平均排队长度可能很困难。要在入队时获取我尝试过的项目的队列长度:
WITH EventDuration (SELECT
CAST(Generated AS Date) GeneratedDate
, DATEDIFF(SECOND, Generated, Modified)/60.0 TimeMinutes
, (
SELECT COUNT(*)
FROM EventLog sub
WHERE EventLog.Generated between sub.Generated and sub.Modified
AND StateId != @ActiveState
) queueLength
FROM EventLog
WHERE StateId != @ActiveState)
SELECT
GeneratedDate
, AVG(TimeMinutes) AvgTime
, AVG(QueueLength) AvgLength
, COUNT(*) Count
FROM EventDuration
GROUP BY GeneratedDate
ORDER BY GeneratedDate DESC
但是自联接/子查询需要从亚秒查询到我在 >10 分钟后放弃。
我的表中有大约 175000 行。有什么推荐的方法吗?
执行计划显示查询的昂贵部分是:
- 索引线轴 (51%)
- 输出列表:
EventLog.StateId,EventLog.Modified - 搜索谓词:
Seek Keys[1]: End: EventLog.Generated < Scalar Operator(EventLog.Generated)
- 输出列表:
- 过滤器 (40%)
- 谓词:
EventLog.Modified as sub.Modified>EventLog.Generated AND EventLog.StateId as sub.StateId<>(0)
- 谓词:
其他上下文/注释:
- 事件数据可能非常突发,大量事件快速到达(队列不断增加),然后是安静的时期(处理器可以赶上(希望如此))
- 处理器确实会不时赶上。
- 处理过程中存在性能问题,如果队列太大,一个单独的进程会清除队列并生成事件以重建事实表。
- 我无法控制日志表的结构或处理系统的架构。对此的任何更改都超出了范围。
- 事件的处理不是外部系统的主要目的。它是为业务的其他部分提供数据的附加组件。
【问题讨论】:
-
您需要发布您的索引定义。单个索引不一定对此有用。但是索引应该是正确的方法,140,000 行不足以引起任何问题。
-
您还可以从检查执行计划中受益,因为它通常会告诉您问题所在。
-
你能提供一些样本数据和想要的结果吗?我很难理解您是否想要均匀分布的行和间隔的平均值,或者只是想将您获得的结果除以它返回的行数。这将根据活动的偏差对平均值产生很大的影响(例如,如果一切都在一小时内发生,那么前一种情况下一天的平均值会小得多,而后者会高得多)。
-
为什么 Generated 可以为空?即使根据您的使用情况不可能有空值,这似乎也是一个缺陷。 设置状态的日期和时间 你的意思是StateId,它是一个int? 一天的平均排队长度不,你不是。您的查询会根据 StateId 选择所有内容。
-
@SMor 我已经更新了问题以澄清一些事情。表结构就是这样,我无法控制它,不幸的是。是的,我的意思是StateId。我扩展了查询以显示我是如何进行聚合的。 CTE 获取事件在队列中的时间以及此时队列的大小。聚合是微不足道的,但我应该将它包含在上下文中。 CTE 是性能瓶颈。
标签: sql-server query-optimization