Задавайте вопросы, мы ответим
Вы не зашли.
Есть 2 таблицы:
CREATE TABLE IF NOT EXISTS `news` (
`id_news` int(8) unsigned NOT NULL auto_increment,
`text` text NOT NULL default '',
`view` int(4) unsigned NOT NULL default '0',
`status` enum('checked','deleted','unchecked') NOT NULL default 'checked',
`last_view` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id_news`),
KEY `status` (`status`)
) ENGINE=MyISAM;
CREATE TABLE IF NOT EXISTS `counters` (
`id_news` int(8) unsigned NOT NULL,
`last_view` datetime NOT NULL default '0000-00-00 00:00:00',
KEY `id_news` (`id_news`)
) ENGINE=MEMORY;
В первой таблице хранится порядка 500 000 новостей, во второй – количество просмотров и время последнего просмотра новости за последние 5 минут.
Раз в 5 минут по крону запускается скрипт:
$result = mysql_query ("SELECT id_news, MAX(last_view), COUNT(id_news) FROM counters GROUP BY id_news");
while (list ($id_image, $last_view, $views) = mysql_fetch_row($result))
{
mysql_query ("UPDATE news SET view=view+".$views.", last_view='".$last_view."' WHERE id_news='".$id_image."' and (status='checked' OR status='unchecked') limit 1");
}
$result = mysql_query ("DELETE FROM counters");
$result = mysql_query("UNLOCK TABLES");
За 5 минут в таблице counters накапливается порядка 3000-5000 записей и на обновление счетчиков в таблице news уходит около 5 секунд. Пока происходит обновление счетчиков, сайт подвисает на те же 5 секунд.
Можно ли как-то еще оптимизировать процесс обновления счетчиков, кроме как еще чаще запускать скрипт обновление по крону?
Пробовал другой запрос:
UPDATE news AS t1
LEFT JOIN (SELECT id_news, MAX(last_view), COUNT(id_news) AS counter FROM counters GROUP BY id_news) AS t2 ON t1.id_news=t2.id_news AND (t1.status='checked' OR t1.status='unchecked') SET t1.view=t1.view+t2.counter
Так он вообще выполняется больше 30 секунд. Почему так?
Заранее спасибо!
Отредактированно grey109 (17.12.2010 22:33:03)
Неактивен
Вы еще LOCK TABLES в начале не написали, он тоже важный, из-за него
блокируются таблицы
Предлагаю вот такой сценарий (никаких внешних блокировок):
1. Сделать временную табличку с id из counters (все, что есть).
2. Курсором для каждой строки вытаскивать из counters все нужные чиселки.
3. Обновлять news для этой строки (view = view + @counter).
4. Обновлять counters для этой строки (delete from counters limit @counter,
нужно будет сделать подготовленное выражение).
5. Удалить временную табличку.
Неактивен
Да, LOCK TABLES конечно присутствует, это я его упустил пока писал пост.
При таком сценарии скрипт будет работать и грузить процессор гораздо больше 5 нынешних секунд.
Пусть сейчас в таблице counter 5000 записей, после «GROUP BY id_news» цикл сделает 1000-1500 UPDATE’ов. А если сделать как вы предлагаете, то цикл придется прокрутить все 5000. Конечно в это время сервер будет работать, но будет тормозить так же как и в варианте из первого поста, только тормоза будут не 5 секунд, а в 2-3 раза дольше (т.к. нет блокировки и количество обращения к базе возрастет в разы).
Так что наверно и не выход. Или я вас не правильно понял?
Неактивен
Честно говоря, не понял, откуда такие числа. У Вас есть 1000 id, которые попадут
в counters. Для каждого из этих id Вы выполняете операции. Их как было 1000, так
и осталось. Просто даете между изменениями данных другим процессам тоже пора-
ботать.
Неактивен
Пробовал другой запрос:
UPDATE news AS t1
LEFT JOIN (SELECT id_news, MAX(last_view), COUNT(id_news) AS counter FROM counters GROUP BY id_news) AS t2 ON t1.id_news=t2.id_news AND (t1.status='checked' OR t1.status='unchecked') SET t1.view=t1.view+t2.counter
А попробуйте на всякий случай просто JOIN вместо LEFT JOIN - Вам ведь незачем всю таблицу большую трогать, нужны только те записи, что соответствуют id в подзапросе.
Неактивен
Сейчас объясню по числам. Есть таблица:
CREATE TABLE IF NOT EXISTS `counters` (
`id` int(10) unsigned NOT NULL auto_increment,
`id_news` int(8) unsigned NOT NULL,
`last_view` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY `id_news` (`id_news`)
) ENGINE=MEMORY;
При просмотре новости в нее вставляется строка – "id", "id новости" и "время просмотра":
Неактивен
Хорошо. Делаем временную табличку
CREATE TEMPORARY TABLE ids (id INT);
INSERT INTO ids SELECT DISTINCT id_news FROM counters;
Вопрос: сколько строк будет в этой табличке? Я предлагаю бегать именно по ней.
Неактивен
Просто JOIN помог.
Сейчас остановился на таком алгоритме:
- в отдельную таблицу t1 делаю INSERT id новости и времени последнего просмотра
- раз в n минут запускаю по крону скрипт, который создает временную таблицу t1_tmp и копирует туда данные из t1
- потом выполняю запрос написанный выше, только с JOIN
- удаляю из таблицы t1 новости c id, которые присутствуют в t1_tmp
- удаляю временную таблицу t1_tmp
> Вопрос: сколько строк будет в этой табличке?
Где-то 1000-2000.
Неактивен