Чому LIKE більш ніж в 4 рази швидше, ніж MATCH… ПРОТИ за індексом FULLTEXT в MySQL?


12

Я цього не отримую.

У мене є таблиця з цими показниками

PRIMARY     post_id
INDEX       topic_id
FULLTEXT    post_text

Таблиця має (лише) 346 000 рядків. Я намагаюся виконати 2 запити.

SELECT post_id 
FROM phpbb_posts 
WHERE topic_id = 144017 
AND post_id != 155352 
AND MATCH(post_text) AGAINST('http://rapidshare.com/files/5494794/photo.rar')

займає 4,05 секунди

SELECT post_id 
FROM phpbb_posts 
WHERE topic_id=144017 
AND post_id != 155352 
AND post_text LIKE ('%http://rapidshare.com/files/5494794/photo.rar%')

займає 0,027 секунди.

EXPLAIN показує, що єдина відмінність - у можливих_контактах ( fulltextвключений пост_текст, LIKEне так)

Це справді дивно.

Що за цим? Що відбувається на задньому плані? Як можна LIKEбути настільки швидким, коли не використовується індекс і FULLTEXT, так повільно, коли використовується його індекс?

ОНОВЛЕННЯ1:

Насправді це займає приблизно 0,5 секунди, можливо, таблиця була заблокована, але все ж, коли я включаю профілювання, це показує, що FULLTEXT INITIALIZATION займає 0,2 секунди. Як справи?

Я можу запитувати свою таблицю LIKE10 разів на секунду, з повним текстом лише 2 рази

ОНОВЛЕННЯ2:

Сюрприз!

mysql> SELECT post_id FROM phpbb_posts WHERE post_id != 2 AND topic_id = 6 AND MATCH(post_text) AGAINST ('rapidshare.com');
Empty set (0.04 sec)

тому я запитую, як це можливо?

Крім того,

SELECT count(*) FROM phpbb_posts WHERE MATCH(post_text) AGAINST ('rapidshare.com')

дійсно повільно. Можна повний текст будь-якого зламаного?

ОНОВЛЕННЯ3:

Якого біса?

SELECT forum_id, post_id, topic_id, post_text  FROM phpbb_posts  WHERE MATCH(post_text) AGAINST ('rapidshare.com') LIMIT 0, 30;

займає 0,27 с

SELECT count(*) FROM phpbb_posts  WHERE MATCH(post_text) AGAINST ('rapidshare.com') LIMIT 0, 30;

займає більше 30 секунд! Що тут не так?


Чи часи відповіді між двома послідовними протягом кількох циклів? Мені спокушається думати, що кешування диска може ввійти в гру, коли перший «повільний» тест завантажує всі дані, необхідні в оперативні пам’яті, тому другий «швидкий» запит - це досить швидко.
— atxdba

Тестові запити тільки з SQL_NO_CACHE .
— mgutt

Це досить старе питання / відповідь. Будь-які досягнення від mysql / mariadb з тих днів?
— Роман Сусі

1
Попередження: Час виконання цього запитання означає, що мова йде лише про MyISAM. Його застосовність до InnoDB під питанням.
— Рік Джеймс

@RomanSusi - Хочете почати нове запитання, спрямоване на InnoDB?
— Рік Джеймс

Відповіді:


2

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

Кожен раз, коли виникає запит, що включає індекс FULLTEXT, оптимізатор запитів MySQL, як правило, перетворює запит на повне сканування таблиці. Я це бачив протягом багатьох років. Я також писав попередній пост про цю найсмішнішу поведінку в індексах FULLTEXT .

Можливо, вам доведеться зробити дві речі:

  1. refactor запиту, щоб індекс FULLTEXT не переводив оптимізатор запитів MySQL у стан заплутаності
  2. Додайте додатковий індекс, який належним чином підтримуватиме відновлений запит

РЕФАКТОР ЗАПИТАННЯ

Ось ваш оригінальний запит

SELECT post_id  
FROM phpbb_posts  
WHERE topic_id = 144017  
AND post_id != 155352  
AND MATCH(post_text) AGAINST('http://rapidshare.com/files/5494794/photo.rar') 

Вам потрібно буде переробити запит таким чином:

SELECT subqueryA.post_id
FROM
(
    SELECT post_id FROM phpbb_posts
    WHERE topic_id = 144017
    AND post_id != 155352
) subqueryA
INNER JOIN
(
    SELECT post_id FROM phpbb_posts
    WHERE MATCH(post_text) AGAINST('http://rapidshare.com/files/5494794/photo.rar')
) subqueryB
USING (post_id);

Створіть новий індекс

Вам знадобиться індекс для підтримки subqueryA. Ви вже маєте індекс topic_id. Його потрібно замінити наступним чином:

ALTER TABLE phpbb_posts ADD INDEX topic_post_ndx (topic_id,post_id);
ALTER TABLE phpbb_posts DROP INDEX topic_id;

Спробувати !!!

ОНОВЛЕННЯ 2012-03-19 13:08 EDT

Спробуйте це спочатку

SELECT post_id FROM
(
    SELECT * FROM phpbb_posts
    WHERE topic_id = 144017
    AND post_id != 155352
) A;

Якщо це працює швидко і повертає невелику кількість рядків, спробуйте цей вкладений підзапит:

SELECT post_id FROM
(
    SELECT * FROM phpbb_posts
    WHERE topic_id = 144017
    AND post_id != 155352
) A
WHERE MATCH(post_text) AGAINST('http://rapidshare.com/files/5494794/photo.rar');

ОНОВЛЕННЯ 2012-03-19 13:11 EDT

Порівняйте час роботи цього:

SELECT count(*) FROM phpbb_posts  WHERE MATCH(post_text) AGAINST ('rapidshare.com') LIMIT 0, 30;

з цим

SELECT count(*) FROM phpbb_posts WHERE 1 = 1;

Якщо час роботи однаковий, то пункт MATCH виконується у кожному рядку. Як я вже згадував раніше, використання індексів FULLTEXT, як правило, зводить нанівець будь-які переваги, спробувані та внесені оптимізатором запитів MySQL.


Отже, ви хочете сказати, що мій запит насправді сканує всю таблицю через тему_id і post_idплутає її? Чому запит LIKE працює навіть без індексу на цих стовпцях (topic_id, post_id)? Чому MYSQL не просто інтелектуально вибирає, topic_id = 144017 AND post_id != 155352а потім просто переглядає ці результати? А що робити, якщо 100k рядків містить мою рядок пошуку в повному обсязі post_text? Чи не вибрав би їх усіх?
— генезис

Насправді я плутаюся ще більше. LIKE '% text%' також не використовує індекси, це означає, що він сканує всю таблицю, так чому це так швидко?
— генезис

Подивіться, будь ласка, на моє ОНОВЛЕННЯ , я думаю, ви вирішите це дуже швидко. Я збираюся дати вам свою репліку, якщо ви вирішите її.
— генезис

Відповідь на ваше друге оновлення. Другий запит тривав менше ніж 0,01 мс, перший не закінчувався. Чому ви сказали "Якщо час роботи однаковий, то пункт MATCH виконується в кожному рядку". ? Чи не зовсім протилежне тому, що має бути? Якщо ви заглянете сюди , ви побачите, що я не єдиний у цій проблемі
— генезис

Відповідь на ваше перше оновлення. Перший запит виконував 0,01 мс, 0 рядків, другий - "Неможливо знайти індекс FULLTEXT, що відповідає списку стовпців". Однак ваш запит із 2-ма підпитами працює ідеально!
— генезис
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.