Низкая производительность при выборе следующего сообщения из пользовательской очереди - PullRequest
3 голосов
/ 18 декабря 2009

У меня есть простая система очередей на основе таблиц. В простейшей форме он состоит из идентификатора, имени очереди и статуса. При чтении следующего сообщения из данной очереди нам необходимо обеспечить FIFO (первым пришел - первым обслужен), т. Е. Самый низкий идентификатор из данной очереди с данным статусом. Все это прекрасно работает с несколькими тысячами строк, но когда мы достигаем 1M + строк, это больше не идет хорошо.

Мы не можем использовать rownum = 1, так как это делается до сортировки, сортировка выполняется только по столбцу id (asc). Если я делаю курсор и сортирую по id 1000 раз, это занимает около 100 мс, что является хорошей производительностью (0,1 мс / цикл). Если я включу статус и имя очереди в запрос (который мне нужен, так как мне нужен самый низкий идентификатор непрочитанного сообщения для конкретной очереди), это займет около 1300 мс для 10 циклов (130 мс / цикл), что далеко не нормально.

Я попытался создать индекс по каждому из трех столбцов, а также комбинированный индекс по идентификатору, очереди, состоянию и, наконец, комбинацию с индексом по идентификатору и комбинированный индекс по очереди и состоянию. Столбец id также является первичным ключом. Вся комбинация была опробована также в настройке на основе правил (с использованием подсказки правила).

С уважением, Майкл Рингхольм Сундгаард - iHedge A / S www.ihedge.dk www.ibrain.dk

Ответы [ 6 ]

5 голосов
/ 18 декабря 2009

Одна вещь, о которой я не упомянул в индексах, которые вы пробовали, это индекс (очередь, состояние, идентификатор). Если вы помещаете идентификатор в начало индекса, это в основном разрушает использование индекса, поскольку вы ищете «низший», который не имеет смысла, пока не будут применены другие критерии.

Порядок столбцов в индексе часто может быть таким же важным, как и сами столбцы.

3 голосов
/ 18 декабря 2009

Общая идея:

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

Рассматривали ли вы использование Oracle AQ для этого вместо того, чтобы использовать свой собственный?

0 голосов
/ 07 января 2010

Рекомендовано использовать индексную подсказку (без заказа на), т. Е.

SELECT - + index_asc (q my_small_queue_index) декодировать (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, чтобы гарантировать правильные результаты.

0 голосов
/ 18 декабря 2009

Некоторые уродливые / умные хаки, которые могут просто сработать или могут быть просто перебором.

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) Вы можете создать раздел для каждой очереди, если имена очередей фиксированы и их не много.

0 голосов
/ 18 декабря 2009

Вы не поделились с нами запросом. Сортировать несколько тысяч легко по сравнению с 1M строк. Там может быть много других причин, вам нужно проверить производительность? Проверьте следующее:

  • Анализируются ли ваши таблицы? DBMS_STATS.gather_table_stats или gather_index_stats используется?
  • Вы проверили EXPLAIN PLAN? Они показывают используемые ИНДЕКСЫ?
  • Какая версия у вас Oracle?

Вам следует попробовать Oracle Advanced Queuing , как предложено.

0 голосов
/ 18 декабря 2009

Я полагаю, что ваш индекс не используется, потому что статистика еще не собрана для индекса.

Проверьте этот ТАК вопрос . В запросе можно указать подсказку, чтобы принудительно использовать созданные вами индексы. Если это помогает, то запуск пакета DBMS_STATS.gather_table_stats для вашей таблицы должен принудительно обновить статистику, устраняя необходимость в подсказке. В конечном итоге база данных сама соберет статистику (см. Ответ Джастина Кейва).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...