Стратегия индексирования MySQL - PullRequest
1 голос
/ 04 декабря 2009

Я пытаюсь использовать следующий запрос к таблице с ~ 200k записей в ней. Есть множество других полей, по которым можно фильтровать, но это базовый пример.

SELECT b.isbn FROM books b 
WHERE 
b.price IS NOT NULL AND 
b.deleted = '' AND 
b.publication_date <= '2009-12-04' AND 
(
  b.subject1_id IN ('CAT1','CAT2','CAT3','CAT4','CAT5') OR
  b.subject2_id IN ('CAT1','CAT2','CAT3','CAT4','CAT5') OR
  b.subject3_id IN ('CAT1','CAT2','CAT3','CAT4','CAT5')
)

В настоящее время у меня есть отдельный индекс для всех этих полей, и этот запрос занимает ~ 4,5 секунды, что слишком долго. EXPLAIN списки NULL под ключом.

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

Как я могу проиндексировать эти поля, чтобы ускорить мои запросы?

РЕДАКТИРОВАТЬ: Вот мои текущие индексы (ни один из которых, кажется, не используется запросом):

  • индекс (цена)
  • индекс (удалено)
  • индекс (publication_date)
  • индекс (subject2_id)
  • индекс (subject3_id)
  • index (цена, удалено, дата публикации, subject1_id, subject2_id, subject3_id)

РЕДАКТИРОВАТЬ 2: Ответ Per --ıu - после нормализации таблиц и использования, в основном, его запроса, он несколько ускоряет его (сейчас время составляет ~ 3,5 секунды), но не так сильно, как я ищу. Я проиндексировал новую таблицу как PRIMARY KEY (isbn, subject_id), и этот индекс используется для объединения.

EDIT3: я добавил дополнительный индекс для второй таблицы (subject_id, isbn), который помогает. Добавление другого индекса, о котором упоминается ниже, немного помогает, но используется только тогда, когда я использую «FORCE INDEX» в запросе. Сейчас примерно 1,5 секунды. Есть ли надежда получить его намного ниже?

Ответы [ 4 ]

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

Вам нужно нормализовать свою схему, прежде чем индексирование поможет вам в этом случае.

Вы можете создать другую таблицу, которая содержит (subject, isbn), добавить индексы для книги и темы, а затем присоединиться к этой таблице, например:

select b.isbn from books b
inner join book_subject bs on bs.isbn=b.isbn
where 
    b.price is not null and b.deleted != 'DELETED'
    AND b.publication_date <= '2009-12-04' 
    AND bs.subject in ('CAT1', 'CAT2'...)

Правило № 1 (буквально) в нормализации схемы: « нет повторяющихся групп ». Операция OR в предложении where в трех столбцах темы не позволит вам воспользоваться индексом для этой части запроса.

(обновлено, чтобы отразить, что isbn является первичным ключом)

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

Прежде всего: MySQL может использовать только один индекс на таблицу во время выбора. Он пытается выбрать наилучший из возможных индексов, но иногда сервер не может принять решение по нескольким причинам. Наличие нескольких индексов только для одного поля поможет только в том случае, если у вас много операторов, выполняющихся только с одним предложением where за раз.

Для оптимизации: вам нужно создать индекс, включающий поля

price
deleted
publication_date

НЕ включайте категории, так как вы используете предложение OR.

ALTER TABLE `test`.`books` ADD INDEX `idxPriceDeletedPublication`(`price`, `deleted`, `publication_date`);

Затем вы получите следующий EXPLAIN-вывод:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: b
         type: range
possible_keys: idxPriceDeletedPublication
          key: idxPriceDeletedPublication
      key_len: 5
          ref: NULL
         rows: 1
        Extra: Using where
1 row in set (0.00 sec)
0 голосов
/ 05 декабря 2009

По отношению к посту Ника:

создать другую таблицу, которая содержит (subject, book_id), добавить индексы на книга и тема:

не будет ли чище иметь

select b.isbn from books b

where 
#various table b where restrictions

AND b.isbn IN (
    Select isbn
    from book_subject bs 
    where bs.subject IN ('CAT1', 'CAT2' ...)
)
0 голосов
/ 05 декабря 2009

Я бы больше подумал о том, что на самом деле означает ваш запрос, и это может помочь привести вас к ответу.

Давайте расскажем об этом, чтобы показать вам проблему.

SELECT b.isbn FROM books b 
WHERE 
b.price IS NOT NULL AND 
b.deleted != 'DELETED' AND 
b.publication_date <= '2009-12-04' AND 
(
  b.subject1_id = 'CAT1' OR 
  b.subject1_id = 'CAT2' OR 
  b.subject1_id = 'CAT3' OR 
  b.subject1_id = 'CAT4' OR 
  b.subject1_id = 'CAT5' OR
  b.subject2_id = 'CAT1' OR 
  b.subject2_id = 'CAT2' OR 
  b.subject2_id = 'CAT3' OR 
  b.subject2_id = 'CAT4' OR 
  b.subject2_id = 'CAT5' OR
  b.subject3_id = 'CAT1' OR 
  b.subject3_id = 'CAT2' OR 
  b.subject3_id = 'CAT3' OR 
  b.subject3_id = 'CAT4' OR 
  b.subject3_id = 'CAT5'

)

Очевидно, что нет никакого индекса, который он будет использовать, кроме тех, для которых (цена, удаленный, публикация_дата, тема1) и т. Д. Для других тем.

Какие поля в индексе?

...