В строке go случайного поиска это называется операцией сделка (раздайте 27 разных карт из перетасованной колоды 4k или около того. Другая случайная операция называется бросок : разрешает дублирование.)
Вы используете SELECT mess-of-columns FROM mess-of-joins WHERE mess-of-criteria ORDER BY RAND() LIMIT small-number
для выполнения операций перемешивания и сдачи. Это пресловутое представление антипаттерна. Это приводит к некоторой дополнительной работе для сервера, поскольку он должен упорядочить довольно большой набор результатов, а затем отбросить почти все (с помощью LIMIT).
Чтобы избавиться от некоторых проблем, нужно отложить присоединения к подробности. Перемешать только идентификаторы. Затем возьмите небольшое количество результатов и получите необходимые данные. Примерно так.
SELECT u.id /* just the id values */
FROM mobile_login_list l
JOIN users u
ON l.username = u.username
JOIN mobile_token_list t
ON t.username = l.username
WHERE l.time > 1584393399
AND l.username NOT IN ('enesdoo')
AND u.username NOT IN (
SELECT blocked_username
FROM hided_mobile_users_from_shuffle
WHERE username = 'enesdoo'
)
AND u.ban_status = 0
AND u.perma_ban = 0
AND u.mobile_online_status = 1
AND u.lock_status = 0
ORDER
BY RAND( )
LIMIT 27
Вы можете отлаживать, запускать EXPLAIN и оптимизировать этот подзапрос, изменяя индексы и, возможно, ужесточая критерии выбора. Это тот, кто делает всю тяжелую работу, тасуя и действуя.
Затем присоедините этот набор результатов к таблицам подробностей, чтобы выбрать нужные данные. Этот внешний запрос должен обработать только 27 строк. Обязательно снова перемешайте.
SELECT u.id
, u.is_instagram_connected
, u.tokens
, u.username
, u.name
, u.photo
, u.bio
, u.voice
, u.mobile_update
, 1584450999 - l.time idleTime
FROM mobile_login_list l
JOIN users u
ON l.username = u.username
JOIN (
/* the subquery from above */
) selected ON u.id = selected.id
ORDER BY RAND()
Собрав все воедино, вы получите этот большой повторяющийся беспорядок запроса. Но это должно быть немного быстрее.
SELECT u.id
, u.is_instagram_connected
, u.tokens
, u.username
, u.name
, u.photo
, u.bio
, u.voice
, u.mobile_update
, 1584450999 - l.time idleTime
FROM mobile_login_list l
JOIN users u
ON l.username = u.username
JOIN (
SELECT u.id
FROM mobile_login_list l
JOIN users u
ON l.username = u.username
JOIN mobile_token_list t
ON t.username = l.username
WHERE l.time > 1584393399
AND l.username NOT IN ('enesdoo')
AND u.username NOT IN (
SELECT blocked_username
FROM hided_mobile_users_from_shuffle
WHERE username = 'enesdoo'
)
AND u.ban_status = 0
AND u.perma_ban = 0
AND u.mobile_online_status = 1
AND u.lock_status = 0
ORDER
BY RAND( )
LIMIT 27
) selected ON u.id = selected.id
ORDER BY RAND()
Более эффективный способ работы с записями заключается в следующем, если вы много разыгрываете.
Добавьте FLOAT столбец таблицы, с которой вы имеете дело, назовем это deal
. Поместите в него индекс.
Каждые несколько часов, или, возможно, в одночасье или даже раз в неделю, перетасуйте таблицу, выполнив этот запрос UPDATE users SET deal = RAND();
Это займет некоторое время; ему нужно изменить значение deal
в каждой строке.
Когда вам нужно совершить сделку, выполните ...WHERE deal >= RAND() * 0.9 ... ORDER BY deal LIMIT n
. Умножение на 0,9 помогает гарантировать, что вы не дойдете до конца таблицы, выбрав случайное число, слишком близкое к 1.
Это эквивалентно, с точки зрения карточного акула, перетасовыванию колоды каждые несколько часов, а затем просто сокращая за каждую сделку. Именно так в Википедии реализована функция «показать случайную статью».