Модераторы: skyboy
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Оптимизация запроса, Обработка графов 
:(
    Опции темы
Stolzen
Дата 21.5.2014, 13:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1041
Регистрация: 17.10.2005

Репутация: нет
Всего: 48



Доброго всем дня!

Есть у меня большой социальный граф - 1.6 млн пользователей (вершины графа) и 22 млн связей между ними (ребра)
Так же есть около 1 млн пар (юзер, юзер), для которых нужно посчитать некоторые значение - все это будет использоваться для link prediction. 

Имеются следующие таблицы (DDL):
  • users(user): ключ на user
  • edges(to, from): две таблицы с одинаковыми данными, ключ в первой  (to, from), а во второй (from, to)
  • membership(user, community): ид групп в которых состоят пользователи
  • target edges - 1.1 млн пар, для которых нужно выполнить запрос ниже
Вот, собственно, запрос:
Код
select pairs.a, pairs.b,
    (select count(*)
    from edges_src followed_a, edges_src followed_b
    where pairs.a = followed_a.source AND pairs.b = followed_b.source AND
        followed_a.target = followed_b.target) as out_degree_frieds,
    (select count(*)
    from edges_trg follow_a, edges_trg follow_b
    where pairs.a = follow_a.target AND pairs.b = follow_b.target AND
    follow_a.source = follow_b.source) as in_degree_frieds,
  (select count(*) from
          edges_trg follow_a, edges_trg follow_b,
          edges_src followed_a, edges_src followed_b
     where
          pairs.a = follow_a.target AND
          pairs.b = follow_b.target AND
          follow_a.source = follow_b.source AND
          pairs.a = followed_a.source AND
          pairs.b = followed_b.source AND
          followed_a.target = followed_b.target AND
          follow_a.source = followed_a.target) as bi_degree_frieds,
    @follow_a := (select count(*) from edges_trg where pairs.a = target) as follow_a,
    @follow_b := (select count(*) from edges_trg where pairs.b = target) as follow_b,
    @follow_a * @follow_b as prf_att_score,
  (select count(*)
    from membership ma, membership mb
    where ma.user = pairs.a and mb.user = pairs.b and ma.community = mb.community)
        as same_com_cnt,
  (select count(distinct source)
        from edges_trg
        where target = pairs.a or target = pairs.b) as tot_friens_in,
    (select count(distinct target)
        from edges_src
        where source = pairs.a or source = pairs.b) as tot_friens_out,
    (select count(distinct s.target)
        from edges_src s, edges_trg t
        where (s.source = pairs.a or s.source = pairs.b) and t.target = s.source and
            s.target = t.source) as tot_friens_bi
from target_set pairs limit 0, 1000;

Вот Explain Plan этого запроса

Запрос будет выполнятся пачками по 1000 шт, т.к. вычисляется долго, чтобы соединение не отваливалось.

Как можно оптимизировать этот запрос, чтобы он выполнялся хотя бы минуту для 1 тыс строк? Сейчас вычисление отваливаются по таймауту после 10 мин

Добавлено через 1 минуту и 15 секунд
Немного подробнее про запрос

Граф этот направленный, поэтому правильнее сказать, что в графе не друзья, а "подписчики"

Код

(select count(*)
from edges_src followed_a, edges_src followed_b
where pairs.a = followed_a.source AND pairs.b = followed_b.source AND
    followed_a.target = followed_b.target) as out_degree_frieds,


Кол-во людей на которых подписаны и а и б

Код

(select count(*)
from edges_trg follow_a, edges_trg follow_b
where pairs.a = follow_a.target AND pairs.b = follow_b.target AND
    follow_a.source = follow_b.source) as in_degree_frieds,


Кол-во людей подписавшихся на а и б


Код
(select count(*) from
  edges_trg follow_a, edges_trg follow_b,
  edges_src followed_a, edges_src followed_b
where
  pairs.a = follow_a.target AND
  pairs.b = follow_b.target AND
  follow_a.source = follow_b.source AND
  pairs.a = followed_a.source AND
  pairs.b = followed_b.source AND
  followed_a.target = followed_b.target AND
  follow_a.source = followed_a.target) as bi_degree_frieds,


кол-во людей в пересечении прошлых двух запросов 


Код
@follow_a := (select count(*) from edges_trg where pairs.a = target) as follow_a,
@follow_b := (select count(*) from edges_trg where pairs.b = target) as follow_b,
@follow_a * @follow_b as prf_att_score,


кол-во подписчиков на а умножить на кол-во подписчиков на б

Код
(select count(*)
  from membership ma, membership mb
  where ma.user = pairs.a and mb.user = pairs.b and ma.community = mb.community)
      as same_com_cnt,


Кол-во групп, в которых состоят оба пользователя


Код
(select count(distinct source)
  from edges_trg
  where target = pairs.a or target = pairs.b) as tot_friens_in,


кол-во подписчиков на а и на б

Код
(select count(distinct target)
  from edges_src
  where source = pairs.a or source = pairs.b) as tot_friens_out,


кол-во людей на которых подписаны и а и б

Код
(select count(distinct s.target)
  from edges_src s, edges_trg t
  where (s.source = pairs.a or s.source = pairs.b) and t.target = s.source and
      s.target = t.source) as tot_friens_bi


кол-во людей из пересечения прошлых двух запросов


--------------------
datatalks.ru - анализ данных, статистика, машинное обучение
PM MAIL WWW   Вверх
tzirechnoy
Дата 21.5.2014, 20:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1173
Регистрация: 30.1.2009

Репутация: 3
Всего: 16



0) LIMIT без ORDER -- безсмысленнен и вреден. Если Вам кажэтся, что он делает что-то полезное -- то Вы ошыбаетесь.
0.5) Да и вообще лучшэ его не использовать. Кривая конструкцыя.
Надо вам тут такой шардинг? Ну, вставьте номер в эту табличку pairs или ещё как-то её виртаульно поделите на кусочки.
1) Оптимизируйте по одному запросу. Сейчас я совершэнно не могу понять в plan, кто там от кого стоял. В смысле -- какой dependenet subquery относится к какому запросу. Ну, не совершэнно -- часто, конечно, имена проскакивают, но сложно это всё.
Кроме того, так Вы сможэт понять, какой из запросов выполняется быстро, а какой -- нет, и требует доводки или промежуточных таблиц.
2) Оставьте одну таблицу edges, с двумя индэксами. См. CREATE INDEX.
3) Постарайтесь выполнить этот запрос на компьютэре, в котором всё содержымое базы влезает в память. Кажэтся, 8GB должно хватить.
PM MAIL   Вверх
Stolzen
Дата 21.5.2014, 22:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1041
Регистрация: 17.10.2005

Репутация: нет
Всего: 48



Спасибо за ответ

Цитата(tzirechnoy @  21.5.2014,  21:34 Найти цитируемый пост)
0) LIMIT без ORDER -- безсмысленнен и вреден. Если Вам кажэтся, что он делает что-то полезное -- то Вы ошыбаетесь.

А как еще можно выполнять этот запрос кусочками по 1000? Добавить id и добавлять в where фильтр по этому id? 


Цитата(tzirechnoy @  21.5.2014,  21:34 Найти цитируемый пост)
1) Оптимизируйте по одному запросу. Сейчас я совершэнно не могу понять в plan, кто там от кого стоял. В смысле -- какой dependenet subquery относится к какому запросу. Ну, не совершэнно -- часто, конечно, имена проскакивают, но сложно это всё.
Кроме того, так Вы сможэт понять, какой из запросов выполняется быстро, а какой -- нет, и требует доводки или промежуточных таблиц.

Сейчас я понял, что именно последние три запроса вызывают проблему - если их убрать, то 1000 строк считаются за 40 секунд. 


Что еще интересно

Код
select count(distinct source)
    from edges_trg
    where target = 1611621 or target = 1230340;

select pairs.a, pairs.b,
    (select count(distinct source)
        from edges_trg
        where target = pairs.a or target = pairs.b) as tot_friens_in
from model_test_set pairs limit 0, 1;


Эти два запроса вычисляют одно и то же значение, только первый это делает за доли секунды (мускль пишет 0.000 сек), а второй - 12.808. 

Первый 
user posted image

Второй
user posted image

Если убрать or, то второй запрос так же выполняется мгновенно 

Код
select pairs.a, pairs.b,
    (select count(distinct source)
        from edges_trg
        where target = pairs.a) as tot_friens_in
from model_test_set pairs limit 0, 1;


user posted image

Какая принципиальная разница между этими двумя запросами? Он во втором full scan делает? Судя по кол-ву строк в dependent subquery   




--------------------
datatalks.ru - анализ данных, статистика, машинное обучение
PM MAIL WWW   Вверх
Stolzen
Дата 21.5.2014, 22:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1041
Регистрация: 17.10.2005

Репутация: нет
Всего: 48



Цитата(tzirechnoy @  21.5.2014,  21:34 Найти цитируемый пост)
2) Оставьте одну таблицу edges, с двумя индэксами. См. CREATE INDEX.

В этом случае один из индексов будет unclustered, т.е. по одной дополнительной I/O операции на каждый index lookup - что на таких объемах будет заметно. Или я не прав? 


Цитата(tzirechnoy @  21.5.2014,  21:34 Найти цитируемый пост)
3) Постарайтесь выполнить этот запрос на компьютэре, в котором всё содержымое базы влезает в память. Кажэтся, 8GB должно хватить. 

Т.е. для всех таблиц из запроса сделать engine=MEMORY и запустить? У меня как раз 8 гб, но когда я попробовал сунуть таблицу edges целиком в память (выделил под heap таблицы 2 гб - влезло) - получилось даже медленнее. При этом для таблицы в памяти я сделал два индекса. Видимо я что-то не так сделал?


--------------------
datatalks.ru - анализ данных, статистика, машинное обучение
PM MAIL WWW   Вверх
Stolzen
Дата 22.5.2014, 00:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1041
Регистрация: 17.10.2005

Репутация: нет
Всего: 48



Цитата(Stolzen @  21.5.2014,  23:21 Найти цитируемый пост)
Видимо я что-то не так сделал? 

Очевидно не так. По умолчанию в MEMORY в качестве индекса используется HASH а не BTree, поэтому запрос выполнялся совсем не так, как я предполагал. В итоге добавил в табличку два BTree индекса на (source, target) и (target, source) и все заработало. 5 тыс строк за 20 сек считаются. Возможно еще есть куда дальше оптимизировать, но такой прирост производительности сейчас более чем устраивает. 

Спасибо большое за помощь.


--------------------
datatalks.ru - анализ данных, статистика, машинное обучение
PM MAIL WWW   Вверх
tzirechnoy
Дата 22.5.2014, 08:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1173
Регистрация: 30.1.2009

Репутация: 3
Всего: 16



Цитата
Добавить id и добавлять в where фильтр по этому id? 


Например. Это был первый мой вариант. Второй -- ну, поделите в уме pairs.a на отрезки, и указывайте BETWEEN в WHERE.

Цитата
Т.е. для всех таблиц из запроса сделать engine=MEMORY и запустить?


Да нет, этого вобще говоря не требуется. Вот пройти их каким-нибудь index range scanом, чтобы соответствующие индэксы цэликом закачались в память -- вот это было бы полезно.

PM MAIL   Вверх
Stolzen
Дата 25.5.2014, 16:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1041
Регистрация: 17.10.2005

Репутация: нет
Всего: 48



Цитата(tzirechnoy @  22.5.2014,  09:06 Найти цитируемый пост)
Да нет, этого вобще говоря не требуется. Вот пройти их каким-нибудь index range scanом, чтобы соответствующие индэксы цэликом закачались в память -- вот это было бы полезно.

А что это значит? И как это можно сделать? 

Проблема уже решена, но все равно интересно smile 


--------------------
datatalks.ru - анализ данных, статистика, машинное обучение
PM MAIL WWW   Вверх
tzirechnoy
Дата 25.5.2014, 22:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1173
Регистрация: 30.1.2009

Репутация: 3
Всего: 16



<quote>&gt;А что это значит? И как это можно сделать? </quote>

Вот то и значит -- сочинить такой запрос, чтобы все эти индэксы из дискового кэша переместились в RAM. Сделать можно по-разному -- либо написать такой запрос, кстати, есть ещё вариант -- тупо прочитать соответствующий файл в файловой системе.
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | MySQL | Следующая тема »


 




[ Время генерации скрипта: 0.0680 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.