Какая best practice защиты от записи в файл в несколько потоков?

Добрый день.
Я немного отстал от версий php в этой области и прошу прокомментировать какие лучшие методы сейчас есть для решения задачи.
Задача.
Есть скрипт который пишет в файлы последовательно информацию. Скрипт дергается в несколько потоков. И если в момент передачи в скрипт очередной порции данных первый файл данных занят записью скрипт должен писать во второй файл.
php 7.1
Варианты которые я вижу
1) При открытии файла ставим блокировку. Второй "поток" не сможет получить доступ.
Жирный минус - 99ый поток передергает 98 файлов на предмет наличия блокировки перед нахождением нужного файла для записи. Это явно будет подтормаживать, а в некоторых операционках возможно еще и вызовет дополнительную нагрузку на IO операционки, а возможно даже диск.
2) Файл блокировок. В одном файле храним список файлов доступных для записи. Минусы - адски сложно реализовать, при том все равно в ряде случаев могут начаться проблемы с мультидоступом к этому файлу, два "потока" могут получить одновременно разрешение писать в один файл. Мы тогда не только не защитимся, а еще и разрушим данные одновременной записью. Ну и опять дергаем постоянно жесткий диск, причем в данном случае каждый поток будет порождать 2 записи в файл. В общем совсем никакой вариант
3) Таблица блокировок. То же самое что пункт 2, но храним таблицу где-то в ... ну скажем memcache. Остаются проблемы с конкурентным доступом, но хотяб не насилуем диск лишними операциями.

Собственно из трех вариантов предпочтительным выглядит 1ый. Но может есть какие-то лучшие варианты в актуальных версиях php?
  • Вопрос задан
  • 1352 просмотра
Пригласить эксперта
Ответы на вопрос 6
@BorisKorobkov Куратор тега PHP
Web developer
Не всегда поставленную задачу надо выполнять в лоб. Иногда надо подумать, а зачем писать в несколько файлов. Например, у nginx сотни потоков одновременно могут писать ОДИН access_log, никаких проблем с этим не возникает.
- Либо один поток-логгер, получающий данные из очереди
- Либо разные потоки, все пишут (append) текст в один файл в неблокирующем режиме
Ответ написан
Нужно дергать не скрипт, а некое API на создание простых файберов (fibers), которые работая внутри вечного цикла (event loop) поочередно будут писать в файл. На уровне этого цикла их работу можно легко приоритезировать, например, по важности данных или лимиту времени.

Многопоточно можно попробовать решить эту задачу на другом языке, например, на Go.
Ответ написан
Комментировать
latteo
@latteo
Записать в файл с уникальным именем потом собирать в один.
Я microtime(true) при запуске скрипта запоминаю и формирую из него имя файла. Есть нативная uniqid(). При записи проверка, что файл не существует, пересечения на 100 потоков бывают очень редко. Но у вас может быть и иначе, в зависимости от времени работы каждого потока и их количества.
По сути это медленный аналог очереди в памяти, но зато могу быть уверенным, что на диске информация точно осталась.
Ответ написан
Комментировать
@bashkarev
@theg4sh
Проблему с проверкой занятости файла думаю лучше всего решать с помощью shm:
php.net/manual/en/ref.sem.php
Либо установкой брокировки на запись, используя:
php.net/manual/en/function.flock.php

Используя семафоры, можно маркировать файл как используемый, при этом не будет необходимости в стороннем хранилище, таком как memcache.

В случае, если требуется "плотная" запись (имею ввиду, что есть требование писать сперва в первые доступные файлы), нужно будет использовать цикл поиска свободного от записи файла.
Если плотная запись не требуется, то можно фиксировать имя последнего закрытого файла опять же в shm, минусом данной реализации будет неравномерное распределение данных.

Имхо, в вашем случае будет достаточно flock(). Внутри, насколько понимаю, как раз используется shm для бокировок записи (прошу поправить меня если я ошибаюсь).

Примерно так может выглядеть поиск первого доступного файла:
for($i=0; $i<MAX_OFILES; ++$i) {
  $fp = fopen("file$i.log", "w");
  if ($fp && !flock($fp, LOCK_EX | LOCK_NB)) {
    fclose($fp);
    continue;
  }
  // write your actions here
  fclose($fp);
  break;
}

Пример ожидания доступности файла не сильно отличается от приведенного и есть по ссылке на доку к методу flock.
з.ы. Давно не писал на пыхе, поэтому код может быть не валидным.
Ответ написан
Комментировать
xmoonlight
@xmoonlight
https://sitecoder.blogspot.com
Всё сложное - тупо!
©2020, xmoonlight

1. Сразу создаём пакетную обработку запросов с единым менеджером очередей.
2. Пакет - это несколько независимых запросов в единицу времени на чтение или запись.
3. Две очереди: чтение и запись.
4. Попеременно проверяем наличие пакета в каждой из очередей и пакет запросов исполняем асинхронно.
5. Динамически подстраиваем длину пакета в зависимости от типа запросов и приблизительной скорости их исполнения.
Profit!
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы