Способы подсчета статистики посещения страниц сайта «на лету»

в 8:51, , рубрики: mail.ru, Блог компании Mail.Ru Group, Статистика в IT, счетчики, метки: ,

Часто возникает задача подсчета количества посещений той или иной страницы портала или подсчета кликов по какой-то ссылке. Например. может быть интересно подсчитать количество просмотров статьи или объявления, число кликов по «показать номер телефона» на странице объявления о продаже и т.д. и т.п.

Главное точно понимать, для чего вы планируете использовать эти счетчики и для чего они могут понадобиться в обозримом будущем. Нужно ли по ним выбирать/сортировать/группировать сущности или вам просто нужны редкие разовые отчеты (например раз в сутки)? А может, вы хотите показывать эти счетчики в админке или в личном кабинете пользователя? Хотите ли вы в реальном времени показывать эти числа или можно ограничиться часовой/суточной отсечкой? В зависимости от этих и, возможно, еще каких-то критериев будет зависеть выбор способа подсчета и хранения счетчиков.

Подсчет кликов/визитов

Задачу подсчета кликов/визитов можно решать разными способами:

  • при посещении страницы или при обращении к какому-то URL делать +1 к счетчику;
  • периодически просматривать логи Web-сервера и считать число обращений к нужным URL-ам;
  • делать через redirect со специального URL-а, и перед редиректом делать +1 (но это, как вы понимаете, работает не во всех случаях);
  • с помощью JavaScript обращаться к URL счетчика при каком-то событии;
  • другие способы, на которые хватит фантазии.

На данном этапе необходимо решить, нужно ли нам считать визиты поисковых роботов. Возможно, вы не захотите считать запросы, совершаемые из внутренней сети. Также может быть интересным считать только запросы авторизованных пользователей и т.д.

Также важно понять, насколько критична точность подсчета, можно ли ошибиться на 1-2-3 единицы или нет. От этого может зависеть многое. Если важна точность, то можно пренебречь производительностью, или же наоборот, если важна производительность (например, скорость отдачи страницы), то можно незначительно пренебречь точностью.

Хранение счетчиков

Хранить счетчики можно тоже по-разному:

  • в базе данных в виде числового поля прямо в таблице сущности/страницы;
  • в базе данных в отдельной таблице, созданной специально для счетчиков, но также в виде простого числового поля;
  • сгруппировать разные счетчики, засериализовать их и также хранить в базе в BLOB или varchar-поле;
  • в какой-либо NoSQL базе, например, как Id -> count;
  • комбинация memcache + СУБД;
  • в файлах. Тут уж кто во что горазд, можно в одном файле в сериализованном виде, можно по файлу на сущность. В общем, можно много чего придумать, вплоть до реализации своей простой СУБД для хранения счетчиков.

Пример из жизни

Давайте рассмотрим конкретный пример из нашей практики. Сразу скажу, что мы используем MySQL.

Задача: подсчет числа визитов страницы объявления, без учета Web-ботов. Также нужно считать общее количество просмотров всех объявлений у автосалона.

Важные моменты:

  • Точность подсчета важна, но в какой-то очень редкой форс-мажорной ситуации допустима потеря суммарно не более 200 визитов (это визиты всего за несколько минут в утреннее/дневное/вечернее время).
  • Нужна сортировка сущностей по числу визитов.
  • Обновление счетчика в базе в утренние/дневные/вечерние часы не реже одного раза в 30 минут. Это нужно для актуальности сортировок и адекватного отображения числа визитов в личном кабинете.

Сначала попробовали реализовать «в лоб». Мы создали в таблице сущности поле views_count (аналогично в таблице автосалона) и обновляли его при каждом просмотре страницы объявления (делали UPDATE <table> SET views_count = views_count +1 WHERE id = <id>). При тестировании сразу обнаружилась очевидная проблема — очередь из запросов на обновление и, как следствие, увеличение времени на получение данных из этой таблицы при отображении листингов объявлений.

Подумали-подумали, и решили, что количества просмотров объявлений (views_count) в листингах нам не нужны, а нужны они нам только в админке и в личном кабинете пользователя. А значит и не зачем это поле хранить в таблице сущности, блокируя тем самым эту таблицу обновлениями. Вынесли это поле в отдельную таблицу, состоящую из 2 полей: id сущности (объявления) и, собственно, views_count (для автосалонов такое не делали, т.к. там нагрузка на таблицу гораздо меньше). Теперь при необходимости сортировки по числу просмотров (в админке) и для получения числа просмотров в личном кабинете пользователя делаем INNER JOIN этих двух таблиц.

Отлично, одна проблема решена, теперь поиск объявлений, да и просто листинги, стали работать быстро. Но теперь появилась очередь из апдейтов этой новой таблицы. Стали думать, как можно сократить число апдейтов и их интенсивность, но при этом не сильно потерять точность и актуальность данных о количестве просмотров.

Пришли к такой схеме:

  • Создали еще одну таблицу (уже третью). Причем она использует MySQL-движок MEMORY. Эта таблица служит небольшим накопительным окном. Одна запись — один визит (просмотр страницы).
  • Как только таблица-окно заполняется на N записей (N=100 в нашем случае), мы берем эти записи и группируем по id сущности (объявление о продаже). В результате получаем: [id сущности] ⇒ [количество записей (просмотров)]. Параллельно группируем по id автосалона и получаем [id автосалона] ⇒ [количество просмотров].
  • После чего берем все сущности с одинаковым количеством просмотров и обновляем таблицу счетчиков (т.е. первым UPDATE-ом обновляем те сущности, которые просмотрели 1 раз, потом просмотренные 2 раза и т.д.)
  • UPDATE <таблица счетчиков> SET views_count = views_count + <кол-во просмотров> WHERE id IN (<id сущностей с одинаковым кол-вом просмотров>). Аналогично обновляем таблицу автосалонов.
  • Очищаем окно

Способы подсчета статистики посещения страниц сайта «на лету»

В итоге таблицу счетчиков обновляем не 100 раз (размер окна), а в среднем 7 раз.
К тому же для реализации всей этой схемы мы написали базовый класс размером всего 70 строк и каждый последующий наследник (для каждого счетчика) состоял в среднем из 50 строк с реализацией всего двух виртуальных методов.

Какие альтернативы мы рассматривали

  1. Пробовали парсить access логи раз в N минут. Но парсинг был не всегда тривиальным. К тому же дважды пришлось менять этот парсер: первый раз, когда поменялся формат URL-ов, второй — после изменения формата логов.
  2. Пробовали хранить в memcached и потом оттуда по расписанию выгружать в базу. Но определять, по каким ключам лезть за информацией, оказалось непросто, и бизнес-логика в какой-то момент стала слишком сложной.
  3. Конечно же думали насчет использования http://top.mail.ru/, но получать данные для большого числа сущностей, да к тому же по разным счетчикам внутри самой сущности, представилось невозможным. Если бы нам надо было выводить только количество просмотров на конкретной странице объявления, то тогда это было бы отличным решением. Наша задача оказалась шире, нам, как я уже говорил, нужно было сортировать и делать еще кое-какие манипуляции в админке по этим счетчикам

Всем удачи с подсчетами. Не изобретайте свои велосипеды без реальной необходимости.

Автор: graywolfxxx

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js