2013-07-26 2 views
3

У меня огромный стол, какОптимизация большой таблицы ключевых слов?

CREATE TABLE IF NOT EXISTS `object_search` (
    `keyword` varchar(40) COLLATE latin1_german1_ci NOT NULL, 
    `object_id` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`keyword`,`media_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; 

с около 39 миллионов строк (используя более ГБ пространства 1), содержащий данные, индексированные на 1 млн записей в таблице объектов (где object_id точек, в).

Теперь поиск через это с запросом, как

SELECT object_id, COUNT(object_id) AS hits 
FROM object_search 
WHERE keyword = 'woman' OR keyword = 'house' 
GROUP BY object_id 
HAVING hits = 2 

уже значительно быстрее, чем делать LIKE поиск на наборной keywords поле в object таблице, но по-прежнему занимает до 1 минуты.

Это объясняет как выглядит:

+----+-------------+--------+------+---------------+---------+---------+-------+--------+----------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | filtered | Extra     | 
+----+-------------+--------+------+---------------+---------+---------+-------+--------+----------+--------------------------+ 
| 1 | SIMPLE  | search | ref | PRIMARY  | PRIMARY | 42  | const | 345180 | 100.00 | Using where; Using index | 
+----+-------------+--------+------+---------------+---------+---------+-------+--------+----------+--------------------------+ 

Полный объяснить с присоединился object и object_color и object_locale таблицы, в то время как выше запрос выполняется в подзапрос, чтобы избежать накладных расходов, выглядит следующим образом:

+----+-------------+-------------------+--------+---------------+-----------+---------+------------------+--------+----------+---------------------------------+ 
| id | select_type | table    | type | possible_keys | key  | key_len | ref    | rows | filtered | Extra       | 
+----+-------------+-------------------+--------+---------------+-----------+---------+------------------+--------+----------+---------------------------------+ 
| 1 | PRIMARY  | <derived2>  | ALL | NULL   | NULL  | NULL | NULL    | 182544 | 100.00 | Using temporary; Using filesort | 
| 1 | PRIMARY  | object_color  | eq_ref | object_id  | object_id | 4  | search.object_id |  1 | 100.00 |         | 
| 1 | PRIMARY  | locale   | eq_ref | object_id  | object_id | 4  | search.object_id |  1 | 100.00 |         | 
| 1 | PRIMARY  | object   | eq_ref | PRIMARY  | PRIMARY | 4  | search.object_id |  1 | 100.00 |         | 
| 2 | DERIVED  | search   | ref | PRIMARY  | PRIMARY | 42  |     | 345180 | 100.00 | Using where; Using index  | 
+----+-------------+-------------------+--------+---------------+-----------+---------+------------------+--------+----------+---------------------------------+ 

Моя главная цель - просканировать это через 1 или 2 секунды.

Итак, существуют ли дальнейшие методы улучшения скорости поиска по ключевым словам?


Update 2013-08-06:

Применение наиболее внушения Невилл K «s теперь у меня есть следующие настройки:

CREATE TABLE `object_search_keyword` (
    `keyword_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `keyword` varchar(64) COLLATE latin1_german1_ci NOT NULL, 
    PRIMARY KEY (`keyword_id`), 
    FULLTEXT KEY `keyword_ft` (`keyword`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci; 

CREATE TABLE `object_search` (
    `keyword_id` int(10) unsigned NOT NULL, 
    `object_id` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`keyword_id`,`media_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

новый запрос объясним выглядит следующим образом :

+----+-------------+----------------+----------+--------------------+------------+---------+---------------------------+---------+----------+----------------------------------------------+ 
| id | select_type | table   | type  | possible_keys  | key  | key_len | ref      | rows | filtered | Extra          | 
+----+-------------+----------------+----------+--------------------+------------+---------+---------------------------+---------+----------+----------------------------------------------+ 
| 1 | PRIMARY  | <derived2>  | ALL  | NULL    | NULL  | NULL | NULL      | 24381 | 100.00 | Using temporary; Using filesort    | 
| 1 | PRIMARY  | object_color | eq_ref | object_id   | object_id | 4  | object_search.object_id |  1 | 100.00 |            | 
| 1 | PRIMARY  | object   | eq_ref | PRIMARY   | PRIMARY | 4  | object_search.object_id |  1 | 100.00 |            | 
| 1 | PRIMARY  | locale   | eq_ref | object_id   | object_id | 4  | object_search.object_id |  1 | 100.00 |            | 
| 2 | DERIVED  | <derived4>  | system | NULL    | NULL  | NULL | NULL      |  1 | 100.00 |            | 
| 2 | DERIVED  | <derived3>  | ALL  | NULL    | NULL  | NULL | NULL      | 24381 | 100.00 |            | 
| 4 | DERIVED  | NULL   | NULL  | NULL    | NULL  | NULL | NULL      | NULL |  NULL | No tables used        | 
| 3 | DERIVED  | object_keyword | fulltext | PRIMARY,keyword_ft | keyword_ft | 0  |       |  1 | 100.00 | Using where; Using temporary; Using filesort | 
| 3 | DERIVED  | object_search | ref  | PRIMARY   | PRIMARY | 4  | object_keyword.keyword_id | 2190225 | 100.00 | Using index         | 
+----+-------------+----------------+----------+--------------------+------------+---------+---------------------------+---------+----------+----------------------------------------------+ 

Многие из них получают Едут от ключевого слова по сравнению подзапрос быть вложены в другой подзапрос, который не делает ничего, кроме подсчитать количество строк, возвращаемых:

SELECT SQL_NO_CACHE object.object_id, ..., @rn AS numrows 
FROM (
    SELECT *, @rn := @rn + 1 
    FROM (
     SELECT SQL_NO_CACHE search.object_id, COUNT(turbo.object_id) AS hits 
     FROM object_keyword AS kwd 
     INNER JOIN object_search AS search ON (kwd.keyword_id = search.keyword_id) 
     WHERE MATCH (kwd.keyword) AGAINST ('+(woman) +(house)') 
     GROUP BY search.object_id HAVING hits = 2 
    ) AS numrowswrapper 
    CROSS JOIN (SELECT @rn := 0) CONST 
) AS turbo 
INNER JOIN object AS object ON (search.object_id = object.object_id) 
LEFT JOIN object_color AS object_color ON (search.object_id = object_color.object_id) 
LEFT JOIN object_locale AS locale ON (search.object_id = locale.object_id) 
ORDER BY timestamp_upload DESC 

Этот запрос фактически будет работать в течение ~ 6 секунд, так как он ищет двух ключевых слов. Чем больше ключевых слов я ищу, тем быстрее выполняется поиск.

Любой способ дальнейшей оптимизации?


Обновление 2013-08-07

Блокирующий вещь кажется почти наверняка быть приложены ORDER BY заявление. Без него запрос выполняется менее чем за секунду.

Итак, есть ли способ быстрее отсортировать результат? Любые предложения приветствуются, даже хакерские, которые потребуют последующей обработки в другом месте.


Обновление 2013-08-07 позже в тот же день

Alright дамы и господа, гнездящихся заявления WHERE и ORDER BY в другом слое подзапроса, чтобы не позволить ей возиться с таблицами не нужно примерно в два раза его производительность снова:

SELECT wowrapper.*, locale.title 
FROM (
    SELECT SQL_NO_CACHE object.object_id, ..., @rn AS numrows 
    FROM (
     SELECT *, @rn := @rn + 1 
     FROM (
      SELECT SQL_NO_CACHE search.media_id, COUNT(search.media_id) AS hits 
      FROM object_keyword AS kwd 
      INNER JOIN object_search AS search ON (kwd.keyword_id = search.keyword_id) 
      WHERE MATCH (kwd.keyword) AGAINST ('+(frau)') 
      GROUP BY search.media_id HAVING hits = 1 
     ) AS numrowswrapper 
     CROSS JOIN (SELECT @rn := 0) CONST 
    ) AS search 
    INNER JOIN object AS object ON (search.object_id = object.object_id) 
    LEFT JOIN object_color AS color ON (search.object_id = color.object_id) 
    WHERE 1 
    ORDER BY object.object_id DESC 
) AS wowrapper 
LEFT JOIN object_locale AS locale ON (jfwrapper.object_id = locale.object_id) 
LIMIT 0,48 

Поисковые запросы, которые имели 12 секунд (одного ключевого слова, ~ результаты 200K) в настоящее время принимают 6 и поиск двух ключевых слов, которые потребовалось 6 секунд (60 тысяч результатов) в настоящее время занимает около 3,5 С.Е. CS.

Теперь это уже значительное улучшение, но есть ли шанс направить это дальше?


Обновление 2013-08-08 рано день

расстегнул, что последний вариант вложенной запросы, так как он на самом деле замедлил другие вариации этого ... я сейчас пытаюсь каким-то другое вещи с разными таблицами таблиц и FULLTEXT индексы с использованием MyISAM для выделенной таблицы поиска с комбинированным полем ключевых слов (запятая разделена в поле TEXT).


Обновление 2013-08-08

Хорошо, простой индекс полнотекстового реально не поможет.

Назад к предыдущей настройке, единственной блокировкой является ORDER BY (которая использует временную таблицу и файловый центр). Без него поиск завершен менее чем за секунду!

Так в основном то, что осталось от всего этого:
Как оптимизировать ORDER BY заявление работать быстрее, вероятно, за счет исключения использования временной таблицы?

+0

Можете ли вы отправить результат «EXPLAIN» для запроса? – Vatev

+0

@ Ватев там! –

+0

Я только что запустил 'OPTIMIZE TABLE', и теперь это между 10 и 30 секундами. –

ответ

1

Full text search будет намного быстрее, чем использование стандартных функций сравнения строк SQL.

Во-вторых, если у вас есть высокая степень избыточности ключевых слов, вы могли бы рассмотреть «многие ко многим» реализации:

Keywords 
-------- 
keyword_id 
keyword 

keyword_object 
------------- 
keyword_id 
object_id 

objects 
------- 
object_id 
...... 

Если это уменьшает сравнение строк из 39 миллионов строк 100K строк (примерно размер английского словаря), вы также можете увидеть отчетливое улучшение, так как запрос должен будет выполнять только 100K сравнения строк, а объединение целочисленного поля keyword_id и object_id должно быть намного быстрее, чем выполнение сопоставлений строк в 39M.

+0

Не оставил бы меня с 39 миллионами строк в таблице 'keyword_object'? –

+1

Да, это было бы - но дорогостоящая часть вашего текущего запроса почти наверняка представляет собой сравнение строк. Я обновил ответ, чтобы объяснить. –

+0

Хорошо, я уже немного экспериментировал с этим решением, и я могу получить результаты в течение 10-20 секунд, что значительно улучшилось, но все же не так быстро, как хотелось бы. Кроме того, моя таблица ключевого слова закончила тем, что имела 1M строк вместо 100K. Одна мысль, которая меня беспокоит, заключается в том, как Google может сканировать триллионы записей менее чем за десятую часть секунды. –

0

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

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

SELECT s1.object_id 
FROM object_search s1 
JOIN object_search s2 ON s2.object_id = s1.object_id AND s2.key_word = 'word2' 
JOIN object_search s3 ON s3.object_id = s1.object_id AND s3.key_word = 'word3' 
.... 
WHERE s1.key_word = 'word1' 

, и я не уверен, что это будет быстрее, таким образом.

Также вам нужно будет иметь индекс на object_id (при условии, что ваш ПК равен (key_word, object_id)).

0

Если у вас редко есть INSERT и часто SELECT, вы можете оптимизировать свои данные для чтения, то есть пересчитать количество object_ids за ключевое слово и непосредственно сохранить его в базе данных. Тогда SELECTs будут очень быстрыми, вСТАВКА займет несколько секунд.

+0

Как иметь каждое ключевое слово только один раз и присваивать ему список 'object_id'? Как это будет работать? –

Смежные вопросы