forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   PHP / MySQL (http://forum.boolean.name/forumdisplay.php?f=135)
-   -   [MySql] Оператор сравнения IN (http://forum.boolean.name/showthread.php?t=19979)

pax 03.09.2015 10:51

[MySql] Оператор сравнения IN
 
Что-то не могу нагуглить, есть ли у оператора IN ограничения. Сколько значений может принимать этот оператор? 100 принимает, проверено, а 1000? И стоит ли обновлять записи в базе таким запросом на 1000 вхождений или лучше 10 запросов по 100?

moka 03.09.2015 14:03

Ответ: [MySql] Оператор сравнения IN
 
Скорость работы IN, в зависимости от запроса может умножать сложность запроса кратной элементов в IN.
Так что если делаешь запрос на IN 10 ID например, то это скорее всего приведет к 10 кратному усложнению запроса, следственно в 10 дольше будет генерировать ответ. И это в хорошем случае когда индексы используются.
Без индексов все может быть похуже, если первым используется другое поле, и затем из профильтрованных значений нужно делать проверку. То либо будет создаваться мелкий временный индекс по меньшему списку проверямых данных, и еще одна итерация по полям для сравнения, либо вообще O(a*b), что я надеюсь в дб движке избежали используя временный индекс.
Если используется временный индекс, то это использует RAM, индексы обычно легкие.

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

pax 03.09.2015 17:03

Ответ: [MySql] Оператор сравнения IN
 
У меня задача - сервер оповещений оптимизировать. Я беру из базы LIMIT offset, 1000 записей, по ним вызываю оповещение в соц сети, потом мне надо этим записям записать дату последней нотификации. Так по всей базе пользователей данной соц сети. Пользователи других соц сетей тоже в этой базе приустствуют. По полю user_id из соц сети у меня есть индекс.

moka 03.09.2015 17:20

Ответ: [MySql] Оператор сравнения IN
 
Немного запутался когда ты говоришь "база". Ты имеешь ввиду "таблица"?
У тебя в одной таблице все пользователи, с колонкой "соц_сеть"?

И тебе нужно пробежаться по всем пользователям определенной соц сети, послать им оповещение (соц. сети API), и затем записать дату оповещения?

Я не совсем вижу вообще зачем тебе IN здесь.
Сортируешь по user_id (он не изменный и только увеличивается). И далее делаешь выборку например 128 пользователей, далее шлешь им оповещания и обновляешь им notification_date, при этом тут проще будет использовать user_id больше и меньше чем первый и последний в этом списке.

Затем делаешь снова ту же выборку, только уже с дополнительным условием: user_id > last_user_id - где last_user_id самый большой последний user_id. И повторяешь все те же операции как и раньше.
И так по циклу, пока возвратимый результат не вернет 0 записей.

Что-то типо (псевдокод)

PHP код:

var last_user_id 0;

// repeat as long as there are users
while(true) {
    
// select batch of users
    
users "SELECT users WHERE user_id > " last_user_id " AND social_network = 'vk' ORDER BY user_id ASC LIMIT 128";

    
// no more users left to notify
    
if (users.length === 0)
        break;

    
// send notification
    
for(user in users)
        
send_notification(user)

    
// get first user_id and last user_id
    
var first_user_id users[0].id;
    
last_user_id users[users.length 1].id;

    
// update notification date of users
    
"UPDATE users WHERE user_id >= " first_user_id " AND user_id <= " last_user_id " SET notification_date = date";


Такой подход масштабируется с любым размером таблицы. Если больше записей будет, он всего лишь займет немного дольше времени.
Также увеличивая размер batch'а (128 в данном примере), ты больше заполнишь оперативки, но можешь быстрее обработать всю таблицу, снизив число запросов и парсинга.

Также такие вещи нужно писать не в блокируемом языке, с учетом того что send_notification может блокировать. Если он блокирует (PHP или Python), то у тебя этот скрипт займет ужасно много времени.
В идеале тебе нужно делать скрипт где send_notification выполняется паралельно с ограниченным числом.
Также лучше вообще параллелить батчи тоже, паралельно обрабатывая скажем 8 пачек по 128 пользователей.

В node.js такое легко параллелизуется, и будет очень шустро работать.

pax 03.09.2015 17:29

Ответ: [MySql] Оператор сравнения IN
 
Цитата:

Сообщение от moka (Сообщение 299277)
Немного запутался когда ты говоришь "база". Ты имеешь ввиду "таблица"?
У тебя в одной таблице все пользователи, с колонкой "соц_сеть"?

И тебе нужно пробежаться по всем пользователям определенной соц сети, послать им оповещение (соц. сети API), и затем записать дату оповещения?

Сорри, да, так и есть.

Но тут дело такое - я выбираю только тех пользователей, которые логинились менее месяца назад (во время оповещения кто-то может залогиниться к примеру и нарушить порядок). Плюс АПИ соц сети возвращает id пользователей, которым дошло оповещение. Хотелось бы записать только тех, кому действительно оно дошло.

Сервер нотификаций работал в отдельном потоке, так что на работу основного сервера не влиял. Сейчас я его в отдельное приложении вынес. Все пишу на C# (Debian/Mono).

moka 03.09.2015 18:38

Ответ: [MySql] Оператор сравнения IN
 
Цитата:

Сообщение от pax (Сообщение 299278)
Но тут дело такое - я выбираю только тех пользователей, которые логинились менее месяца назад (во время оповещения кто-то может залогиниться к примеру и нарушить порядок).

Не нарушит, т.к. у тебя идет четкий порядок по user_id, который не изменный.
Следственно в условие запроса можешь добавить что пользователь логинился тогда-то. И даже если кто-то залогинится, это ок, т.к. у тебя сортировка и условие зависят от user_id.

Цитата:

Сообщение от pax (Сообщение 299278)
Плюс АПИ соц сети возвращает id пользователей, которым дошло оповещение. Хотелось бы записать только тех, кому действительно оно дошло.

А для посылки notification, ты в API шлешь массив с id'шками, или каждому пользователю индивидуально?
Главное не запускай этот скрипт рассылки пока не закончилось выполнение прошлого запуска, и тогда не будет конфликтов.

Цитата:

Сообщение от pax (Сообщение 299278)
Сервер нотификаций работал в отдельном потоке, так что на работу основного сервера не влиял. Сейчас я его в отдельное приложении вынес. Все пишу на C# (Debian/Mono).

Это хорошо чтобы не загрязнять основной процесс API.
Но нужна паралеллизация по задачкам в процессе. Но это не критично.
Зависит как быстро будет работать рассылка на данный момент, если медленно (10+ секунд), то стоит подумать о паралеллизации.

pax 04.09.2015 00:53

Ответ: [MySql] Оператор сравнения IN
 
Ух, ну и спор начался...

Цитата:

Сообщение от moka (Сообщение 299282)
Не нарушит, т.к. у тебя идет четкий порядок по user_id, который не изменный.

Запрос примерно такого плана:
PHP код:

"SELECT * 
FROM users 
WHERE sn = 'vk' AND DATE_ADD(last_login, INTERVAL 31 day) > NOW() 
ORDER BY id 
LIMIT @offset, 1000" 

Потом вызываю апи соц сети, в одном запросе не более 100 id. т.е. 10 раз. Это 10 https вызовов, время работы апи вконтакта от 6 ms до 100 ms к примеру. Еще количество запросов в секунду к апи соц сети ограничено. Т.е. пока я рассылаю нотификейшены для первых 100к прользователей среди них логинится десять. В результате я 10-и пользователям могу отправить по 2 нотификейшена (так как результаты поиска сдвигаются). В альтернативном варианте я могу выполнять SQL без дат, а даты обрабатывать в коде сервера (сейчас у меня так сделано). В этом случае проблем нет с отправкой, но с обновлением следующая:

В ответах api соц сети получаю id получивших. Это новый список (только часть из тех, кому пытался отправить). Вот их надо обновить, не всех в диапазоне min_id ... max_id. Отсюда IN и вопрос что лучше, 10 запросов по 100 или 1 на 1000. (1000 это в худшем случае, обычно меньше, т.к. многие пользователи отключают нотификейшены).

moka 04.09.2015 02:19

Ответ: [MySql] Оператор сравнения IN
 
Цитата:

Сообщение от pax (Сообщение 299300)
Ух, ну и спор начался...

Да жесть вообще :)


Цитата:

Сообщение от pax (Сообщение 299300)
Запрос примерно такого плана:
PHP код:

"SELECT * 
FROM users 
WHERE id > @last_largest_id AND sn = 'vk' AND DATE_ADD(last_login, INTERVAL 31 day) > NOW() 
ORDER BY id 
LIMIT 1000" 


Заместо использования offset, используй самый большой ID пользователя с последнего списка пользователей. Изначально 0.
Тогда не будет никто влезать если залогинятся.

Цитата:

Сообщение от pax (Сообщение 299300)
В ответах api соц сети получаю id получивших. Это новый список (только часть из тех, кому пытался отправить). Вот их надо обновить, не всех в диапазоне min_id ... max_id. Отсюда IN и вопрос что лучше, 10 запросов по 100 или 1 на 1000. (1000 это в худшем случае, обычно меньше, т.к. многие пользователи отключают нотификейшены).

Тут, UPDATE c id IN будет "ок", можешь смело делать на каждый ответ с api соц сети.

Если смотреть проще, можешь даже делать по одному запросу на LIMIT 100, обрабатывать его в другом потоке.
Далее если у тебя по времени влезает в ограничения соц. сети, то делаешь еще запрос, и так по кругу, пока не будешь упираться в ограничения по времени.
Таким образом можно будет и паралельно пачками слать, в итоге у тебя будет максимальная скорость рассылки. Т.к. работа с бд у тебя будет по любому в разы быстрее чем их ограничения на api.

Randomize 04.09.2015 03:23

Ответ: [MySql] Оператор сравнения IN
 
Pax, я повторюсь, используй EXPLAIN для профилирования запросов.
Там наглядно показано как что на производительность влияет.
А то тут уже непонятные споры на ровном месте.

pax 04.09.2015 10:47

Ответ: [MySql] Оператор сравнения IN
 
Цитата:

Сообщение от moka (Сообщение 299301)
Заместо использования offset, используй самый большой ID пользователя с последнего списка пользователей. Изначально 0.
Тогда не будет никто влезать если залогинятся.

Теперь понял, спасибо!

moka 06.09.2015 03:11

Ответ: [MySql] Оператор сравнения IN
 
Вынес оффтоп сюда: http://forum.boolean.name/showthread.php?t=19986

pax, было бы интересно если ты использовал бы EXPLAIN на все запросы твоего скрипта. И вообще поделился бы в итоге результатами скорости работы SQL запросов, размера таблицы и времени работы всего скрипта на одну рассылку по всем пользователям.

pax 06.09.2015 17:36

Ответ: [MySql] Оператор сравнения IN
 
Это было бы круто, но тестировать на живую сложно. Пока нотификейшен сервер не обновлен в игре и рассылать уведомления просто так не хороший тон. Делать фейковую базу для тестирования нагрузки просто нет времени.

moka 07.09.2015 03:38

Ответ: [MySql] Оператор сравнения IN
 
Ну ты замерь время выполнения реального скрипта, и кинь в логи, потом поделись данным.
Имхо как разработчик ты же сам должен знать сколько занимают твои скрипты, иначе это слепо надеятся на то что оно как-то работает нормально?

pax 07.09.2015 10:50

Ответ: [MySql] Оператор сравнения IN
 
Когда-нибудь заморочусь над этим. Одно могу сказать точно, при вчерашнем обновлении игры рассылка нотификейшенов прошла быстрее. Но там был комплекс переработок. Нагрузка на БД уменьшилась во время рассылки (чего я и хотел добиться). Вероятно из-за выборки по последнему разосланному id пользователя и выборке по 1000 вместо 100. Нагрузка на CPU увеличилась, но с этим мне еще предстоит разобраться. Полное логирование времени запросов лень делать чисто потому, что нотификейшены мы массово рассылаем раз в неделю две.

moka 07.09.2015 13:57

Ответ: [MySql] Оператор сравнения IN
 
Но предположим рассылка нотификейшенов занимает 5 минут всего.
Где 2 минуты расходуются на обработку SQL запросов. Это пару раз в неделю, вы будете на этот период сильно снижать производительность других сложных и менее сложных запросов. На 2 минуты ответы от бд будут дольше, это в сценарии если бд юзает очень много CPU и/или IO. Вот и будет неприятно на 2 минуты могут быть лаги.


Часовой пояс GMT +4, время: 21:52.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot