Я оптимизирую базу данных рабочих билетов Firebird 2.5. Они хранятся в таблице, объявленной так:
CREATE TABLE TICKETS (
TICKET_ID id PRIMARY KEY,
JOB_ID id,
ACTION_ID id,
STATUS str256 DEFAULT 'Pending'
);
Я обычно хочу найти первый билет, который не был обработан и находится в Pending
статусе.
Мой цикл обработки будет:
- Получить 1-й билет, где
Pending
- Работайте с Ticket.
- Обновить статус заявки =>
Complete
- Повторение.
Ничего особенного. Если я наблюдаю за базой данных во время выполнения этого цикла, я вижу количество индексированных операций чтения для каждой итерации. Как мне кажется, производительность не сильно ухудшается, но машина, на которой я тестирую, довольно быстрая. Тем не менее, я получил сообщения о снижении производительности со временем от некоторых из моих пользователей.
У меня есть индекс Status
, но кажется, что он просматривает Ticket_Id
столбец каждую итерацию. Кажется, я что-то упускаю, но не уверен, что. Ожидается ли увеличение числа индексированных операций чтения для чего-то подобного этому, или индекс работает неправильно?
- Редактирует для комментариев -
В Firebird вы ограничиваете поиск строк следующим образом:
Select First 1
Job_ID, Ticket_Id
From
Tickets
Where
Status = 'Pending'
Поэтому, когда я говорю «сначала», я просто спрашиваю, где ограниченный набор записей Status = 'Pending'
.
источник
ticket_id
, вам, вероятно, нужен индекс(status, ticket_id)
ticket_id
фактически выполняется хуже, чем просто индексирование Status.id
(тип данных) доменом, который вы определили?Ответы:
Ухудшение с течением времени происходит из-за увеличения количества элементов, которые находятся в состоянии «Завершено». Подумайте об этом на секунду - вы не получите никакого снижения производительности при тестировании, поскольку у вас, вероятно, есть небольшое количество строк со статусом «Завершено». Но в процессе производства они могут иметь миллионы строк со статусом «Завершено», и это число со временем будет увеличиваться. Это, по сути, делает ваш индекс статуса все менее и менее полезным с течением времени. Таким образом, база данных, вероятно, просто решает, что, поскольку Status почти всегда имеет значение «Complete», она будет просто сканировать таблицу вместо использования индекса.
В SQL Server (и, возможно, в других СУБД?) Это можно обойти, используя отфильтрованные индексы. В SQL Server вы бы добавили условие WHERE в конец определения вашего индекса, чтобы сказать «применять этот индекс только к записям со статусом <>« Завершено »». Тогда любой запрос, использующий этот предикат, скорее всего будет использовать индекс для небольшого количества записей, для которых не установлено значение «Завершено». Однако, основываясь на документации здесь: http://www.firebirdsql.org/refdocs/langrefupd25-ddl-index.html , похоже, что Firebird не поддерживает отфильтрованные индексы.
Обходной путь - поместить записи «Complete» в таблицу ArchiveTickets. Создайте таблицу с точно таким же определением (хотя и без какого-либо автоматически сгенерированного идентификатора), как у вашей таблицы Tickets, и поддерживайте строки между ними, помещая записи «Complete» в таблицу ArchiveTickets. Индекс на вашей таблице Билетов будет тогда с гораздо меньшим количеством записей и будет намного более эффективным. Это, вероятно, будет означать, что вам нужно будет изменить любые отчеты и т. Д., Которые ссылаются на «Завершенные» билеты, чтобы указывать на таблицу «Архив», или выполнять UNION для билетов и ArchiveTickets. Это будет иметь преимущество не только в быстроте, но и в том, что вы сможете создавать специальные индексы для таблицы ArchiveTickets, чтобы она работала лучше для других запросов (например:
Вы должны быть обеспокоены этим, если ваша продукция будет идти в тысячи строк. Производительность со временем снижается и отрицательно влияет на ваш пользовательский опыт.
источник
Будет ли это зависеть от производительности, будет зависеть от объема данных и производительности машины. Учитывая возможности современного аппаратного обеспечения, трудно представить объем продаж билетов, который не может быть обработан описанным вами дизайном. Тем не менее, есть некоторые изменения, которые я бы порекомендовал для корректности, и которые могут улучшить производительность в качестве вторичного преимущества.
Ваш ожидающий запрос get first является недетерминированным. Сначала по какому порядку? Таблица SQL не имеет внутреннего порядка;
First 1
хак просто дает вам некоторую произвольную первую. Чтобы сделать его детерминированным, почему бы не обработать отложенные задания в порядке Job_ID?Если у вас есть два индекса {Job_ID} и {Status, Job_ID}, этот запрос вернет одну строку предсказуемо и эффективно:
Я не пользователь Firebird, поэтому вам нужно проверить план запроса, но он должен быть эффективным, поскольку подзапрос ссылается только на второй индекс, выдает значение для первого. (Для вас могут быть доступны другие приемы повышения эффективности. Вы можете организовать физическую таблицу в виде дерева B + или, например, иметь доступ к скрытому row_id.)
Другое изменение, которое я бы сделал для корректности, - это создание
Status
одного ограниченного байта и предоставление приложению строки «Pending». Это защитит от ошибочныхStatus
значений и, вероятно, уменьшит индекс при сделке. Что-то вроде:Конечно, вы можете использовать представление (или, возможно, производный столбец) для предоставления канонических строк для Status.
источник