fopen блокировка файла в PHP (тип чтения / записи типа)

У меня есть сценарий, когда один процесс PHP пишет файл примерно 3 раза в секунду, а затем несколько PHP-процессов читают этот файл.

Этот файл представляет собой кеш. На нашем сайте очень настойчивый опрос, который постоянно меняется, и мы не хотим, чтобы каждый посетитель попадал в БД каждый раз, когда они опросили, поэтому у нас есть процесс cron, который читает DB 3 раза в секунду, обрабатывает данные, и выгружает его в файл, который затем могут считывать клиенты опроса.

Проблема, с которой я сталкиваюсь, заключается в том, что иногда открытие файла для записи занимает много времени, иногда даже до 2-3 секунд. Я предполагаю, что это происходит из-за того, что он заблокирован чтением (или чем-то), но у меня нет убедительного доказательства этого, плюс, согласно тому, что я понимаю из документации, PHP не должен блокировать что-либо , Это происходит каждые 2-5 минут, так что это довольно часто.

В коде я не занимаюсь какой- либо блокировкой, и мне почти все равно, повреждена ли информация этого файла, если сбой чтения, или если данные изменяются в середине чтения. Тем не менее, мне все равно, если письмо на это занимает 2 секунды, в частности, потому что процесс, который должен произойти трижды в секунду, пропустил несколько ударов.

Я пишу файл с этим кодом:

$handle = fopen(DIR_PUBLIC . 'filename.txt', "w"); fwrite($handle, $data); fclose($handle); 

И я читаю его напрямую:

 file_get_contents('filename.txt') 

(он не доставляется непосредственно клиентам как статический файл, я получаю регулярный PHP-запрос, который читает файл и делает некоторые базовые вещи с ним)

Файл составляет около 11kb, поэтому для чтения / записи не требуется много времени. Хорошо под 1 мс.

Это типичная запись в журнале, когда возникает проблема:

  Open File: 2657.27 ms Write: 0.05984 ms Close: 0.03886 ms 

Не уверен, что это актуально, но чтение происходит в обычных веб-запросах через apache, но запись – это обычная «командная строка» PHP-исполнения, выполняемая cron Linux, она не проходит через Apache.

Любые идеи о том, что может вызвать эту большую задержку при открытии файла?
Любые указатели на то, где я мог бы посмотреть, чтобы помочь мне определить фактическую причину?

Или вы можете думать о чем-то, что я мог бы сделать, чтобы избежать этого? Например, мне бы хотелось установить 50-минутный тайм-аут для открытия, и если он не откроет файл, он просто пропустит вперед и позволит выполнить следующий запуск cron.

Опять же, мой приоритет заключается в том, чтобы держать cron бить три раза, все остальное вторично, поэтому любые идеи, предложения, что-то очень приветствуются.

Спасибо!
Даниил

Я могу думать о 3 возможных проблемах:

  • файл становится заблокированным при чтении / записи с помощью более низких системных вызовов php, не зная об этом. Это должно блокировать файл для 1 / 3s max. Вы получаете больше времени, чем это.
  • кэш fs запускает fsync (), и вся система блокирует чтение / запись на диск до тех пор, пока это не будет выполнено. В качестве исправления вы можете попробовать установить больше ОЗУ или обновить ядро ​​или использовать более быстрый жесткий диск
  • ваше «кэширующее» решение не распространяется, и оно поражает наихудшее действующее устройство во всей системе много раз в секунду … что означает, что вы не можете масштабировать его дальше, просто добавляя больше машин, только увеличивая скорость hdd. Вы должны взглянуть на memcache или APC или, возможно, на разделяемую память http://www.php.net/manual/en/function.shm-put-var.php

Решения, о которых я могу думать:

  • поместите этот файл в ramdisk http://www.cyberciti.biz/faq/howto-create-linux-ram-disk-filesystem/ . Это должен быть самый простой способ избежать удара диска так часто, без каких-либо серьезных изменений.
  • использовать memcache. Его очень быстро при использовании на местном уровне он хорошо масштабируется, используется «большими» игроками. http://www.php.net/manual/en/book.memcache.php
  • используйте общую память. Он был разработан для того, что вы пытаетесь сделать здесь … с «общей зоной памяти» …
  • изменить планировщик cron для обновления меньше, возможно, реализовать какую-то систему событий, поэтому он будет обновлять кеш при необходимости, а не на временной основе
  • сделайте сценарий «записи» пишите в 3 файла по-разному и сделайте «читателей» прочитанными из одного из файлов случайным образом. Это может позволить «распределению» удалений по большему количеству файлов, и это может уменьшить вероятность блокировки определенного файла при записи на него … но я сомневаюсь, что это принесет какие-либо заметные выгоды.

Вы должны использовать что-то действительно быстрое решение, если хотите гарантировать постоянное низкое время открывания. Возможно, ваша ОС выполняет синхронизацию диска, файл базы данных или другие вещи, с которыми вы не можете работать.

Я предлагаю использовать memcached, redis или даже mongoDB для таких задач. Вы даже можете написать свой собственный демон кэширования, даже в php (однако это совершенно не нужно и может быть сложным).

Если вы абсолютно уверены, что вы можете решить эту задачу только этим кешем файлов, и вы находитесь под Linux, попробуйте использовать другой планировщик ввода-вывода на диске, например, крайний срок, OR (cfq И уменьшите приоритет PHP-процесса до -3 / -4).