MySQL

индекс
230,83

Медленный UPDATE vBulletin — в чем подвох?


Купил лицензию на vBulletin на два форума. На одном — летает круглые сутки (около 20000 зарегистрированных пользователей), на другом — начинает тормозить к вечеру (120000 пользователей).
Посещаемость одинаковая — 50-100 человек онлайн. Запустил на тормозящем форуме Debug Mode, оказалось что самый последний запрос занимает вплоть до 16 секунд!
UPDATE user
SET lastactivity = 1226505039
WHERE userid = 1


* This source code was highlighted with Source Code Highlighter.

Причем, раз на раз не приходится — запрос может исполняться пару раз нормально (тысячные доли секунды), а может и 16 секунд.
Думаю, что пенять на размер БД и количество юзеров было бы неверным, хотя мысль удалить не посещающих форум была.
+6
12 ноября 2008, 19:13
6

комментарии (40)

0
sanchesfree #
очевидно проблема не в этом запросе ;) просто когда он выполняется параллельно выполняется что-то более ресурсоемкое. почти готов поспорить :)
0
CuamckuyKot #
Раз 100 запускал /?explain=1 с рассмотрением запросов. Все чисто. Когда этот запрос не буксует, все грузится в за 0.1-0.2 секунды.
А когда буксует, то это же время + время пробуксовки.
0
sanchesfree #
Ну тогда предлагаю вам юзать UPDATE DELAYED, больше вариантов простых не вижу =)
0
pietrovich #
можно понимать что Repair и Optimize были испробованы и не помогли, так?
0
shx #
Разница вот она 120000 против 20000, и неважно сколько пользователей онлайн, просто таблица больше. Все упирается в БД а не в движок. Можно поставить модификатор отложеного запроса LOW_PRIORITY, если это MySQL, но может поможет…
0
CuamckuyKot #
Ставил, не помогает.
0
shx #
такое бывает… может стоит поменять тип таблицы например на InnoDB они не блокируют всю таблицу при вставке. И еще параметры сервера стоит подкрутить, чтобы больше памяти было у БД.
0
CuamckuyKot #
Памяти достаточно — по шаблону my-huge.cnf.
Перевел в InnoDB, вроде бы все шустро, спасибо за советы всем.
0
Nc_Soft #
Странно, но в на хостинге от джино есть статистика медленных запросов, и аналогичный запрос почему-то тоже там считается медленным
UPDATE reg SET last_time=NOW() WHERE id='2948';
+1
metaball #
Такое ощущение, что на сервере крутятся ещё сайты и mysql не держит нагрузку. 120000 записей — это уж точно не предел, хотя еще не известно, что в других таблицах
+1
elfiki #
селект и апдейт для одной таблицы не может выполняться одновременно, ибо это может привести к колапсы и неверной выборке, поэтому они ставятся в очередь.
А вообще можно поизучать processlist и посмотреть почему так долго идет запрос и что делается перед ним.
0
Kaluchi #
Может быть у вас индексы как-нибудь криво перестраиваются?
0
Nc_Soft #
как криво-то? я так понимаю там userid это PRIMARY KEY и всё.
вообще, elfiki хорошую идею подал, надо попробовать вынести время последнего пребывания в другую таблу.
–1
keatlon #
В форуме более важную роль играет кол-во сообщений, форумов, дополнительные моды. Приведенный запрос абсолютно не причем, если по user_id есть ключ и 20, 120 тысяч тут не причем.

В качестве быстрого решения можно включить memcached, а вообще на форумах vbulletin.com, vbulletin.org есть темы с форумами-миллионниками. Там есть много ответов и решений
0
CuamckuyKot #
Memcached включен, но он не касается вопросов записи в базу. Ключ есть по userid.
В том-то и прикол, что неясно откуда баг.
0
norguhtar #
Какой движок у MySQL для базы используется? Не MyISAM случаем?
0
CuamckuyKot #
Он самый.
+4
norguhtar #
Поздравляю вы попали. Вы в курсе что при каждой операции записи лочится вся таблица? Просто потому что MyISAM не знает что такое транзакции. Если проблема именно в этом запросе то достаточно будет конвертнуть в InnoDB таблицу user и станет хорошо.

PS Вообще общий совет. Если у вас нет полнотекстового поиска в базе и много одновременных операций записи перейти на InnoDB. Правда предварительно подтюнив. Что тюнить и как можно посмотреть на MySQL Performance Blog. Ну и если все же MySQL не справляется рекомендуется мигрировать на PostgreSQL. Он лучше держит нагрузки и проще в обслуживании.
0
Drimean #
VBulletin, на сколько я помню, не знает, что такое транзакции.
0
norguhtar #
А причем тут VBulletin? Главное чтобы СУБД знало. В случае если транзакциями не управляют явно, ими управляет СУБД и каждый запрос обрабатывается в пределах одной транзакции. Это так называемый autocommit.
0
Angerslave #
Я бы скорее сказал, дело не в транзакционности, а в том, что MYISAM при записи лочит всю таблицу, а InnoDB — только ту строку, с которой работает.
0
norguhtar #
Вообще-то InnoDB может лочить одну строку именно из-за механизма транзакций. В MyISAM он отсутствует по этому требуется блокировать всю таблицу если требутеся избежать проблем с целостностью данных.
0
pietrovich #
а что статистика по поводу Locks говорит?
0
norguhtar #
Ну а смысл от нее? Есть более простой и быстрый способ избавится от проблемы.
0
pietrovich #
смысл увидеть… если во время проблемы будет куча Table locks waited то тогда да, а если не будет? ;)
0
CuamckuyKot #
Перевел в InnoDB — та же беда :-(
Page generated in 14.14102 seconds with 12 queries.
0
norguhtar #
вы там ACID отключили? А то с ACID оно медленно работает. Ну и еще вопрос. Сейчас стабильно время на той query такое или нет?
0
CuamckuyKot #
Где он отключается?
+1
norguhtar #
в my.cnf
innodb_flush_log_at_trx_commit = 2
0
CuamckuyKot #
Спасибо.
0
shx #
Хм… У инноДБ отдельные параметры настройки…
–1
odessky #
Пиши в memcache, %username%
При низкой нагрузки — массовый update.

А вообще, пиши мне в личку — я могу пошаманить над innodb, часто помогает
0
CuamckuyKot #
Пишу с самого начала.
0
coldFlame #
А нефиг писать в таблицу, откуда постоянно ведутся выборки.
Такие «статистические» поля, которые часто обновляются, надо выносить в отдельную таблицу.
+1
CuamckuyKot #
Напишите разработчикам vBulletin.
0
coldFlame #
да отож.
+1
chernomyrdin #
Решал схожую проблему, то есть у меня была конструкция вида:

UPDATE posts SET open = open + 1 WHERE id = 1;

При большом количестве запросов к базе данных (то есть когда qps подскакивал до 500 и выше) имел большие тормоза, это было связано с тем что базы MyISAM (соответственно — table lock) и отказаться от них не получаться (используется Full Text Search).

Потом пробывал разрулить ситуацию с HIGH_PRIORITY и LOW_PRIORITY, то есть UPDATE LOW_PRIORITY и SELECT HIGH_PRIORITY, но все равно сталкивался с проблемами медленно выполняемых запросов

Решил проблему с помощью следующей конструкции:
Вместо UPDATE делаем INSERT в другую таблицу:
INSERT post_cnt (id,dtime) VALUES (1,now())

А при SELECT-е делаем:
SELECT *, (SELECT COUNT(*) AS cnt FROM post_cnt c WHERE c.id = p.id) AS open_add
FROM posts p WHERE id=%id%

Единственное _но_ в бизнес логике страниц пришлось делать что-то типа:
$post = GetPostID( $id );
// После проверки, что с $post все нормально делаем:
$post['open'] += $post['open_add'];

И регулярно (раз в несколько часов) делаю:
set @n=subtime(now(),'01:00:00');
UPDATE posts p
SET p.open=p.open+(SELECT COUNT(*) AS cnt FROM posts_cnt c WHERE c.id=p.id AND dtime<=@n )
WHERE p.id IN (select pc.id from post_cnt pc WHERE dtime<=@n);
DELETE FROM post_cnt;

В Вашем случае можно написать
INSERT INTO user_la( lastactivity, id, dtime) VALUES (1226505039,1,now());

Вместо SELECT-а:
SELECT *, (SELECT MAX(lastactivity) AS la FROM user_la l WHERE l.id = u.id) AS user_lastactivity
FROM user u WHERE id=%id%

в бизнес-логике
$user['lastactivity']=$user['user_lastactivity'];

Ну и переодически можно делать:
set @n=subtime(now(),'01:00:00');
UPDATE user u
SET u.lastactivity=(SELECT MAX(lastactivity) AS ula FROM user_la l WHERE l.id=u.id AND dtime<=@n )
WHERE u.id IN (select la.id from user_la la WHERE dtime<=@n);
DELETE FROM post_cnt;

Хотя конечно с memcache получается в чем-то красивее
0
ekim #
DELETE FROM post_cnt;
Лучше использовать TRUNCATE — он гораздо быстрее чем DELETE. TRUNCATE удаляет и воссоздает таблицу, что намного быстрее, чем поочередное удаление строк (при DELETE).
TRUNCATE post_cnt;
0
DavidSky #
С опозданием… добавь LIMIT 1, но если и это не поможет попробуй отменить использование индекса т.е. делать полное сканирование таблицы.
0
pwlnw #
Думаю вы неправильно диагностировали медленный запрос. В таких случаях нужно смотреть не общее время, а lock time. Табличка эта очень мелкая, скорее всего у вас тормозит другой запрос с join с этой табличкой, поэтому накладывается блокировка чтения на user.

В похожей ситуации я тоже перевел user на inndb и LOW_PRORITY.
Не сказать что сильно помогло.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.