почему тех и почему за ним, скорее того и перед выбрал нашего юзверя и того кто перед ним, по рейтингу(1й запрос); поменял местами, если рейтинг у нашего вдруг стал больше(2й запрос)
Я считаю что гадать на кофейной гуще в данном случае совершенно бессмысленный вариант, поэтому нужно просто "тупо" погонять эти варианты и решить что лучше. Дабы не быть совершенно бессмысленным капитаном очевидностью, я взялся за это дело. Итак. Тестировалось на таблице со структорой Psih на 1 000 000(один миллион) записей с небольшими изменениями: 1) smallint, как-то не рулит(у нас же лям записей, для каждой записи свое место), пришлось заменить на INT. 2) Добавил индекс по месту. Методом "от балды" я пришел к выводу, что нужно измерять: 1) Время на пересчет мест(если оно есть). 2) Время на запрос положения одного пользователя в таблице рейтинга. Вариант Psih'a: Пересчет мест: [sql] SET @place := 0; UPDATE `competition` SET `cmp_place` = @place := @place + 1 ORDER BY cmp_rating DESC; [/sql] Пересчет занял 19 минут. Запрос положения в рейтинге: [sql] SELECT `cmp_place` FROM `competition` WHERE `cmp_usr_id`=343223; [/sql] 0,0007-0,0005 s Результаты EXPLAIN: [sql] select_type: simple table: competition type: const possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: const rows: 1 [/sql] что в принципе логично. Вариант Mr.M.I.T.: Затраты на пересчет мест: 0 Запрос положения рейтинга: Тут есть пара моментов спорных: 1) Мы уже должны знать общий рейтинг пользователя. 2) При наличии пользователей с одинаковым рейтингом, результат срабатывания неправилен, т.е. всем пользователям с рейтингом ноль в моем случае выдавалось милионное место, всем пользователям с рейтингом 99997, 31-е место и т.д. и т.п. Короче не работает. Чисто из академического интереса посчитаем: [sql] SELECT COUNT(*) FROM `competition` WHERE `cmp_rating`>=56748; [/sql] Скорость выполнения, разумеется, завист от того, на какой позиции находится пользователь. Так для "ТОП 100" выполнение запроса занимало: 0,45-0,55 Для запроса выше 0,24 Для ТОП 1000 лузеров -- 0,0012-0,0006 Что не есть гуд (чаще всего ведь дергают юзверей с высоким рейтингом) Результаты EXPLAIN: [sql] select_type: simple table: competition type: range possible_keys: cmp_rating key: cmp_rating key_len: 4 ref: NULL rows: 500252 [/sql] Вариант Darevill: Как в варианте Mr.M.I.T. мы по умолчанию считали что уже "знаем" рейтинг пользователя, будем считать что тут мы изначально знаем его место Затраты на пересчет мест: [sql] -- Узнаем где он должен находится(при пользователях с одинаковым рейтингом нового запихиваем на самый верх, ну это уже как там Psih решит со своей бизнес логикой) -- 0.0009 s SELECT `cmp_place` FROM `competition` WHERE `cmp_rating`=18 ORDER BY `cmp_place` LIMIT 1; -- Сдвигаем всех (тестил для "взлета" на 10 позиций и для перемещения вверх на одну позицию: почему при любом раскладе упорно выдает 1-у секунду) -- После того как добавил индекс по плейс стало делаться за 0,2 s для 10-ти позиций и 0,06 для одной UPDATE `competition` SET `cmp_place`=`cmp_place`+1 WHERE `cmp_place` BETWEEN $up_place AND $down_place -- Перемещаем наверх "нового-лидера" 0.08s UPDATE `competition` SET `cmp_place`=$place WHERE `cmp_usr_id`=$id [/sql] Итого что-то около 0,2809-0,1409s Выборки также как у варианта Psih'a Итого: Вариант Psih не рулит совсем. Вариант Mr.M.I.T. выдает некорректные данные и среднюю производительность(для хайлоада непозволительно низкую). Вариант Darevill Оптимален на мой взгляд. Бонус трек: Последний вариант структуры таблицы: [sql] DROP TABLE IF EXISTS `test`.`competition`; CREATE TABLE `test`.`competition` ( `cmp_usr_id` int(10) unsigned NOT NULL, `cmp_rating` int(10) unsigned NOT NULL, `cmp_place` int(11) NOT NULL default '0', PRIMARY KEY (`cmp_usr_id`), KEY `cmp_rating` (`cmp_rating`), KEY `cmp_place` (`cmp_place`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; [/sql] Скрипт которым забивались данные: PHP: <?php require_once(dirname(__FILE__).'/goDB/classes/godb.php'); $db = new goDB('localhost', 'root', 'Li6tF5llonE', 'test'); $query = ''; $rowCount = 0; for($i = 1; $i < 1000001; $i++){ ++$rowCount; $query .= '('.$i.','.rand(0, 100000).',0), '; if($rowCount >= 1000){ insertRows($db, $query); $rowCount = 0; $query = ''; } } if($rowCount != 0){ insertRows($db, $query); } function insertRows($db, $query){ $db->query('INSERT INTO `competition`(`cmp_usr_id`, `cmp_rating`, `cmp_place`) VALUES '.rtrim($query, ' ,')); } ?> Тестовая конфигурация: Процессор: AMD Athlon X2 Dual-Core QL-60 1.9 GHz RAM: 2Gb (хз какая, вроде как DDR 2) OS: Windows Vista Home Basic (на убунте из-за моей криворукости умерли Apache и MySQL и не переставляются, а ось пока руки не дошли переставить). P.S. MySQL фактически никак настроен не был(все настройки по дефолту). Жду тестов других участников топика, вполне возможно что я где-то накосячил.
Черт совсем забыл выразить свое бесценное мнение, я бы сделал все как в варианте Darevill, только запихал бы это дело в процедуру, куда передавал бы только id пользователя и кол-во баллов для зачисления, но делать сейчас процедуру и мерить сколько памяти она съест откровенно влом.
akrinel накосячил. Поскольку не учел соотношения чтение/запись... Решение Mr.M.I.T. по отношению к Psih 19*60 / (0.55 -0.0007) = 2070 Т.е. если на каждую операцию записи у Psih приходится 2070 чтений, то варианты Psih,Mr.M.I.T. эквиваленты. по отношению к Darevill 0,2809 / (0.55 - 0.0007) = 0.5 Т.е. если на каждую операцию записи у Darevill приходится 0.5 чтений, то варианты Darevill,Mr.M.I.T. эквиваленты. Смущает вот это... Но сейчас проверю. P.S. скрипт с подключением твоей библиотеки наверняка замечательная вещь... Вот только библиотеки нет
какие странные результаты для COUNT(*), проверим Ps. А как должно с нулевым рейтингом быть? Pss. Ты не учёл местного кеширования в моём варианте
это типа если за юзверя не голосавали { выводим place из бд }иначе{ считаем место записываем его в бд ставим флаг о том что скешировали } поэтому в топе проблем будет много меньше
431703 за 0,2-0,17 1000000 за 0,41 Свой запрос, блин, в студию У меня получилось следующее Все ТОП 1000 [sql]SELECT * FROM `competition` ORDER BY `cmp_rating` DESC LIMIT 0, 1000;[/sql] 0,0101 -0,0086 Все ТОП 1000 лузеров [sql]SELECT * FROM `competition` ORDER BY `cmp_rating` ASC LIMIT 0, 1000;[/sql] 0,0424 -0,0086 конкретный пользователь [sql]SELECT COUNT(*) FROM `competition` WHERE `cmp_rating`>99900[/sql] 970е место 0,0012-0,0008 (таких пользователе 35 штук, второй запрос на получение пользователя 0.0014-0,0010) Итого 0,0026-0,0018 [sql]SELECT COUNT(*) FROM `competition` WHERE `cmp_rating`>900[/sql] 990883е место 0,39-0,38 (таких пользователе 35 штук, второй запрос на получение пользователя 0.0012-0,0007) Итого 0,3912-0,3807