Вопрос или проблема
Это кажется достаточно распространенной проблемой, что, вероятно, это дубликат, но я не смог найти никаких других похожих вопросов.
У меня есть что-то вроде лог-файла, к которому я добавляю данные, но я также хочу, чтобы он оставался отсортированным, поэтому у меня есть что-то вроде этого:
echo "foo" >> file.txt
LC_ALL=C sort -k1,1 -u -o file.txt file.txt
Это действительно небольшой файл (<1000 строк), поэтому запись и сортировка должны быть очень быстрыми. Однако иногда у меня может быть около 5 событий в секунду, но некоторые из них теряются. Я был очень удивлен, увидев эту проблему с такой низкой пропускной способностью приложения.
Добавление в файл должно работать каждый раз, поэтому, вероятно, проблема возникает при сортировке. Я думал, что -o
(записать результат в файл вместо стандартного вывода) решит проблемы с конкурентностью, но, возможно, это не так.
Существует ли правильный способ сортировки файла, если другой процесс может записывать в него?
Смотря на реализацию sort
, к которой у меня есть самый простой доступ (OpenBSD), выглядит так, что вы потеряете данные, если входной файл будет дополнен между
- завершением чтения данных из входного файла утилитой
sort
и началом фактической сортировки, и - переименованием временного выходного файла утилитой
sort
в имя реального выходного файла (предоставленного аргументом-o
) незадолго до выхода.
Я действительно не вижу способа предотвратить это, кроме как временно остановить любой процесс, записывающий в файл, перед сортировкой, а затем снова запустить их, когда сортировка будет завершена. Это можно сделать с помощью kill -STOP
и kill -CONT
соответственно.
Процесс, записывающий в файл, должен будет снова открыть его для записи, иначе он будет записывать в файл, который больше не находится в структуре каталога (так как он был заменен). GNU sort
кажется заменяет содержимое оригинального файла отсортированными данными, поэтому это не является проблемой.
На системе GNU:
$ seq 10 > file $ strace sort -o file file [...] openat(AT_FDCWD, "file", O_WRONLY|O_CREAT|O_CLOEXEC, 0666) = 3 dup2(3, 1) = 1 close(3) = 0 openat(AT_FDCWD, "file", O_RDONLY|O_CLOEXEC) = 3 [...] read(3, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n", 512) = 21 read(3, "", 512) = 0 lseek(3, 0, SEEK_CUR) = 21 close(3) = 0 ftruncate(1, 0) = 0 fstat(1, {st_mode=S_IFREG|0775, st_size=0, ...}) = 0 write(1, "1\n10\n2\n3\n4\n5\n6\n7\n8\n9\n", 21) = 21 close(1) = 0
Как вы можете видеть выше, sort
действительно открывает выходной файл для записи без обрезки даже перед его открытием для чтения, чтобы убедиться, что он сможет записывать в него, но затем позже, после чтения и сортировки входных данных, обрезает его перед записью отсортированного содержимого.
Таким образом, если в файл были добавлены дополнительные строки между тем, как sort
изначально прочитал его содержимое и отсортировал его, они будут потеряны.
Возможно, еще хуже, если эти дополнительные строки будут добавлены в то время, как sort
записывает отсортированный вывод, они будут перемешиваться с отсортированными строками, вероятно, будут записаны посередине некоторых отсортированных строк, поскольку sort
будет записывать вывод блоками.
Чтобы этого избежать, вы можете сделать:
sort < file 1<> file
Где оболочка (не sort
) откроет file
для чтения на fd 0 и для записи на fd 1 без обрезки. Технически <>
открывает файл в режиме чтения+записи. Здесь нам не важно чтение, важно только то, что флаг O_TRUNC
не передается в open()
, как это было бы с >
(и, очевидно, мы не можем использовать >>
, которое не обрезает, но добавляет вывод в конец).
Тогда отсортированный вывод будет записан поверх входных данных, и если в интервал добавились какие-то данные, они останутся там (несортированными).
Этот подход должен работать на любой системе, не только на GNU, и будет иметь преимущество в сохранении метаданных файла на таких системах, как OpenBSD, где sort -o file
создает новый file
.
Ответ или решение
Сортировка файла на месте в условиях конкуренции записи
Актуальная задача
Сортировка файла, к которому одновременно могут записываться данные, является распространенной и актуальной задачей в области информационных технологий. Ваша ситуация с лог-файлом, который часто обновляется, требует тщательного подхода, чтобы избежать потери данных при их сортировке. Стандартные утилиты, такие как sort
, могут не обеспечивать безопасность данных, если они используются в условиях конкурентного доступа.
Проблема
Когда вы хотите отсортировать файл, который может изменяться в процессе работы, возникает несколько критических моментов:
-
Чтение и запись: Утилита
sort
сначала читает файл, а затем сортирует и записывает его обратно. Если в промежутке между этими операциями происходят изменения в файле, новые записи могут быть утеряны или перемешаны с уже отсортированными данными. -
Конкурентный доступ: Если другой процесс продолжает записывать в файл, это создает возможность для «прыжков», когда индекс данных изменяется, тем самым нарушая порядок при записи в файл.
Решение
Во избежание этих проблем можно использовать несколько подходов:
-
Сигналы для управления процессами:
- Вы можете временно приостановить процесс записи в файл, используя сигналы
kill -STOP
иkill -CONT
. Однако это может быть неэффективно и неудобно для применения.
- Вы можете временно приостановить процесс записи в файл, используя сигналы
-
Использование командной оболочки:
Чтобы избежать усечения данных, используйте перенаправление ввода-вывода без обрезки файла. Это можно сделать с помощью следующей команды:sort < file.txt 1> file.txt
В этом случае файл будет открыт для чтения и записи одновременно, не обрезая содержимое. Данные, которые были добавлены во время процесса сортировки, останутся в файле, но они останутся неотсортированными.
-
Создание временного файла:
Более надежным решением будет использование временного файла. Вы можете сначала отсортировать данные в новом файле, а затем заменить оригинальный файл:sort file.txt > temp.txt && mv temp.txt file.txt
Это гарантирует, что во время сортировки файл не будет изменен, и что только отсортированные данные будут записаны в оригинальный файл.
Заключение
Сортировка файла в условиях конкуренции записи требует внимательного подхода, чтобы гарантировать целостность данных. Использование перенаправления ввода-вывода или создание временного файла являются эффективными и безопасными способами решения этой задачи. Эти методы минимизируют риск потери данных и обеспечивают аккуратную сортировку.
Выбор оптимального решения зависит от вашей конкретной среды выполнения и требований к производительности. Системы, с которыми вы работаете, и их конфигурация могут влиять на выбор подхода, поэтому тестирование в вашей конкретной среде всегда будет полезным.