Низкий коэффициент кардинальности с неравномерным распределением возможных значений - PullRequest
0 голосов
/ 18 апреля 2020

У меня есть таблица , которая используется в качестве очереди :

create table items 
(
    itemId     int, -- PK, identity
    status     int, -- Possible values: 0 = Pending, 1 = Processing, 2 = Processed
    createdAt  datetime2,
    updatedAt  datetime2,
    content    text
)

Производители добавляют записи в очередь, а потребители читают их FIFO:

  • Производители добавить записи со статусом Pending с текущим временем createdAt
  • Потребители выбирают записи в статусе Pending, упорядоченные по createdAt. При потреблении они помечают запись как Processing и устанавливают updatedAt на текущее время (используя update/select CTE)
  • . После обработки потребители отмечают записи как Processed
  • Во время обработки потребители могут взломать sh и, следовательно, не смогут пометить запись как Processed
  • Когда другой потребитель найдет запись, которая застряла в состоянии Processing более чем на x минут (т.е. updatedAt < current_time - x и status = Processing) они собирают их и обрабатывают (предположим, новый потребитель не взломает sh;))
  • Таблица имеет около 1 млн записей и растет примерно на 20 тыс. В день
  • В любой момент времени будет <100 <code>Pending и Processing записей

У меня есть 2 вопроса

  1. Учитывая этот сценарий (особенно последний 2 балла), будет ли индекс на (status, createdAt) с updatedAt в качестве включенного столбца хорошим индексом?

Я пробовал это, и он показывает, что индекс попал и время выполнения очень быстрое (суб-вторых). Однако я не совсем уверен, что этот низкий показатель кардинальности (с начальным столбцом status) является хорошим показателем, поскольку такие индексы обычно считаются плохими . Мне интересно, работает ли это в моем случае из-за крайне неравномерного распределения возможных значений (<1% записей составляют <code>Pending, InProgress, что я и запрашиваю. Запросы для выбора не выполняются) 1051 *)

Я добавил включенный столбец (updatedAt) для поддержки этого фильтра status = Processing and updatedAt < current_time - x, но я не уверен, что он полезен. Планировщик запросов заботится о включенном столбце или просматривает только столбцы в индексе (status, createdAt)?

Бонусные баллы, если вы ответите на оба вопроса;)

1 Ответ

3 голосов
/ 18 апреля 2020

Одной из основных целей индекса является уменьшение количества строк, считываемых из таблицы. Низкий индекс мощности означает, что столбец принимает только несколько значений. Таким образом, если таблица содержит десять миллионов строк и имеется десять значений, то каждое значение будет иметь - в среднем - миллион строк.

Индекс бесполезен для извлечения миллиона строк из десяти таблица миллионов строк, потому что каждая (или почти каждая) страница данных будет иметь соответствующую строку. Цель состоит в том, чтобы уменьшить количество читаемых страниц данных.

Таким образом, использование вами индекса вполне разумно, поскольку вы найдете только несколько строк. Вы используете индекс для поиска необработанных строк, и их немного.

Ваш индекс намного больше необходимого, поскольку содержит информацию об обработанных строках. Вы можете рассмотреть отфильтрованный индекс. Из того, что вы описываете, я думаю, что это будет:

create index idx_items_status_updated
    on items(status, updated)
    where status in (1, 2);

Иногда в этих ситуациях вы хотите использовать кластеризованный индекс для state. По сути, это позволяет «обработанным» элементам группироваться вместе. И, если страницы данных, на которых они находятся, не доступны, то эти страницы данных загружать не нужно.

Однако в вашем случае, я предполагаю, что элементы добавляются последовательно, поэтому только более новые элементы в процессе. Старые страницы данных будут заполнены обработанными элементами и, поскольку на них никогда не ссылаются, могут даже не занимать место в памяти.

...