Python: замедлите чтение и запись для миллионов маленьких файлов

Заключение: кажется, что HDF5 является способом пойти в моих целях. В основном "HDF5 является моделью данных, библиотекой и форматом файла для того, чтобы сохранить и управлять данными". и разработан для обработки невероятных объемов данных. Это имеет модуль Python, названный таблицами Python. (Ссылка находится в ответе ниже),

HDF5 делает задание, сделанное на 1 000% лучше в сохранении тонн и тонн данных. Чтение/изменение данных из 200 миллионов строк является болью, хотя, таким образом, это - следующая проблема, которая займется.


Я создаю дерево каталогов, которое имеет тонны подкаталогов и файлов. Существует приблизительно 10 миллионов распространений файлов приблизительно сто тысяч каталогов. Каждый файл находится под 32 подкаталогами.

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

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

def addInFile(path, scoreToAdd):
    num = scoreToAdd
    try:
        shutil.copyfile(path, '/tmp/tmp.txt')
        fp = open('/tmp/tmp.txt', 'r')
        num += int(fp.readlines()[0])
        fp.close()
    except:
        pass
    fp = open('/tmp/tmp.txt', 'w')
    fp.write(str(num))
    fp.close()
    shutil.copyfile('/tmp/tmp.txt', path)
  • Реляционные базы данных кажутся слишком медленными для доступа к этим данным, таким образом, я выбрал подход файловой системы.
  • Я ранее пытался выполнить консольные команды Linux для них, но это был путь медленнее.
  • Я копирую файл во временный файл, сначала затем получают доступ/изменяют к нему, затем копируют его назад, потому что я нашел, что это было быстрее, чем прямой доступ к файлу.
  • Помещение всех файлов в 1 каталог (в reiserfs формате) вызвало слишком много замедления при доступе к файлам.

Я думаю, что причина замедления состоит в том, потому что существуют тонны файлов. Выполнение этой функции 1000 раз синхронизируется в меньше, чем секунда.. но теперь это достигает 1 минуты.

Как Вы предлагаете, чтобы я зафиксировал это? Я изменяю свою структуру дерева каталогов?

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

14
задан Benjamin Intal 15 June 2010 в 03:16
поделиться

6 ответов

Я знаю, что это не прямой ответ на ваш вопрос, но это прямое решение вашей проблемы.

Вам нужно исследовать использование чего-то вроде HDF5. Он предназначен как раз для иерархических данных с миллионами отдельных точек данных.

Вам РЕАЛЬНО повезло, потому что для HDF5 существует потрясающая привязка к Python под названием pytables. Я использовал его очень похожим образом и добился огромного успеха.

6
ответ дан 1 December 2019 в 12:38
поделиться

Два предложения:

Во-первых, , структура, которая включает 32-глубину вложенности подкаталогов, по своей сути ошибочна. Предполагая, что у вас действительно есть «около 10 миллионов файлов», одного уровня подкаталогов должно быть абсолютно достаточно (при условии, что вы используете современную файловую систему).

Второй : вы говорите, что у вас «около 10 миллионов файлов», и что каждый файл «содержит целую строку». Предполагая, что это 32-битные целые числа, и вы храните их напрямую, а не в виде строк, общий размер набора данных составляет 40 МБ (10 МБ файлов * 4 байта на файл). Предполагая, что каждое имя файла имеет длину 32 байта, добавьте к этим данным еще 320 МБ для «ключей».

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

6
ответ дан 1 December 2019 в 12:38
поделиться

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

Если вы выполняете много операций ввода-вывода, вы также можете просто добавить больше оборудования и использовать твердотельные накопители или хранить все данные в ОЗУ (явно или путем кэширования). С одними жесткими дисками у вас нет шансов добиться хорошей производительности в этом сценарии.

Я никогда не использовал его, но, например, Redis - это постоянное хранилище ключей и значений, которое должно работать очень быстро. Если ваши данные подходят этой модели, я бы обязательно попробовал это или что-то подобное. В этой статье вы найдете некоторые данные о производительности, которые должны дать вам представление о том, каких скоростей вы можете достичь.

5
ответ дан 1 December 2019 в 12:38
поделиться

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

РЕДАКТИРОВАТЬ: в предыдущей версии есть ошибка, когда количество цифр становится меньше, чем текущее количество цифр (например, если вы вычитаете или добавляете отрицательное число); в этой версии это исправлено, результат синхронизации практически не изменяется

def addInFile(path, scoreToAdd):
    try:
        fp = open(path, 'r+')
    except IOError as e:
        print e
    else:
        num = str(scoreToAdd + int(fp.read()))
        fp.seek(0)
        fp.write(num)
        fp.truncate(len(num))
    finally:
        fp.close()

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

def addInFile(path, scoreToAdd):
    try:
        orig = open(path, 'r')
        tmp = open('/home/lieryan/junks/tmp.txt', 'w')
    except IOError as e:
        print e
    else:
        num = int(orig.read())
        tmp.write(str(scoreToAdd + num))
    finally:
        orig.close()
        tmp.close()
    try:
        # make sure /tmp/ and path is in the same partition
        # otherwise the fast shutil.move become a slow shutil.copy
        shutil.move(path, '/home/lieryan/junks/backup.txt')
        shutil.move('/home/lieryan/junks/tmp.txt', path)
        os.remove('/home/lieryan/junks/backup.txt')
    except (IOError, shutil.Error) as e:
        print e

также не используйте голые исключения.

Как насчет того, чтобы сгруппировать все 256 файлов на нижнем листе в один файл большего размера? Затем вы можете прочитать несколько чисел за один раз в одном кэше. А если вы использовали файл фиксированной ширины, то вы можете быстро использовать seek (), чтобы перейти к любой записи в файле в O (1).

Некоторые тайминги, запись 1000 раз в один и тот же файл:

  • Ваш исходный подход: 1.87690401077
  • Мой первый подход (открывается с помощью rw +): 0,0926730632782
  • Мой второй подход, скопируйте в тот же раздел: 0,464048147202

(все функции не тестировались на пути обработки ошибок)

1
ответ дан 1 December 2019 в 12:38
поделиться
  1. Диск ограничен количеством байтов, которые он может читать / записывать в секунду, а также количеством операций , которые он может выполнять за секунду.
  2. Пока ваши небольшие файлы кэшируются, операции выполняются значительно быстрее, чем с некэшированными файлами.

Похоже, вы столкнулись с обеими проблемами:

  • выполняете слишком много операций ввода-вывода
  • заканчиваете кеш

Я бы посоветовал пересмотреть структуру, которую вы используете, и использовать файлы меньшего размера. Сохраняйте в minf (как показывает опыт), чем операция ввода-вывода менее 128 КБ, затраты времени выполнения более или менее равны 1 байту ввода-вывода!

2
ответ дан 1 December 2019 в 12:38
поделиться

Разрешение всех этих подкаталогов требует времени. Вы перегружаете файловую систему.

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

/parent/00/01/02/03/04/05/06/07
       /08/09/0A/0B/0C/0D/0E/0F
       /10/11/12/13/14/15/16/17
       /18/19/1A/1B/1C/1D/1E/1F.txt

... вы могли бы создать файл с таким путем:

/parent/00_01_02_03_04_05_06_07_
        08_09_0A_0B_0C_0D_0E_0F_
        10_11_12_13_14_15_16_17_
        18_19_1A_1B_1C_1D_1E_1F.txt

... конечно, у вас все равно будет проблема, потому что теперь все ваши десять миллионов файлов будут находиться в одном каталоге, а по моему опыту (NTFS), каталог с более чем несколькими тысячами файлов в нем все равно перегружает файловую систему.

Можно придумать гибридный подход:

/parent/00_01_02_03/04_05_06_07
       /08_09_0A_0B/0C_0D_0E_0F
       /10_11_12_13/14_15_16_17
       /18_19_1A_1B/1C_1D_1E_1F.txt

Но это все равно создаст проблемы, если вы исчерпывающе создадите все эти каталоги. Даже если большинство этих каталогов "пустые" (в том смысле, что они не содержат никаких файлов), операционная система все равно должна создать запись INODE для каждого каталога, а это занимает место на диске.

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

На сколько уровней вглубь следует создавать иерархию каталогов? В моем примере я преобразовал 32-уровневую иерархию в 8-уровневую, но после тестирования вы можете выбрать немного другое отображение. Это действительно зависит от ваших данных и от того, насколько равномерно эти пути распределены в комбинаторном пространстве решений. Вам нужно оптимизировать решение с двумя ограничениями:

1) Минимизировать количество создаваемых каталогов, зная, что каждый каталог становится INODE в базовой файловой системе, и создание слишком большого их количества приведет к перегрузке файловой системы.

2) Минимизируйте количество файлов в каждом каталоге, зная, что слишком большое количество файлов на каталог (по моему опыту, более 1000) перегружает файловую систему.

Есть еще одно соображение, которое следует иметь в виду: Место для хранения на дисках адресуется и выделяется с помощью "блоков". Если вы создаете файл размером меньше минимального размера блока, он, тем не менее, занимает весь блок, расходуя дисковое пространство. В NTFS эти блоки определяются "размером кластера" (который частично определяется общим размером тома) и обычно по умолчанию составляет 4 кБ:

http://support.microsoft.com/kb/140365

Таким образом, если вы создадите файл с одним байтом данных, он все равно займет 4 кБ дискового пространства, потеряв 4095 байт.

В своем примере вы сказали, что у вас около 10 миллионов файлов, содержащих около 1 гБ данных. Если это так, то каждый из ваших файлов имеет длину всего около 100 байт. При размере кластера 4096 у вас примерно 98% потраченного пространства.

Если это возможно, попробуйте объединить некоторые из этих файлов. Я не знаю, какого рода данные они содержат, но если это текстовый формат, вы можете попробовать сделать что-то вроде этого:

[id:01_23_45_67_89_AB_CD_EF]
lorem ipsum dolor sit amet consectetur adipiscing elit
[id:fe_dc_ba_98_76_54_32_10]
ut non lorem quis quam malesuada lacinia
[id:02_46_81_35_79_AC_DF_BE]
nulla semper nunc id ligula eleifend pulvinar

... и так далее, и так далее. Может показаться, что вы теряете место из-за всех этих многословных заголовков, но для диска это гораздо более эффективная стратегия, чем иметь отдельные файлы для всех этих маленьких фрагментов. Этот маленький пример использовал ровно 230 байт (включая новые строки) для трех записей, поэтому вы можете попробовать поместить около шестнадцати записей в каждый файл (помня, что гораздо лучше иметь чуть меньше 4096 байт на файл, чем чуть больше 4096, тратя целый дополнительный блок диска).

Как бы то ни было, удачи!

2
ответ дан 1 December 2019 в 12:38
поделиться
Другие вопросы по тегам:

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