MySQL: оптимизация запроса на обновление счетчика

Опубликовал mvs3d | Дата 31.03.2008 – 19:46 |

При сохранении различной статистики, довольно часто встречается задача обновить существующую запись а если она не существует – создать.

Например, существует таблица “test” с полями “day” (дата, без времени) и “counter” (число – счетчик). Есть уникальный индекс по полю “day”. Надо его увеличивать при наступлении какого-то внешнего события (у меня это downloads).

Довольно часто я применял такой метод – делаем SELECT (ищем запись в базе), если ее нет – INSERT, иначе – UPDATE. В последнее время стал использовать функцию PHP mysql_affected_rows(), а вчера увидел еще одно решение – всегда делается вставка, и если произошла ошибка дублирования ключа – выполняем UPDATE.

Сначала я подумал что это решение ужасно, но потом сильно задумался.

Решил опросить коллег, вызвался помочь 1 человек, порекомендовал mysql_affected_rows(), но без всяких пояснений. Тогда я закинул вопрос в сервис Google “Вопросы и ответы”, там мне рассказали что в MySQL существует встроенный оператор для осуществления того что мне надо -
“INSERT INTO … ON DUPLICATE KEY UPDATE”.

Как оказалось он наиболее эффективен из всех решений. Продублирую сюда свои выкладки:

Я написал тестовый скриптик с обновлением счетчика 100тыс раз а таблицу заполнил случайными данными (2200записей).

Тестировалсь 4 метода:

1. Встроенный синтаксис MySQL (INSERT INTO test(day, counter) VALUES (…) ON DUPLICATE KEY UPDATE…
2. Внутри PHP пытаемся делать update, если mysql_affected_rows()<>1 то вставляем новую запись.
3. Делаем SELECT (ищем есть ли в таблице запись на нужный нам Day), если запись найдена – update, иначе – insert (именно этот способ я чаще всего использвал в своих приложениях, поэтому интересно было сравнить).
4. Делаем INSERT, если вернулась ошибка mysql DUPLICATE KEY выполняем update.

Скриптик написал с использованием нативных функций PHP mysql_.. Выполнил его 3 раза, на компе остановил все тяжелые задачи, даже проигрывание музыки :)
Версия PHP 4.3.11, mysql – 4.1.11-nt. Конектился к localhost.

Результаты такие:

первый запуск:

Start of testing method1..finished, executed time: 10.931 (100000 iterations)
Start of testing method2..finished, executed time: 11.207 (100000 iterations)
Start of testing method3..finished, executed time: 27.775 (100000 iterations)
Start of testing method4..finished, executed time: 20.738 (100000 iterations)

второй запуск:

Start of testing method1..finished, executed time: 10.354 (100000 iterations)
Start of testing method2..finished, executed time: 11.048 (100000 iterations)
Start of testing method3..finished, executed time: 27.807 (100000 iterations)
Start of testing method4..finished, executed time: 20.449 (100000 iterations)

третий запуск:

Start of testing method1..finished, executed time: 10.571 (100000 iterations)
Start of testing method2..finished, executed time: 11.101 (100000 iterations)
Start of testing method3..finished, executed time: 28.179 (100000 iterations)
Start of testing method4..finished, executed time: 20.634 (100000 iterations)

(Время дано в секундах)

Вывод:

Для подобной операции наиболее эффективна встроенная команда, хотя метод с mysql_affected_rows проигрывает совсем немного. Наиболее часто используемый мной способ почти в 3 раза медленней, а тот код, с которого я начал разбирательство – в 2 раза.

PS Кстати, использование сервиса от Google “Вопросы и ответы” было экспериментальным, – хотел проверить насколько он эфективен в работе. И мне понравилось. Ответ я получил буквально в течении часа. Видимо сервис очень популярен (на данный момент у меня 95 просмотров моего вопроса). Так что всем рекомендую его использовать, и не только для вопросов но и ответов ;)

  1. 2 коммент. к “MySQL: оптимизация запроса на обновление счетчика”

  2. Igor Karablin - Апр 11, 2008 | Ответить

    Кстати Вов, я 2й вариант начал использовать еще когда мы в автомире сидели :)
    А 3й вариант далеко не на всех версиях mysql существует, так что будь бдителен.

  3. mvs3d - Апр 11, 2008 | Ответить

    Игорек ты что-то с номерами попутал.. ибо 3-й вариант существует в любой базе данных :) А какой тогда ты стал в Автомире еще использовать? Точно 2-й :) ?

Оставить комментарий или два

Об авторе

Меня зовут Владимир. Я живу в России, в г.Тольятти Самарской области. C 2004 года активно занимаюсь Web-разработками. Интересуюсь развитием сервисов Сети, технологиями создания и продвижения Интернет-ресурсов, компьютерными железками.. и не только ;)

Подпишись на обновления!

 RSS-канал / Email-рассылка
Поиск :