Высокопроизводительная многоуровневая фильтрация тегов - PullRequest
10 голосов
/ 05 августа 2011

У меня есть большая база артистов, альбомов и треков.Каждый из этих элементов может иметь один или несколько тегов, назначенных посредством склеенных таблиц (track_attributes, album_attributes, artist_attributes).Существует несколько тысяч (или даже сотен тысяч) тегов, применимых к каждому типу элементов.

Я пытаюсь выполнить две задачи, и мне очень трудно заставить запросы выполняться приемлемо.

Задание 1) Получить все треки, которые имеют любые заданные теги (если предоставлены), исполнителями, которые имеют любые заданные теги (если предоставлены) в альбомах с любыми заданными тегами (если предоставлены).Любой набор тегов может отсутствовать (т. Е. Активен только тег трека, нет тегов исполнителя или альбома)

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

Задача 2) Получить список тегов, которые применяются к результатам предыдущего фильтра, вместе со счетчиком того, сколько треков имеет каждый данный тег.

За мной следуют некоторые общие рекомендациив подходе .Я пробовал временные таблицы, внутренние соединения, IN (), все мои попытки пока приводят к медленным ответам.Хороший пример результатов, которые я получаю, можно увидеть здесь: http://www.yachtworld.com/core/listing/advancedSearch.jsp, , за исключением , они имеют только один уровень тегов, я имею в виду три.

Структуры таблиц:

Table: attribute_tag_groups
   Column   |          Type               |   
------------+-----------------------------+
 id         | integer                     |
 name       | character varying(255)      | 
 type       | enum (track, album, artist) | 

Table: attribute_tags
   Column                       |          Type               |   
--------------------------------+-----------------------------+
 id                             | integer                     |
 attribute_tag_group_id         | integer                     |
 name                           | character varying(255)      | 

Table: track_attribute_tags
   Column   |          Type               |   
------------+-----------------------------+
 track_id   | integer                     |
 tag_id     | integer                     | 

Table: artist_attribute_tags
   Column   |          Type               |   
------------+-----------------------------+
 artist_id  | integer                     |
 tag_id     | integer                     | 

Table: album_attribute_tags
   Column   |          Type               |   
------------+-----------------------------+
 album_id   | integer                     |
 tag_id     | integer                     | 

Table: artists
   Column   |          Type               |   
------------+-----------------------------+
 id         | integer                     |
 name       | varchar(350)                | 

Table: albums
   Column   |          Type               |   
------------+-----------------------------+
 id         | integer                     |
 artist_id  | integer                     | 
 name       | varchar(300)                | 

Table: tracks
   Column    |          Type               |   
-------------+-----------------------------+
 id          | integer                     |
 artist_id   | integer                     | 
 album_id    | integer                     | 
 compilation | boolean                     | 
 name        | varchar(300)                | 

РЕДАКТИРОВАТЬ Я использую PHP, и я не против какой-либо сортировки или других hijinx в скрипте, моя проблема № 1 - скорость возврата.

Ответы [ 6 ]

3 голосов
/ 05 августа 2011

Если вам нужна скорость, я бы посоветовал вам взглянуть на Solr / Lucene. Вы можете хранить свои данные и быстро выполнять поиск, вызывая Solr и анализируя результат из PHP. И в качестве дополнительного преимущества вы также получаете граненый поиск (который является задачей 2 вашего вопроса, если я правильно его интерпретирую). Недостатком является то, что у вас может быть избыточная информация (однажды сохраненная в БД, однажды в хранилище документов Solr). И для настройки требуется некоторое время (ну, вы могли бы многому научиться из интеграции с Drupal Solr).

Просто посмотрите справочные документы по PHP для Solr .

Вот статья о том, как использовать Solr с PHP, на всякий случай: http://www.ibm.com/developerworks/opensource/library/os-php-apachesolr/.

2 голосов
/ 10 августа 2011

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

Прежде всего - ваша структура таблиц форсирует JOIN - я думаю, что вы должны избегать их, если это возможно, при написании высокопроизводительных приложений.Я не знаю, "attribute_tag_groups" есть, поэтому я предлагаю структуру таблицы: тег (varchar 255), id (int), id_type (enum (трек, альбом, исполнитель)).Идентификатор может быть artist_id, track_id или album_id в зависимости от id_type.Таким образом, вы сможете также собрать все ваши данные в одну таблицу, но, конечно, она будет использовать гораздо больше памяти.

Далее - вам следует рассмотреть возможность использования нескольких баз данных.Это поможет еще больше, если каждая база данных содержит только часть ваших данных (каждый поиск будет быстрее).Принятие решения о том, как распределить ваши данные между базами данных, обычно является довольно сложной задачей: я предлагаю вам составить некоторую статистику о длине тега, найти диапазоны длины, которые позволят получить аналогичные результаты трассировки / исполнителей, и жестко закодировать их в код поиска.

Конечно, вы должны рассмотреть настройку MySql (я уверен, что вы сделали это, но на всякий случай) - все ваши таблицы должны находиться в оперативной памяти - если это невозможно, попробуйте получить диски SSD, рейды и т. Д. Правильная индексация и база данныхтипы / настройки также очень важны (MySql может даже показать некоторые узкие места во внутренней статистике).

Это предложение может показаться безумным - но иногда полезно позволить PHP выполнить некоторые вычисления, которые MySql может выполнить сам.Базы данных MySql гораздо сложнее масштабировать, а сервер для обработки PHP может быть добавлен в считанные минуты.И разные потоки PHP могут работать на разных ядрах процессора - у MySql с этим проблемы.Вы можете повысить производительность PHP с помощью некоторых расширенных модулей (вы даже можете написать их самостоятельно - профилировать свои PHP-скрипты и узкие места жесткого кода в быстром C-коде).

Последнее, но я думаю, что самое важное - вы должен использовать какой-то тип кэширования.Я знаю, что это действительно сложно, но я не думаю, что был какой-то большой проект без действительно хорошей системы кэширования.В вашем случае некоторые теги, безусловно, будут намного более популярны, чем другие, поэтому это должно значительно повысить производительность.Кэширование является формой искусства - в зависимости от того, сколько времени вы можете на него потратить и сколько ресурсов доступно, вы можете сделать 99% всех запросов, использующих кеш.

Использование других баз данных / инструментов индексирования может помочь вам,но вы всегда должны учитывать теоретическое сравнение скорости запросов (O (n), O (nlog (n)) ...), чтобы понять, могут ли они действительно помочь вам - использование этих инструментов иногда дает вам низкий прирост производительности (например, постоянные 20%), но они могут усложнить разработку вашего приложения, и в большинстве случаев оно того не стоит.

2 голосов
/ 08 августа 2011

Возможно, вам следует попытаться денормализовать ваши данные. Ваша структура оптимизирована для вставки / обновления загрузки, но не для запросов. Как я понял, у вас будет гораздо больше запросов на выборку, чем запросов на вставку / обновление.

Например, вы можете сделать что-то вроде этого:

храните ваши данные в нормализованной структуре.

создать такую ​​таблицу, как эта

  track_id, artist_tags, album_tags, track_tags
   1 , jazz/pop/,  jazz/rock, /heavy-metal/  

    or 

    track_id, artist_tags, album_tags, track_tags
    1 , 1/2/,  1/3, 4/

для ускорения поиска вам, вероятно, следует создать индекс FULLTEXT для столбцов * _tags

запросить эту таблицу с помощью sql, как

select * from aggregate where album_tags  MATCH (track_tags) AGAINST ('rock')

перестраивать эту таблицу постепенно, один раз в день.

1 голос
/ 14 августа 2011

По моему опыту, самая «медленная» база данных MySQL не имеет правильного индекса и / или запросов. Поэтому я бы сначала проверил это:

  1. Убедитесь, что все поля идентификатора данных talbes являются первичным индексом. На всякий случай.
  2. Для всех таблиц данных создайте индекс для полей внешнего идентификатора, а затем идентификатор, чтобы MySQL мог использовать его в поиске.
  3. Для ваших таблиц склеивания: установите первичный ключ в двух полях, сначала тему, затем тег. Это для нормального просмотра. Затем создайте нормальный индекс по тегу id. Это для поиска.
  4. Все еще медленно? Вы используете MyISAM для своих таблиц? Он предназначен для быстрых запросов.
  5. Если все еще медленно, запустите EXPLAIN для медленного запроса и опубликуйте и запрос, и результат в вопросе. Желательно с импортируемым дампом sql вашей полной структуры базы данных.
0 голосов
/ 15 августа 2011

Проверьте свои индексы и правильно ли они используются.Может быть, MySQL не подходит для этой задачи.PostgreSQL должен быть похожим на использование, но он имеет лучшую производительность в сложных ситуациях.

На совершенно другом пути, Google Map-Reduction и использовать одну из этих новых модных баз данных без SQL для действительно действительнобольшие наборы данных.Это может выполнять распределенный поиск на нескольких серверах параллельно.

0 голосов
/ 08 августа 2011

Вещи, которые вы можете попробовать:

  • Используйте Query Analyzer , чтобы исследовать узкие места ваших запросов.(В большинстве случаев базовая DBS выполняет потрясающую работу по оптимизации)

  • Структура вашей таблицы хорошо нормализована, но личный опыт показал, что вы можете архивировать гораздо более высокие уровни производительности со структурами, которыепозволяют вам избегать объединений и вложенных запросов.Для вашего случая я бы предложил хранить информацию тега в одном поле.(Это требует поддержки со стороны базовой DBS)

Пока.

...