从自定义队列中选择下一条消息的速度慢

时间:2009-12-18 18:18:36

标签: sql oracle sorting queue indexing

我有一个简单的基于表的队列系统。在最简单的形式中,它由id,队列名称和状态组成。当从给定队列中读取下一条消息时,我们需要确保FIFO(先进先出),即具有给定状态的给定队列中的最低id。这一切都可以正常运行几千行,但是当我们达到1M +行时,它就不再适合了。

我们不能使用rownum = 1,因为这是在排序之前完成的,排序是基于id列完成的(asc)。如果我制作一个游标并按id排序1000次,这大约需要100ms,这是一个很好的性能(0.1 ms /循环)。如果我在查询中包含状态和队列名称(我需要,因为我需要特定队列的未读消息的最低id),10个循环(130ms /循环)需要大约1300ms,这远远不行。

我尝试在三列中的每一列上都有索引,也是id,queue,status的组合索引,最后是id上的索引和队列和状态的组合索引。 id列也是主键。已经在基于规则的设置中尝试了所有组合(使用规则提示)。

祝你好运, Michael Ringholm Sundgaard - iHedge A / S. www.ihedge.dk www.ibrain.dk

6 个答案:

答案 0 :(得分:5)

我尝试过的索引中没有提到的一件事就是(queue,status,id)上的索引。如果你把id放在索引的开头,那么它主要会破坏索引的使用,因为你正在寻找“最低的”,这在其他标准被应用之前是没有意义的。

索引中列的排序通常与实际列本身一样重要。

答案 1 :(得分:3)

一般的想法是:

select id from
(select id
   from queue_table
   where queue_name = 'nameOfQueue'
   and processed = 'NO'
   order by id
)
where rownum = 1

您是否考虑过使用Oracle AQ而不是自己动手?

答案 2 :(得分:0)

我猜你的索引没有被使用,因为尚未为索引收集统计数据。

结帐this SO question。您可以在查询中提供提示以强制使用您创建的索引。如果这有帮助,那么为您的表运行DBMS_STATS.gather_table_stats包应强制更新统计信息,从而无需提示。最终数据库将gather the stats on it's own (see Justin Cave's answer).

答案 3 :(得分:0)

您尚未与我们分享查询。与排序1M行相比,排序几千个很容易。您需要检查性能还有很多其他原因吗?请检查以下内容:

  • 你的桌子分析了吗?是DBMS_STATS.gather_table_stats还是gather_index_stats
  • 您检查了EXPLAIN PLAN吗?他们是否显示使用了INDEXES?
  • 您的Oracle版本是什么版本?

您应该按照建议尝试Oracle Advanced Queuing

答案 4 :(得分:0)

一些丑陋/聪明的黑客可能会起作用或者可能只是过度杀伤。

1)你可以像这样创建一个很好的基于小函数的索引(语法可能有点偏,现在没有访问Oracle)。

CREATE INDEX my_small_queue_index 
ON queue_table ( decode(is_processed,'YES',null,queue_name)
                ,decode(is_processed,'YES',null,id)
               );

然后您可以这样选择:

  SELECT --+ index_asc(q my_small_queue_index)
     decode(is_processed,'YES',null,id) AS id
  FROM queue_table q
  WHERE decode(is_processed,'YES',null,queue_name) = 'some queue name'
    AND rownum = 1;

如果有很大比例的已处理行且只有少数未处理的行(10 ^ 9对几百),那么应该工作得很好。在任何情况下都不应超过几个。

2)如果队列名称是固定的并且没有很多队列名称,则可以为每个队列创建一个分区。

答案 5 :(得分:0)

使用索引提示(没有订单)的再推荐,即

SELECT - + index_asc(q my_small_queue_index)      decode(is_processed,'YES',null,id)AS id   FROM queue_table q   WHERE decode(is_processed,'YES',null,queue_name)='某个队列名称'     AND rownum = 1;

非常危险。如果该索引被删除,重命名,设置为不可用,或者优化器选择快速全扫描,那么您将不会得到任何错误,您仍将获得1行,但不能保证其正确行。使用索引很好 - 但是您仍然必须具有order-by子句以保证正确的结果。