Чому 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.