SQLinfo.ru - Все о MySQL

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#1 17.12.2010 22:32:41

grey109
Участник
Зарегистрирован: 17.12.2010
Сообщений: 9

Оптимизация UPDATE

Есть 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)

Неактивен

 

#2 18.12.2010 18:07:45

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Оптимизация UPDATE

Вы еще LOCK TABLES в начале не написали, он тоже важный, из-за него
блокируются таблицы wink

Предлагаю вот такой сценарий (никаких внешних блокировок):
1. Сделать временную табличку с id из counters (все, что есть).
2. Курсором для каждой строки вытаскивать из counters все нужные чиселки.
3. Обновлять news для этой строки (view = view + @counter).
4. Обновлять counters для этой строки (delete from counters limit @counter,
нужно будет сделать подготовленное выражение).
5. Удалить временную табличку.

Неактивен

 

#3 18.12.2010 22:48:18

grey109
Участник
Зарегистрирован: 17.12.2010
Сообщений: 9

Re: Оптимизация UPDATE

Да, LOCK TABLES конечно присутствует, это я его упустил пока писал пост.

При таком сценарии скрипт будет работать и грузить процессор гораздо больше 5 нынешних секунд.

Пусть сейчас в таблице counter 5000 записей, после «GROUP BY id_news» цикл сделает 1000-1500 UPDATE’ов. А если сделать как вы предлагаете, то цикл придется прокрутить все 5000. Конечно в это время сервер будет работать, но будет тормозить так же как и в варианте из первого поста, только тормоза будут не 5 секунд, а в 2-3 раза дольше (т.к. нет блокировки и количество обращения к базе возрастет в разы).

Так что наверно и не выход. Или я вас не правильно понял?

Неактивен

 

#4 20.12.2010 17:52:23

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Оптимизация UPDATE

Честно говоря, не понял, откуда такие числа. У Вас есть 1000 id, которые попадут
в counters. Для каждого из этих id Вы выполняете операции. Их как было 1000, так
и осталось. Просто даете между изменениями данных другим процессам тоже пора-
ботать.

Неактивен

 

#5 20.12.2010 20:17:16

LazY
_cмельчак
MySQL Authorized Developer and DBA
Зарегистрирован: 02.04.2007
Сообщений: 849

Re: Оптимизация UPDATE

Пробовал другой запрос:

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 в подзапросе.

Неактивен

 

#6 21.12.2010 13:45:57

grey109
Участник
Зарегистрирован: 17.12.2010
Сообщений: 9

Re: Оптимизация UPDATE

Сейчас объясню по числам. Есть таблица:

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 новости" и "время просмотра":

INSERT LOW_PRIORITY into `counters` values (NULL, '.$id_news.', NOW())
 


Получается что может быть несколько строк
INSERT LOW_PRIORITY into `counters` values (NULL, 1, '2010-12-21 13:28:11');
INSERT LOW_PRIORITY into `counters` values (NULL, 2, '2010-12-21 13:28:21');
INSERT LOW_PRIORITY into `counters` values (NULL, 1, '2010-12-21 13:28:55');
INSERT LOW_PRIORITY into `counters` values (NULL, 1, '2010-12-21 13:28:57');
INSERT LOW_PRIORITY into `counters` values (NULL, 1, '2010-12-21 13:28:59');

Итого - получается 5 записей, но после группировке в запросе "SELECT id_news, MAX(last_view), COUNT(id_news) FROM counters GROUP BY id_news" остается 2, содержащие id новости, количество показов и время последнего показа. И эти 2 цикла прокручивает мой скрипт, а в вашем варианте цикл будет крутиться 5 раз и 5 раз делать дергать базу UPDATE'ами.


>Просто даете между изменениями данных другим процессам тоже поработать.
А как это сделать? sleep()?

Неактивен

 

#7 21.12.2010 16:35:31

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Оптимизация UPDATE

Хорошо. Делаем временную табличку
CREATE TEMPORARY TABLE ids (id INT);
INSERT INTO ids SELECT DISTINCT id_news FROM counters;

Вопрос: сколько строк будет в этой табличке? Я предлагаю бегать именно по ней.

Неактивен

 

#8 28.12.2010 17:04:19

grey109
Участник
Зарегистрирован: 17.12.2010
Сообщений: 9

Re: Оптимизация UPDATE

Просто JOIN помог.

Сейчас остановился на таком алгоритме:
- в отдельную таблицу t1 делаю INSERT id новости и времени последнего просмотра
- раз в n минут запускаю по крону скрипт, который создает временную таблицу t1_tmp и копирует туда данные из t1
- потом выполняю запрос написанный выше, только с JOIN
- удаляю из таблицы t1 новости c id, которые присутствуют в t1_tmp
- удаляю временную таблицу t1_tmp


> Вопрос: сколько строк будет в этой табличке?
Где-то 1000-2000.

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson