SQLinfo.ru - Все о MySQL

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#1 17.12.2010 13:08:04

Михаил)
Участник
Зарегистрирован: 17.12.2010
Сообщений: 14

Хранение массивов ключевых слов от множества веб-сайтов

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

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

   Таблица `www`:
    id     int(11)    primary key, auto_increment
    url     varchar(64)     utf8_general_ci, INDEX
    ...
    keywords     text     utf8_general_ci
    ...
    favicon     char(23)     utf8_general_ci
    screenshots     char(23)     utf8_general_ci

Массив ключевых слов хранится в виде:  greeting cards|5,holidays|17,labor|8,greeting|5   , где через символ "|" хранится его вес на странице. Количество ключевых слов для каждого веб-сайта ограничено 15 словами.

И по этому неиндексному полю выполняются запросы вида:
SELECT COUNT(*) AS `count` FROM `www` WHERE `keywords` LIKE '%holidays\\|%' - Запрашиваю кол-во сайтов с нужным ключевым словом и его вхождением.
SELECT `url` FROM `www` WHERE `keywords` LIKE '%holidays\\|%'  - Запрашиваю сами сайты с нужным ключевым словом и его вхождением.

Вполне естественно, что в первый же месяц BETA-выхода, сервис стал загибаться. Сейчас в таблице около 20 000 записей, в сутки прибавляется около 1,5-2 тысяч. В планах - продержаться на MySQL до нескольких(5-10?) миллионов записей, ежедневный прирост более, чем на 4 тысячи сайтов планирую не допускать.


Я вижу для себя такие способы перестроения базы:


Вариант 1:  Убать упоминание о keywords из основной таблицы `www` и вынести keywords в отдельную таблицу вида:
    id                       bigint(20)           primary key, auto_increment
    site_id              bigint(20)           INDEX
    keyword            varchar(256)     INDEX
    ves                    tinyint(2)         

Таким образом запросы примут вид:
SELECT `keyword` WHERE `site_id`='3846',  т.е. выборка будет производиться по индексному полю.

При таком варианте для каждого сайта из таблицы `www` будет создаваться до 15 записей в таблице ключевых слов. Если хранение 2-х миллионов записей о веб-сайтах я могу себе представить, то 30 миллионов записей в таблице мне откровенно кажется утопией. К тому же смущает дублирование одного и того же `keyword` для нескольких `site_id`.



Вариант 2: Опять же убрать упоминания о ключевых словах из основной таблицы и создать две вспомогательных:

а) Таблица соответствия ключевых слов веб-сайту:
    id                             bigint(20)           primary key, auto_increment
    site_id                    bigint(20)           INDEX
    keyword_id            bigint(20)           INDEX
    ves                          tinyint(2)         

б) Таблица самих ключевых слов.
    id                             bigint(20)           primary key, auto_increment
    keyword                  varchar(256)     UNIQUE


При такой структуре запросы примут такой вид:
Запрос, количества сайтов с заданным ключевым словом вначале определяет id ключевого слова в таблице "б" и затем запрашивает в таблице "а" количество записей WHERE `keyword_id`= id из таблицы "б"
Запрос выбирающий сайты по ключевому слову также вначале определяет id ключевого слова в таблице "б", затем запрашивает в таблице "а" `site_id` сайтов в которых присутствует ключевое слово и лишь после этого выбирает сайт из основной таблицы `www` по первичному ключу id.

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



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

Версия сервера: 5.0.51a-24+lenny4
Расположено на VDS, ОС: Debian


Заранее благодарю за внимание)

Неактивен

 

#2 17.12.2010 15:05:49

rgbeast
Администратор
MySQL Authorized Developer and DBA
Откуда: Москва
Зарегистрирован: 21.01.2007
Сообщений: 3878

Re: Хранение массивов ключевых слов от множества веб-сайтов

У вас ключевые слова не очень длинные, поэтому разница между вариантами 1,2 небольшая (она больше, если используется utf8). Второй вариант предпочтителен, так как вы сокращаете длину записи в самой большой таблице, что позитивно влияет на кэширование. Насчет лишнего запроса беспокоиться не стоит, если в таблице 2 сделать индекс на префикс от keyword, поиск в ней будет быстрым. В качестве хранилища думаю подойдет MyISAM.

Таблица в 30 миллионов записей не утопия. См. сообщение http://webew.ru/posts/2699.webew , в котором упомянуто, что MySQL отлично справляется с таблицей в 3 миллиарда записей, каждая 11 байт длиной (в том примере поиск по первичному ключу выполняется несколько секунд).

Структуру таблицы я бы изменил. id в ней не нужен, если используется MyISAM. Также не требуется bigint, достаточно int и mediumint.

CREATE TABLE site_key (
   site_id mediumint(11) UNSIGNED NOT NULL,
   keyword_id int(11) UNSIGNED NOT NULL,
   KEY(site_id, keyword_id),
   KEY(keyword_id,site_id)
) ENGINE=MyISAM;


Составные ключи здесь для того, чтобы эффективно использовался key_buffer (все запросы смогут выполняться прямо по индексу, не обращаясь к таблице). Если они будут создавать слишком сильную нагрузку, их можно поменять на простые, но до тестирования предложенная схема выглядит оптимальной.

Неактивен

 

#3 17.12.2010 21:16:00

Михаил)
Участник
Зарегистрирован: 17.12.2010
Сообщений: 14

Re: Хранение массивов ключевых слов от множества веб-сайтов

rgbeast, Большое спасибо! Полез делать, обязательно отпишусь о результатах.

Неактивен

 

#4 02.01.2011 16:25:28

Михаил)
Участник
Зарегистрирован: 17.12.2010
Сообщений: 14

Re: Хранение массивов ключевых слов от множества веб-сайтов

Поздравляю всех участников с праздниками! Как обещался, пишу о результатах.
Воспользовался советом rgbeast, после работ база выглядит следующим образом:

Таблица keywords: хранит ключевые слова

Структура:
id     int(11)    
keyword     varchar(256)    

Индексы:
PRIMARY     PRIMARY       id
keyword     UNIQUE     keyword

Используемое пространство:
Данные     5,176     КБ
Индекс     6,790     КБ
Всего     11,966     КБ

Строки     152,301
Длина строки ø     34



Таблица site_key: хранит соответствие сайтов и ключевых слов

site_id     mediumint(11)
keyword_id     int(11)
ves             smallint(5)


Индексы:
site_id     INDEX     296070      site_id
keyword_id

keyword_id     INDEX     296070      keyword_id
site_id


Используемое пространство:
Данные     2,891     КБ
Индекс     7,772     КБ
Всего     10,663     КБ
Строки     296,070
Длина строки ø     10
Размер строки ø     37 Байт


Таблица сводных данных о проанализированном сайте не хранит упоминания о ключевых словах. Соответствия "подтягиваются" из таблицы site_key.

Все таблицы в базе имеют тип MyISAM.

Время выполнения запросов при такой структуре сократилось более чем разительно. Используемые запросы ниже:

1) Выбор количества сайтов по ключевому слову:
SELECT COUNT(*) AS `count`
FROM `site_key`, `keywords`
WHERE keywords.id=site_key.keyword_id AND keywords.keyword='Videos'

2) Выбор ключевых слов для заданного сайта:
SELECT *
FROM `keywords` , `site_key`
WHERE keywords.id=site_key.keyword_id AND `site_id` = '24459'

3) Выбор списка сайтов (некоторых их параметров) по ключевому слову:
SELECT `url`, `title`, `description`, `favicon` FROM `www`, `site_key` , `keywords` WHERE
keywords.keyword='Videos' AND
keywords.id=site_key.keyword_id AND
site_key.site_id=www.id AND
www.status!='b'
GROUP BY site_key.site_id
LIMIT 0, 30


Размер таблиц сократился многократно, Время запросов, как и писал выше, - "небо и земля", ранее сайт практически "умирал".
Интересна экономия на таблице ключевых слов: 296,070 соответствий при 152,301 ключевых словах. Т.е. метод хранения ключевых слов и их соответствий в разных таблицах значительно экономнее хранения "ключевое слово - id сайта" с дублированием повторяющихся ключевых  слов.

Уважаемые гуру, если увидите в структуре или запросах что-то заслуживающее оптимизации - напишите пожалуйста.

Спасибо!)

Отредактированно Михаил) (02.01.2011 16:40:03)

Неактивен

 

#5 07.01.2011 20:42:58

Михаил)
Участник
Зарегистрирован: 17.12.2010
Сообщений: 14

Re: Хранение массивов ключевых слов от множества веб-сайтов

Неужели нет зацепок и не к чему придраться?)

Неактивен

 

#6 09.01.2011 02:35:51

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6756

Re: Хранение массивов ключевых слов от множества веб-сайтов

Нет смысла придираться для галочки. Оптимизировать нужно тогда, когда что-то
плохо работает. Когда всё работает хорошо — оптимизировать не нужно smile

Особенно это актуально с первого по десятое января, правда? wink

Неактивен

 

#7 11.01.2011 17:04:24

jmadhead
Участник
Зарегистрирован: 11.01.2011
Сообщений: 10

Re: Хранение массивов ключевых слов от множества веб-сайтов

Просто как вариант. Для таблицы ключевых слов можно попробовать определить два уровня. Т.е. добавить колонку parent_id. Будет два типа записей: основная часть слова и окончание. Допустим сейчас может храниться так:
id  | keyword
-------------
1   | кресло
2   | кресла
3   | креслу
то есть эти три слова считаются разными, хотя в них различаются только последние буквы. Что предлагаю я:
id  | parent_id | keyword
-------------------------
1   | 0           | кресл
2   | 1           |  а
3   | 1           |  о
4   | 1           |  у
очевидно, что размер таблицы уменьшится (хотя количество строк увеличится). Насколько я помню подобные вещи называются "деревья поиска" или что-то вроде того.

Неактивен

 

#8 11.01.2011 21:41:45

Михаил)
Участник
Зарегистрирован: 17.12.2010
Сообщений: 14

Re: Хранение массивов ключевых слов от множества веб-сайтов

Спасибо за внимание к теме!
Такую обработку окончаний однозначно лучше выполнять самим скриптом, а не хранить в базе все возможные варианты слова.
Интересно применить алгоритм, позволяющий идентифицировать основную форму слова (именит. падеж, ед. число, для глаголов - инфинитив). После определения, можно удалить неосновные формы и поменять соответствия сайтов новому слову.
Применив такой алгоритм при определении ключевых слов, можно было бы немало сэкономить на количестве хранимых ключевых слов.

Например: Выбираем по LIKE %..% : доктор, доктору, доктора, докторишка.  Сокращаем до "доктор".

Неактивен

 

#9 12.01.2011 09:38:37

jmadhead
Участник
Зарегистрирован: 11.01.2011
Сообщений: 10

Re: Хранение массивов ключевых слов от множества веб-сайтов

Кстати говоря есть ещё нейронные сети. Они неплохо выполняют задачу кластеризации (то есть разбиения на схожие группы)...хотя это врятли подойдёт для высоконагруженной системы на php

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson