Как стать автором
Обновить

Комментарии 40

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

Собственно это и есть моё единственное решение.
Почему-то мне кажется что надо будет еще проще — сделать select count(*) where rank>«текущий ранг пользователя». Думаю тут даже может быть использован поиск только по индексу, хотя не уверен.
Да, по-моему это оптимальный вариант, а поиск только по индексу — имхо будет использован, почему нет? Если есть индекс на поле rank — какие еще данные нужны субд чтобы посчитать кол-во записей?
+1
rank в отдельное поле, которое обновлять по нужной формуле через определенный промежуток времени, на rank — индекс, и select count(*)… where rank > user_rank. Это и будет позицией юзера в рейтинге
Начал проверять и наткнулся на проблему юзеров с нулевым рейтингом.
Рассмотрим пример, есть юзеры с рейтингом (1)10 (2)7 (3)5 (4)3 (5)1 (6)0 (7)0 (8)0 (9)0
При выборке where rank > 0 мы получим в ответ — 5 для всех трёх последних юзеров. То же самое выйдет со всеми повторяющимися значениями.
Это, мне кажется, вопрос религии.

Если у них одинаковый рейтинг, то почему бы им не иметь одинакового места в таблице?
имя — количество баллов
а — 1
б — 2
в — 3
г — 5
д — 3
е — 2
ж — 3

на каком месте находится пользователь Б? на втором?
а на каком тогда В? на четвертом?
а кто тогда на третьем?
а — 5
б — 3
в — 2
г — 1
д — 2
е — 3
ж — 2
вернее вот такой список )
Пусть каждый думает что первый из 5 — он :)
И как это должно выглядеть в общей таблице?
Выглядеть-то оно будет нормально, но если их, одинаковых, больше одной страницы, то ссылаться корректно уже не получится.
См. UPD 2
Ну тогда например такое развитие предложенного выше варианта:
SELECT rating FROM table WHERE user_id =?

— получаем рейтинг пользователя
SELECT COUNT(*) FROM table WHERE rating > current_user_rating

— получаем кол-во пользователей у которых рейтинг больше
SELECT COUNT(*) FROM table WHERE rating = current_user_rating AND user_id > current_user_id

— получаем кол-во злодеев, который обходят текущего пользователя по позиции в рейтинге, хотя кол-во рейтинга у них тоже, для актуальной позиции складываем результаты 2 и 3 запроса.
Ну и соот-но в общем списке пользователей с одинаковым рейтингом нужно будет сортировать именно по тому полю, которое используется для определения позиции в третьем запросе (в моем случае user_id)
Хорошая идея, спасибо.
А бд по умолчанию, одинаковых злодеев будет по id сортировать?
Если сделать выборку SELECT * FROM `table` WHERE 1 ORDER BY `rating`
Да, изначально сортировка будет по первичному ключу, раз у вас в таблице есть id — то это наверное он и есть :)
В общем случае это не так, не учите плохому.
Да, действительно не так, я ошибся, хотя проверял перед тем как написать, но похоже мой тогдашний результат оказался совпадением, при сортировке по другому полю действительно никакой сортировки по первичному ключу нет.
Тогда похоже для предложенного мной понадобится выборка
SELECT * FROM `table` ORDER BY `rating`, `id`
А это уже мы начали тратить ресурсы. Ибо двойная сортировка — не есть хорошо.
Да это понятно… в общем я разобрался почему разнятся мои вчерашние и сегодняшние результаты, когда на поле rating висит индекс — результат отдается в соот-ии с первичным ключем, без индекса — в разнобой, но без индекса тут вообще никуда :) в общем тут конечно нужно углубленнее разбираться, хранятся-ли индексы с изначальной сортировкой по первичному ключу или нет.
Насколько я смог понять всю эту красоту: en.wikipedia.org/wiki/B-tree данные в такого рода деревьях (которые собственно и используются в MySQL-индексах по числовым полям) хранятся сортированными вдоль и поперек, если вкратце:
B-tree is a tree data structure that keeps data sorted
Ага, понято. То есть первичный ключ влияет на построение индекса по рейтингу. И если индекс есть, то он вторично упорядочен по первичному ключу, а если нет, то…
А при отдаче без индеска такая же структура случаем не формируется?
Как я смог понять принцип работы всей этой гадости — нет.
Механизм сортировки набирает значения для сортировки в буфер, сортирует и сохраняет на диск, затем когда все данные сохранены — склеивает различные буферы между собой и получает общий результат.
В случае с прямым чтением из таблицы — он будет читать значения по порядку, добавлять в буфер и сортировать, сортировать есс-но только по рейтингу, и результат в итоге выйдет отсортированным только по рейтингу.
В случае-же с индексом — механизм сортировки будет читать значения для сортировки из индекса, где они уже упорядочены, первично по рейтингу, вторично по первичному ключу, и соот-но изменять порядок их следования ему не потребуется, и данные в буфере останутся в таком-же порядке что и были считаны. Ну и соот-но если сортировка производится в обратном порядке (ORDER BY rating DESC) — данные из индекса будут считаны в обратном порядке, и вторичная сортировка по первичному ключу также будет в обратном порядке.
Да, но ведь последовательное считывание данных из таблицы и даёт ордер по первичному ключу, разве нет? Ведь данные туда были записаны именно в такой последовательности?
Вот с обратным ордером уже другое дело, но с прямым-то должно совпадать.
Или не через промежуток времени, а непосредственно в момент изменения какой-нибудь характеристики юзера, влияющей на рейтинг
Да, так и подразумевалось.
Всё-равно на той же странице придётся селект юзера для проверки делать. Так что мы теряем в производительности всего один апдейт. Зато взмен получаем full time актуальность.
НЛО прилетело и опубликовало эту надпись здесь
Может быть, я не спорю. Но решение является не оптимальным по своей сути. Оно тратит ресурсы на избыточность данных, которая в большенстве случаев и не понадобиться.
Я к тому, что зачем обновлять рейтинг всех пользователей каждые 5 минут, когда за 5 минут этим рейтингом даже половина никогда не воспользуется?
НЛО прилетело и опубликовало эту надпись здесь
Я не спорю о производительности, да и портала ещё нет, только разрабатываю.
Дело в том, что в тексте я подразумивал вычисление и сохранение позицый, что означает апдейт всех полей. Вы собственно только подтвердили, что это лишние ресурсозатраты.
На самом деле я считаю, что решение Eyes — оптимально.
НЛО прилетело и опубликовало эту надпись здесь
То что MySQL настолько крут — еще не означает что нужно забив на все болт работать с ним как придется, выборку за 0.1 секунду нельзя назвать «призадумался», однако если есть вариант сделать аналогичную за 0.001 секунду — то следует уж наверное предпочесть ее. Столько людей бъется на тем чтобы оптимизировать работу с БД в высоконагруженных проектах — а тут оказываецца нафиг оно все, MySQL сам все разрулит, жаль только серверные скрипты он сам не пишет.
НЛО прилетело и опубликовало эту надпись здесь
Ну так вот мы и пришли к теме топика :) человек попросил подсказать как ему лучше сделать запрос к БД не через жёпу и правой ногой :) ему подсказали :)
А насчет выбрать из базы 10 тысяч строк — это имхо как раз таки запрос над которым в теории неплохо-бы задуматься, потому как кол-во пользователей будет увеличиваться, кол-во их рейтингов соот-но тоже, и кол-во запросов на получение собственного рейтинга тоже, нагрузка будет расти с двух сторон, дойдет-ли проект до кол-ва пользователей больше 10к это конечно тот еще вопрос, но зачем оставлять на самотек потенциальную проблему, которую изначально можно красиво и внятно решить.

А насчет капч — я честно говоря не понял какое они имеют отношение к изначальной теме разговора, но в чем-то я конечно согласен, обычные капчи сейчас приносят мало пользы, по мне так еще имеют право на жизнь капчи с осмысленным содержимым, где вместо «dtrge» написано «сложи 2 и 2», в конце концов если посетитель не умеет читать/считать — то пользы от него, как от потенциального клиента — все равно нет :)
Тут еще есть одна подзадача:
как тогда будет выглядеть вариант вывода списка участников, например читающих один блог, или с одним увлечением?

Пользователи:
ID Rating
1 — 123
2 — 512
3 — 124
5 — 123
6 — 444
7 — 983
8 — 321
9 — 0
10 — 2

####################
Этот блог читают:
####################
User 1
User 3
User 6
User 7

Вариант select count(*) where rank>«текущий ранг пользователя» уже не подойдет.
Самый простой вариант — это кешируемая на определенное время картинка как тематический индекс цитирования Яндекса
Чегот не совсем понял про ТИЦ, можно по подробнее?
Это уход от прямых запросов при генерации страницы.
Просто в то место, где должно быть выведено место конкретного участника, вставляется картинка

####################
Этот блог читают:
####################
User 1 [img src="/rating_1.jpg"]
User 3 [img src="/rating_3.jpg"]
User 6 [img src="/rating_6.jpg"]
User 7 [img src="/rating_7.jpg"]

сама же картинка генерируется скриптом при первом обращении (по принципу select count(*) where rank>«текущий ранг пользователя» или любому другому), в скрипте устанавливается нужный Expire, и когда она будет неактуальна — перегенерируется заново. Все под контролем.

По-моему вариант.
Да нет, проблема то не в том, как закешировать позицию и не считать её по сто раз. Проблема в том, как посчитать реальную позицию пользователя в рейтинге. В целом я её описал в UPD 2
А имеет ли вообще смысл выводить везде где только можно положение пользователя в рейтинге в сравнении с другими пользователями? Если на то пошло, то гораздо более показательнее будет выводить не место, а само значение ранга.
А то разница между 3 пользователями:
User1 — 1 место
User3 — 10 место
User22 — 100 место
Будет менее очевидна чем:
User1 — 97 баллов
User3 — 88 баллов
User22 — 55 баллов

Хотя конечно зависит от специфики приложения.
Да нет, выводить-то будем и то и другое. ПРосто очень хочется иметь возможность перекидывать пользователя ихз профиля в топ лист. И не просто в топ лист, а именно туда, где он там находится.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории