Где лучше хранить глобальный циклический буфер посещений(посетителей) сайта?
Я понимаю, что преждевременная оптимизация -- причина множества кручин, но тем не менее. Вот допусти надо где-то хранить глобальный циклический буфер посещений страницы сайта. Независимо от сессий. Варианты:
Например (1), просто в файле. Типа циклического лога. При обращении к странице раз читаем файл, обрабатываем (сами или библиотеками) и записываем в тот же файл. Файловых операций две: одна на чтение, второй раз на запись.
Можно тоже самое проделывать с базой (2). Завести таблицу и в нее при каждом обращении к странице добавлять запись, и удалять самую старую. Минус -- в базе накапливается мусор (удаленные записи на самом деле все еще там, и чтобы база не "пухла" понапрасну ее иногда придется чистить.
Более извратный способ -- хранить циклический буфер как файл, но в базе (3). Завести строку с одним полем пожирнее, и в нем, как в файл варианта 1, все складываем. Один SELECT, один UPDATE.
Понятно, что в случаях 1 и 3 сколько-то ресурса сожрет разбор этого циклического буфера. И это время поможет сэкономить вариант 2. Но, допустим, что этим можно временем пренебречь. Какой вариант предпочтительнее/правильнее/распространёнее?? Может еще есть варианты? Буфер небольшой, нагрузки умеренные, городить всякие внешние службы очередей не хочется.
Не надо ничего удалять.
Одна специальная запись (в отдельной таблице) будет хранить номер (первичный ключ) последней записи в логе.
На добавление новой записи в лог расходуется ровно один UPDATE (первичный ключ извлекается подзапросом), после чего номер последней записи инкрементируется и берется остаток по модулю равному количеству записей лога в ротации. Если СУБД позволяет, то идеально было бы сделать это на триггере апдейта в таблицу лога, тогда эти апдейты со стороны клиента становятся полностью атомарными, их больше ненужно заворачивать в транзакции, можно выполнять в любом порядке, в отложенном режиме (не дожидаясь ответа сервера) если драйвер БД это допускает (это может сильно увеличить производительность).
Что касается файла: если вы не являетесь экспертом в различных режимах fsync и атомарности дисковых транзакций, то не советую заниматься частой перезаписью файла с ценные данными. На этом обожглись многие разработчики СУБД не говоря уже о прикладных кодерах. (когда кажется, что последовательность дисковых операций просто не допускает возможности базе остаться в инвалидном состоянии, но все равно после падения база каким-то образом оказывается в инвалидном состоянии).
Сергей Еремин: Если в таблице не полей переменной длинны (БЛОБы, текст не фиксированной длины), нет лишних индексов, нет добавлений/удалений (только апдейты) - такая таблица не распухнет никогда и будет работать очень быстро.
nirvimel: Мне ваш вариант нравится. Если в кольцевом буфере 10-100 записей, это, кажется самым оптимальный. Читаем всю таблицу сортированную по дате. ID+1 самой свежей записи, если меньше размера буфера -- запись необходимая для UPDATE (если записей меньше размера буфера, то INSERT) . Если ID+1 самой свежей записи выходит за приделы буфера, то делаем UPDATE для ID=1... Все. При этом буфер отсортированный по дате в приложении и делай с ним что хочешь...
Для большого буфера (на 10000 записей например) надо думать. Весь буфер тащить в приложение -- затратно по памяти, а если не тащить, то придется делать еще один запрос на размер буфера (для проверки, чтобы понят нужен INSERT или UPDATE).
Сергей Еремин: Можно предварительно заполнить все записи пустышками (BULK INSERT отработает очень быстро) и забыть об INSERT вообще (это снизит фрагментацию таблицы). Или можно отлавливать исключение, вылетающее при попытке UPDATE несуществующей записи, и делать INSERT в обработчике исключения.
Кстати, можно даже не делать поле с timestamp индексным (сортировка по ID даст тот-же результат), что ускорит UPDATE!
...но если когда-нибудь надо будет увеличить этот буфер, потребуется дополнительные действия. Руками или дописать костыль с отдельной проверкой с дополнительным запросом (узнать число записей в базе буфера), и если их не хватает в случае увеличения этого буфера, то дополнительно BULK INSERT-ить, а проверка в костыле "повиснет" замедлителем...
Для правильного вопроса надо знать половину ответа
Файлы тут не годятся однозначно, ибо не поддерживают атомарную вставку/удаление строк. Вам придётся полностью блокировать файл на чтение/запись, иначе будут происходить конфликты версий и часть информации потеряется. То же самое и для blob-записи в базе данных.
Так что оптимальный вариант - стандартная таблица БД с очисткой по крону/триггеру самой БД.
Сергей Еремин:
чистить не значит всё удалять.
а в линуксах logrotate для файлов есть, в пару строк прицепляется.
> Хранить что угодно
Ответ такой же: хранить где угодно
Зависит от того что конкретно и для каких целей.
Если надо будет выборку потом сделать по юзеру/действию/минутам/часам, то очевидно удобнее использовать базу данных.
Если чисто для "дебага" то текстовые логи с logrotate.
Если какие-то супер большие и хитровымудренные логи для мониторинга и аналитики(?), то есть некая связка logstash kibana elasticsearch (elk stack). Не пользовался, не читал особо, но все прям поклоняются этому. Какие конкретно задачи делает тоже толком не скажу, может наврал. https://www.digitalocean.com/community/tutorials/h...