Задать вопрос

Как переписать структуру таблиц или запросы, чтобы работал индекс для моих выборок?

Краткое описание того, что имеем.

Собираем некоторую статистику посещений веб-сайтов. Таблица посещений, выглядит в упрощенном виде так:
Table: links
id | url                                         | domain
1  | https://www.youtube.com/watch?v=6Nu3ZVA8Gic | com.youtube.www
2  | https://www.youtube.com/watch?v=5ww70Xb5pm8 | com.youtube.www
3  | http://www.bbc.com/ukrainian/politics       | com.bbc.www
4  | http://bbc.com/ukrainian/business           | com.bbc


Почему домен записываем в обратном порядке? Потому что у нас есть еще таблица с информацией по крупным сайтам, например:

Table: sites
id | name     | domain      | description
1  | YouTube  | com.youtube | ...
2  | VKontake | com.vk      | ... 
3  | BBC      | com.bbc     | ...


И нам легко получать статистику посещений отдельных крупных сайтов так, чтобы работали индексы MySQL. Например, получаем ссылки по сайту BBC (включая возможные субдомены):

SELECT id, url FROM links 
WHERE domain = 'com.bbc' OR domain LIKE 'com.bbc.%'


Суть вопроса.

Всё было нормально, пока не таблицы не выросли до многих миллионов записей (но и в этом случае приведенный выше пример работает быстро) и не стали появлятся дополнительные задачи обработки статистических данных.

Например, нам надо выбрать какое-то кол-во ссылок, вместе с соответствующей им информацией по сайту. Делаем следующее:

SELECT links.id, links.url, sites.id AS site_id, sites.description 
FROM links
LEFT JOIN sites ON links.domain = sites.domain 
             OR links.domain LIKE CONCAT(sites.domain, '.%')


И естественно из-за использования LIKE CONCAT в JOIN перестает использоваться индекс для links.domain.
Какое-то время, когда было не слишком много записей в обеих таблицах, мы неспешно просчитывали статистику фоновыми задачами. Но сейчас даже просчитывать в фоне - не вариант, уж очень долго, и уж слишком ресурсоемко.

Так что ищу совета, может как-то перестроить структуру? Или что-то сделать с запросами так, чтобы заставить использовать индексы (USE INDEX и FORCE INDEX не хотят работать в моем случае).
А так же важен совет, какой лучше движок использовать в моем случае MyISAM или InnoDB?
  • Вопрос задан
  • 439 просмотров
Подписаться 4 Оценить Комментировать
Решения вопроса 1
@ollisso
Для начала объясню почему индексы в вашем случае не работают и не могут работать.

> OR links.domain LIKE CONCAT(sites.domain, '.%')

CONCAT - это функция, и вы работаете с результатом функции.
Т.е. получается что в вашем запросу нужно:
1. выбрать все строки из links
2. подсодеденить к каждой строке по sites.domain или sites.domain результат фунции.
=> нужно посчитать каждую строку каждый раз.
Это очень много.

что я бы сделал:
1. создал таблицу доменов
в ней:
id | main_id |domain
1 | 1 | com.youtube
2. | 1 |com.youtube.www
3. | 1 |com.youtube.subdomain


Во всех таблицах - перешёл бы на этот ключ.
2. тогда ваша выборка сокращается до:

SELECT links.id, links.url, sites.id AS site_id, sites.description 
FROM links
LEFT JOIN domains ON links.domainId = domains.id
LEFT JOIN sites ON sites.id = domains.main_id

(смутно понимаю что вы хотели в этом запросе, поэтому уж не обессудьте :) )

Т.е. основной посыл:
переходите на int- ключи

PS: Так же это называется нормализация базы данных. Хранить много одинаковых строк - это плохо.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
sim3x
@sim3x
3НФ будет выглядеть так
domain_zone:
  parent = ForeignKey(domain_zone)
  name = Text

site_page
  domain_zone = ForeignKey(domain_zone)  
  url = URL


Суть такова: есть корневая доменная зона "." точка.
Есть доменная зона com, у нее parent точка
У сайта bbc.com parent будет указывать на com

Вобщем делаем аналогично DNS
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы