我有一个简单的基于表的队列系统。在最简单的形式中,它由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
答案 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
?您应该按照建议尝试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子句以保证正确的结果。