none
Редактировать огромные файлы RRS feed

  • Вопрос

  • Задача такая. Есть файл несколько гигов и есть много мелких изменений в секунду. По мимо обычного редактирования, возможны вставки данных. Как реализовать это, что бы все работало быстро? Первая мысль которая мне приходит, это реализовать постраничную запись. Может быть этот вопрос уже решен на уровне ОС и я зря парюсь? Возможно так же существуют уже готовые классы в .Net ?
    14 января 2011 г. 23:40

Ответы

  • Возможен такой вариант. Большому файлу поставить в соответствие индексный файл, в котором хранить адреса и размеры непрерывных секторов, выборку осуществлять последовательными непрерывными секторами. Поясню. Вначале большому файлу соответствует один сектор - адрес 0, размер - длина файла; если нужно сделать вставку, то файл делится на 3 части - 1 - адрес Addr_0, размер - Length_0, длина до вставки, 2 - адрес Addr_1=0+Length_0, размер - Length_1, длина вставки, 3 - адрес Addr_2=Addr_1+Length_1, размер Length_2, длина от разрыва до конца исходного файла. В индексном файле имеем записи - Addr_0,Length_0; Addr_1,Length_1; Addr_2,Length_3; Все остальные дополнения, исправления, вставки, удаления и пр. генерируют непрерывные сектора. Достоинства - никаких перезаписей, Недостаток - сложный алгоритм, но не настолько сложный, чтобы его не смог реализовать программер среднего уровня.
    • Помечено в качестве ответа Max Charp 18 января 2011 г. 22:01
    16 января 2011 г. 12:17
  • Вот здесь рассматриваются стратегии вставки данных в середину файла (без использования механизма постраничной записи) с примерами кода.

    А реализация алгоритма постраничной записи довольно проста: один отраженный в память (для обеспечения максимальной производительности) файл для хранения частей данных и еще один индексный файл, хранящий смещения и длину блоков. Добавление блока - это дописывание данных в первый файл и создание новой страницы в индексном файле. Удаление блока - это редактирование информации о страницах в индексном файле. Во время сборки итогового файла либо пользуемся одним из вышеприведенных алгоритмов, либо создаем новый временный файл. Во втором случае в процессе формирования итогового файла можно будет обеспечить еще и чтение из файла, если будут осуществляться запросы на чтение. Записываемые блоки на время формирования придется временно хранить в памяти.

    • Предложено в качестве ответа Abolmasov Dmitry 18 января 2011 г. 10:27
    • Помечено в качестве ответа Abolmasov Dmitry 18 января 2011 г. 10:42
    • Снята пометка об ответе Max Charp 18 января 2011 г. 22:10
    • Помечено в качестве ответа Max Charp 18 января 2011 г. 22:10
    16 января 2011 г. 13:15
  • И еще один вопрос вам, как тому кто начал говорить про мемори маппинг. Что будет с изменениями, если сделать килл процесс или сбой? Запишутся ли изменения в итоговый файл?

    Если произойдет сбой питания, то данные или не запишутся, или в процессе записи испортят содержимое файла. Если процесс просто завершается, то ОС сбрасывает все ассоциированные с ним буферы, и все несохраненные страницы сохраняются.
    • Помечено в качестве ответа Max Charp 19 января 2011 г. 13:10
    19 января 2011 г. 5:58
  • Для работы с такими файлами не надо мелочиться, нужно 1. Обзавестись бесперебойным питанием, 2. Предусмотреть сохранение при отключении, 3. Предусмотреть безопасную архитектуру такой базы - резервирование в процессе редактирования и самовосстановление. Так было, начиная, кажется, с FoxPro.
    • Помечено в качестве ответа Max Charp 19 января 2011 г. 14:13
    19 января 2011 г. 13:43
  • Да, это большая проблема с ASP.NET-приложениями, но, к счастью, есть специальный хак от небезызвестного Скотта Гатри в его блоге вот здесь .
    • Помечено в качестве ответа Max Charp 19 января 2011 г. 14:12
    19 января 2011 г. 14:01
  • Этот код генерирует исключение System.OverflowException, но я понял о чем Вы. Можно настроить application pool таким образом, чтобы он перезапускался при достижении определенного лимита физической или виртуальной памяти. Но даже в этом случае (и даже в случае генерации исключения System.OutOfMemoryException) код в Application_End срабатывает корректно, проверил несколько раз.

    PS. Тестировал на ASP.NET 4.0 с интегрированным конвейером обработки запросов.

    • Помечено в качестве ответа Max Charp 20 января 2011 г. 0:27
    19 января 2011 г. 18:16

Все ответы

  • Насчет очень больших файлов — есть способ , а вот со вставкой даже не знаю, как можно ускорить этот процесс. Разве что временно хранить вставки в отдельных файлах или дописывать их в конец (как это делалось в предыдущих версиях Microsoft Office для увеличения производительности), а формировать нормальный файл реже, раз в несколько минут, при простое или когда возникнет необходимость получить итоговый файл.
    • Предложено в качестве ответа Abolmasov Dmitry 18 января 2011 г. 10:27
    • Помечено в качестве ответа Abolmasov Dmitry 18 января 2011 г. 10:41
    • Снята пометка об ответе Max Charp 18 января 2011 г. 22:02
    • Снята пометка об ответе Max Charp 18 января 2011 г. 22:02
    15 января 2011 г. 10:09
  • Ну значит как я и думал постраничная запись.  Попробую поискать что нить готовое.
    16 января 2011 г. 0:43
  • Возможен такой вариант. Большому файлу поставить в соответствие индексный файл, в котором хранить адреса и размеры непрерывных секторов, выборку осуществлять последовательными непрерывными секторами. Поясню. Вначале большому файлу соответствует один сектор - адрес 0, размер - длина файла; если нужно сделать вставку, то файл делится на 3 части - 1 - адрес Addr_0, размер - Length_0, длина до вставки, 2 - адрес Addr_1=0+Length_0, размер - Length_1, длина вставки, 3 - адрес Addr_2=Addr_1+Length_1, размер Length_2, длина от разрыва до конца исходного файла. В индексном файле имеем записи - Addr_0,Length_0; Addr_1,Length_1; Addr_2,Length_3; Все остальные дополнения, исправления, вставки, удаления и пр. генерируют непрерывные сектора. Достоинства - никаких перезаписей, Недостаток - сложный алгоритм, но не настолько сложный, чтобы его не смог реализовать программер среднего уровня.
    • Помечено в качестве ответа Max Charp 18 января 2011 г. 22:01
    16 января 2011 г. 12:17
  • А какого вида данные? Обычная база данных, вроде SQL Server, не подойдет?
    My blog
    16 января 2011 г. 12:50
  • Виноват, не посмотрел на предложенный Вам линк, где, по-видимому, и реализован предложенный мной алгоритм. Тогда вопрос, а что же Вам еще нужно? Нужно брать готовое и использовать, вот и вся недолга!
    16 января 2011 г. 12:52
  • Тип данных. В примере по линку - IMG. Но говорится о возможеости любых данных. Я же имел ввиду байтовый формат, как наиболее универсальный
    16 января 2011 г. 13:04
  • Если файл структурирован последовательными записями, то сектора - это записи.
    16 января 2011 г. 13:09
  • Вот здесь рассматриваются стратегии вставки данных в середину файла (без использования механизма постраничной записи) с примерами кода.

    А реализация алгоритма постраничной записи довольно проста: один отраженный в память (для обеспечения максимальной производительности) файл для хранения частей данных и еще один индексный файл, хранящий смещения и длину блоков. Добавление блока - это дописывание данных в первый файл и создание новой страницы в индексном файле. Удаление блока - это редактирование информации о страницах в индексном файле. Во время сборки итогового файла либо пользуемся одним из вышеприведенных алгоритмов, либо создаем новый временный файл. Во втором случае в процессе формирования итогового файла можно будет обеспечить еще и чтение из файла, если будут осуществляться запросы на чтение. Записываемые блоки на время формирования придется временно хранить в памяти.

    • Предложено в качестве ответа Abolmasov Dmitry 18 января 2011 г. 10:27
    • Помечено в качестве ответа Abolmasov Dmitry 18 января 2011 г. 10:42
    • Снята пометка об ответе Max Charp 18 января 2011 г. 22:10
    • Помечено в качестве ответа Max Charp 18 января 2011 г. 22:10
    16 января 2011 г. 13:15
  • Конечно! Да, в принципе и переписывать ничего не надо, только разве что другу или подруге.
    Прямо так и пользовать его - из кучи. Ведь и файлы на диске так и хранятся. Defrag нужен нечасто.
    16 января 2011 г. 13:21
  • Это краткое описание самописной базы данных, с одной таблицей и кластерным индексом по PK.

    My blog
    16 января 2011 г. 13:22
  • Да, и примитивными блокировками :)

    Согласен с тем, что если итоговые данные нужны нечасто (например, раз в полчаса или по запросу пользователя), проще будет воспользоваться решением на основе СУБД. В MSSQL очень хорошая оптимизация для работы с BLOB-полями.

    16 января 2011 г. 13:31
  • В последнее время я частенько пользуюсь этими самыми примитивами, особенно там где нужна скорость. Только лишь переход от структурированных данных к простым массивам увеличил скорость обработки в 200(!) раз. ;-) Была сложная древовидная структура данных, и на конечном этапе, непосредственно перед циклическим расчетом, я преобразовал ее к простым массивам. Честно говоря, такого эффекта (в 200(!)раз) я не ожидал.
    16 января 2011 г. 14:59
  • Это в поддержку Вашего тезиса о разработке собственноручных средств - программ-пускачей. ;-)
    16 января 2011 г. 15:28
  • Алексей

    Да спасибо, про два файла я не подумал. Сначала я хотел зарезервировать место под разметку в самом начале файла. Но это дало бы свои сложности. Идея с двумя файлами, возможно сэкономила мне пару часов времени :). 

    И еще один вопрос вам, как тому кто начал говорить про мемори маппинг. Что будет с изменениями, если сделать килл процесс или сбой? Запишутся ли изменения в итоговый файл?

    18 января 2011 г. 22:09
  • И еще один вопрос вам, как тому кто начал говорить про мемори маппинг. Что будет с изменениями, если сделать килл процесс или сбой? Запишутся ли изменения в итоговый файл?

    Если произойдет сбой питания, то данные или не запишутся, или в процессе записи испортят содержимое файла. Если процесс просто завершается, то ОС сбрасывает все ассоциированные с ним буферы, и все несохраненные страницы сохраняются.
    • Помечено в качестве ответа Max Charp 19 января 2011 г. 13:10
    19 января 2011 г. 5:58
  • ...Ни спасибо, ни пожалуйста, ни здраствуй, ни прощай... А жаль!
    19 января 2011 г. 10:31
  • Спасибо Вам :)
    19 января 2011 г. 10:45
  • Если произойдет сбой питания, то данные или не запишутся, или в процессе записи испортят содержимое файла. Если процесс просто завершается, то ОС сбрасывает все ассоциированные с ним буферы, и все несохраненные страницы сохраняются.

    Это меня радует. Спасибо всем за ответы.

     

     

    19 января 2011 г. 13:10
  • Для работы с такими файлами не надо мелочиться, нужно 1. Обзавестись бесперебойным питанием, 2. Предусмотреть сохранение при отключении, 3. Предусмотреть безопасную архитектуру такой базы - резервирование в процессе редактирования и самовосстановление. Так было, начиная, кажется, с FoxPro.
    • Помечено в качестве ответа Max Charp 19 января 2011 г. 14:13
    19 января 2011 г. 13:43
  • Честно говоря я больше беспокоюсь за перезапуски процесса сайта, который инициализирует IIS. Этот механизм весьма темный, без уведомлений и т.д. что странно.
    19 января 2011 г. 13:54
  • Прошли те времена, когда при отключении питания головки падали на диск и делали из него ежоподобное устройство. ;-)
    19 января 2011 г. 13:58
  • Да, это большая проблема с ASP.NET-приложениями, но, к счастью, есть специальный хак от небезызвестного Скотта Гатри в его блоге вот здесь .
    • Помечено в качестве ответа Max Charp 19 января 2011 г. 14:12
    19 января 2011 г. 14:01
  • Алексей ты меня делаешь счастливым :)
    19 января 2011 г. 14:12
  • А, Вам же нужно только уведомление о завершении ASP.NET-приложения, а не его причину.

    В этом случае все просто (файл global.asax):

    void Application_End(object sender, EventArgs e)
    {
      // Срочно все сохранить
    }
    

    19 января 2011 г. 14:16
  • Это не всегда срабатывает.
    19 января 2011 г. 14:43
  • Можете привести пример (если он, конечно, без каких-либо экзотических случаев, вроде динамического создания объектов HttpApplication)?

    19 января 2011 г. 15:02
  • На получении запроса.

    byte[] b = new byte[0xaaffffff];

     

    На самом деле я видел как IIS прибивает процесс и за меньшее :). В Server.config настраивается сколько памяти может использовать процесс.

    19 января 2011 г. 15:09
  • Это еще ничего. когда-нибудь обнаружится, что IIS одновременно запускает два экземпляра вашего приложения... :)

    Что у вас за сервер и данные такие, что

    - нагрузку выдерживает IIS

    - ту же нагрузку не выдерживает база SQL Server

    Возми обычный SQL Server и не пиши велосипед. потому что иначе возникнут красивые спецэффекты при фрагментации индексов. и вопросы, вроде, "а как сделать резервную копию не выключая приложение". И полезут проблемы с многопоточностью - это ж ASP.NET приложение.

    Если данных много - используй FileStream. В обычные varbinary влазит 2^31-1.

    Не хватит SQL Server - найди готовый nosql. Зачем все это самописное колдовство? :)


    My blog
    19 января 2011 г. 15:50
  • Ага, из соседнего топика понял, почему готовая DB не подойдет. Но причем тут asp.net?
    My blog
    19 января 2011 г. 16:07
  • >>Это еще ничего. когда-нибудь обнаружится, что IIS одновременно запускает два экземпляра вашего приложения... :)

    С этим я уже смирился через static члены.

    >> обычный SQL Server 

    Обычный SQL Server не справляется приемлемо с задачей  FullTextSearch + Filter + Sort (RealTime)

    >> Зачем все это самописное колдовство? :)

    Может потому что я колдун :). А что разве SQL Server не самописный? На самом деле я не считаю эту задачу какой нибудь мега сложной. Пара недель от силы.

    >>причем тут asp.net?

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

    19 января 2011 г. 16:42
  • Этот код генерирует исключение System.OverflowException, но я понял о чем Вы. Можно настроить application pool таким образом, чтобы он перезапускался при достижении определенного лимита физической или виртуальной памяти. Но даже в этом случае (и даже в случае генерации исключения System.OutOfMemoryException) код в Application_End срабатывает корректно, проверил несколько раз.

    PS. Тестировал на ASP.NET 4.0 с интегрированным конвейером обработки запросов.

    • Помечено в качестве ответа Max Charp 20 января 2011 г. 0:27
    19 января 2011 г. 18:16
  • >>Это еще ничего. когда-нибудь обнаружится, что IIS одновременно запускает два экземпляра вашего приложения... :)

    С этим я уже смирился через static члены.

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

    Здесь можно воспользоваться объектами синхронизации на уровне ОС или блокировать файл, с которым ведется работа.

    19 января 2011 г. 18:20
  • >>С этим я уже смирился через static члены.

    static ничем не спасает от запуска двух рабочих процессов (например, при ресайклинге).

    максимум, от чего спасает статик - от двух одновременных запросов, при правильно расставленых блокировках.


    My blog
    19 января 2011 г. 18:20
  • Два процесса на один сайт.. С таким не сталкивался. У меня HttpApplication запускаются в одном процессе, обычно 2-3. В любом случае маппинг файлов поможет избежать блокировок.

     

    19 января 2011 г. 18:27
  • Выше PashaPash привел пример, когда может возникнуть такая ситуация: на сайт приходит несколько запросов, IIS решает по каким-нибудь причинам перезапустить application pool. При этом старые запросы, которые уже начали обрабатываться, будут обработаны до конца в старом экземпляре приложения (после чего выполнится Application_End), а вновь поступающие запросы в это время будут обрабатываться в новом экземпляре.
    19 января 2011 г. 18:43