使用复杂条件最小化索引读取


12

我正在优化工作票的Firebird 2.5数据库。它们存储在这样声明的表中:

CREATE TABLE TICKETS (
  TICKET_ID id PRIMARY KEY,
  JOB_ID id,
  ACTION_ID id,
  STATUS str256 DEFAULT 'Pending'
);

我通常想查找尚未处理的第一张票证并且处于Pending状态。

我的处理循环将是:

  1. 取回第一张票 Pending
  2. 使用票务。
  3. 更新故障单状态=> Complete
  4. 重复。

没什么好看的。如果在此循环运行时正在观察数据库,则可以看到每次迭代的索引读取次数都在增加。据我所知,性能似乎并没有显着下降,但是我正在测试的机器很快。但是,我从一些用户那里收到了性能随时间下降的报告。

我在上有一个索引Status,但看起来仍然像它在Ticket_Id每次迭代时向下扫描列。似乎我正在忽略某些内容,但是我不确定是什么。是这种预期的索引读取数目攀升,还是索引行为异常?

-编辑评论-

在Firebird中,您可以限制行检索,例如:

Select First 1
  Job_ID, Ticket_Id
From
  Tickets
Where
  Status = 'Pending'

因此,当我说“第一”时,我只是在询问它在哪里的有限记录集Status = 'Pending'


您在“检索第一个票证的“待定”位置中的“第一票是什么意思?
ypercubeᵀᴹ

如果“第一”的意思是最小的ticket_id,你需要probbaly的索引(status, ticket_id)
ypercubeᵀᴹ

您如何确定性能下降是由此过程而不是其他查询/语句引起的?
ypercubeᵀᴹ

@ypercube-不,我不确定性能是否会下降。这就是为什么我的问题是“我需要对此加以关注,还是索引的正常行为?”的原因。我在监视数据库时注意到了这一点,但我认为这是意外的。当我针对索引列提供where子句时,我不希望它继续扫描前面的行。FWIW,修改索引以使其ticket_id实际包含的效果要比仅对Status进行索引要差。
gddc 2012年

id(数据类型)你定义域?
a_horse_with_no_name 2013年

Answers:


1

随着时间的推移,由于处于“已完成”状态的项目数量增加而导致性能下降。仔细考虑一下-测试时您不会有任何性能下降,因为您可能只有少量的行,其状态为“完成”。但是在生产中,他们可能有数百万行处于“已完成”状态,并且该数目将随着时间的推移而增加。从本质上讲,这会使您的“状态”索引随着时间的推移而越来越少。因此,数据库可能只是决定,因为Status几乎总是具有“ Complete”值,因此它将仅扫描表而不使用索引。

在SQL Server(可能还有其他RDBMS?)中,可以使用过滤索引来解决此问题。在SQL Server中,您可以在索引定义的末尾添加WHERE条件,以说“仅将此索引应用于状态为<>'Complete'的记录”。然后,使用此谓词的任何查询都将很可能在少量未设置为“完成”的记录上使用索引。但是,根据此处的文档:http : //www.firebirdsql.org/refdocs/langrefupd25-ddl-index.html,它看起来不像Firebird支持过滤索引。

一种解决方法是将“完成”记录放入ArchiveTickets表中。创建一个与Tickets表具有完全相同的定义(尽管没有任何自动生成的ID)的表,并通过将“ Complete”记录推送到ArchiveTickets表来维护它们之间的行。票证表上的索引将覆盖少得多的记录数,并具有更高的性能。这很可能意味着您将需要更改所有引用“已完成”票证的报告等,以指向“存档”表,或者对票证和ArchiveTicket都执行UNION。这不仅具有快速的优势,而且还意味着您可以为ArchiveTickets表创建特定的索引,以使其在其他查询中表现更好(例如:

如果您的生产要进入成千上万的行,则应对此予以关注。随着时间的流逝,性能将下降,并对您的用户体验产生负面影响。


0

性能是否受影响将取决于数据量和计算机容量。鉴于现代硬件的能力,很难想象您所描述的设计无法处理门票销售量。但是,我建议您进行一些更改以确保正确性,并且可能会提高性能,这是其次要优势。

您的第一个未决查询是不确定的。首先按什么顺序?SQL表没有内部顺序。该First 1黑客只是给你一些任意的第一个。为了确定性,为什么不按Job_ID顺序处理挂起的作业?

如果您有两个索引{Job_ID}和{Status,Job_ID},则此查询将可预测且有效地返回一行:

Select Job_ID, Ticket_Id
From   Tickets
Where Job_ID = ( 
  select min(Job_ID) from Tickets 
  where Status = 'Pending'
);

我不是Firebird用户,因此您必须检查查询计划,但这应该是有效的,因为子查询仅引用第二个索引,并为第一个索引生成一个值。(您可能还可以使用其他效率技巧。例如,您可以将物理表组织为B +树,或者可以访问隐藏的row_id。)

为确保正确性,我要进行的另Status一项更改是制作一个受约束的字节,并让应用程序提供“ Pending”字符串。这将防止错误的Status值,并且可能使讨价还价中的索引变小。就像是:

CREATE TABLE TICKETS (
  TICKET_ID id PRIMARY KEY,
  JOB_ID id,
  ACTION_ID id,
  STATUS char(1) not NULL 
     DEFAULT 'P'
     CHECK( STATUS in ('P', 'C', 'X') ) -- whatever the domain is
);

当然,您可以使用视图(或派生列)来提供Status的规范字符串。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.