Поиск CSV-файла объемом 1 ГБ

#c# #.net #performance #file #csv

#c# #.net #Производительность #файл #csv

Вопрос:

У меня есть CSV-файл. Каждая строка состоит из одного и того же формата, например/

 I,h,q,q,3,A,5,Q,3,[,5,Q,8,c,3,N,3,E,4,F,4,g,4,I,V,9000,0000001-100,G9999999990001800000000000001,G9999999990000001100PDNELKKMMCNELRQNWJ010, , , , , , ,D,Z,
  

У меня есть Dictionary<string, List<char>>

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

Словарь используется в другом месте программы, где он принимает входные данные в программу, а затем находит ключ в словаре и использует 24 элемента для сравнения с входными данными.

 StreamReader s = File.OpenText(file);
 string lineData = null;
 while ((lineData = s.ReadLine()) != null)
 {
   var elements = lineData.Split(',');
   //Do stuff with elements
   var compareElements = elements.Take(24).Select(x => x[0]);
   FileData.Add(elements[27], new List<char>(compareElements));

  }
  s.Close();
  

Мне только что сказали, что размер CSV-файла теперь будет 800 мб и в нем будет примерно 8 миллионов записей. Я только что попытался загрузить это на свой двухъядерный 32-разрядный ноутбук Win с 4 ГБ оперативной памяти в режиме отладки, и он выдал OutOfMemoryException .

Теперь я думаю, что лучшим вариантом будет не загружать файл в память, но мне нужно найти способ быстрого поиска в файле, чтобы увидеть, есть ли во входных данных соответствующий элемент, равный element[27] , а затем взять первые 24 элемента в этом CSV и сравнить его с входными данными.

a) Даже если бы я придерживался этого подхода и использовал 16 ГБ оперативной памяти и 64-разрядную версию Windows, было бы нормально иметь такое количество элементов в словаре?

б) Не могли бы вы предоставить какой-нибудь код / ссылки на способы быстрого поиска в CSV-файле, если вы не считаете использование словаря хорошим планом

ОБНОВЛЕНИЕ: Хотя я принял ответ, мне просто интересно, что думают люди об использовании FileStream для выполнения поиска, а затем извлечения данных.

Комментарии:

1. Не ответ, просто предложение: используйте для этого базу данных, а не CSV-файл!! Если вам нужен один файл базы данных, SQLite — это то, что вам нужно.

2. сохранение данных объемом 1 ГБ в памяти определенно не самый разумный выбор. Вероятно, вы не хотите, чтобы программа занимала всю вашу свободную оперативную память. вместо этого используйте СУБД.

3. @Marco Вы хотите сказать, что созданный файл базы данных — это один файл? Если это так, я предполагаю, что могу импортировать CSV-файл в БД, создав новый файл БД и удалив все старые?

4. Вы могли бы использовать это средство чтения CSV: codeproject.com/KB/database/CsvReader.aspx

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

Ответ №1:

Если вы планируете выполнять поиск в таком количестве записей, я бы предложил массово вставлять файл в СУБД, такую как SQL Server, с соответствующими индексами для полей, которые будут вашими критериями, а затем использовать SQL-запрос для проверки существования записи.

Комментарии:

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

2. Затем попробуйте использовать простую СУБД, такую как MS Access. Даже это может очень помочь.

3. Но система должна сама загрузить его в DMBS … в качестве первой части чтения файла. Проблема, вероятно, заключалась бы в создании структуры, которая была бы оптимальной. Если бы вы могли использовать какую-нибудь небольшую базу данных lightwieght, такую как SQLite, это, вероятно, было бы проще всего

4. @Jon: Не могли бы вы создать для них веб-приложение? Кто отвечает за данные?

5. Я с этим не работал, но вам, вероятно, также могло бы сойти с рук использование SQL Server CE, и учитывая, с чем вы работаете. Net, это могло бы лучше вписаться в вашу упаковку и т.д.

Ответ №2:

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

Комментарии:

1. Упс, только что понял, что я продублировал ответ Иоанниса… Тогда, должно быть, это хорошая идея.

Ответ №3:

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

a) Вы могли бы загрузить данные в реляционную базу данных, хотя это может быть излишним для такого типа данных.

б) Вы могли бы использовать решение NoSQL, такое как RavenDB. Я думаю, это может быть хорошим вариантом для вас.

c) Вы могли бы использовать более эффективный вариант физического хранилища, такой как Lucene

d) Вы могли бы использовать более эффективный вариант кэширования в памяти, такой как Redis.

Ответ №4:

Решением может быть разбиение файла на несколько файлов меньшего размера и параллельный поиск в каждом файле. Порядок поиска будет меньше или равен n (чтение всего файла)

Комментарии:

1. Не могли бы вы продемонстрировать пример?

2. Хорошо, если у вас есть файл с данными объемом 1 ГБ, разделите его на 10 файлов по 100 мг, а затем при запуске пользовательских запросов создайте поток 10, который выполняет команду поиска в каждом файле с максимальной точностью, вы можете остановить весь поиск и остановить его

3. Ага, значение, которое вы ищете, уникально или нет? и как вы создаете файл?

4. Значение должно быть уникальным. Я не создаю CSV-файл, который делает клиент

Ответ №5:

Поскольку остальная часть вашей программы использует записи StringDictionary, вам все равно в идеале необходимо сохранять ваши результаты в памяти — на самом деле вы не хотите запрашивать базу данных 1000 раз. (Это может зависеть от того, находится ли ваша программа на сервере БД)!

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

Комментарии:

1. Не совсем понимаю ваш ответ, но если я нахожу соответствие между файлом / словарем и входными данными, я пишу уникальный идентификатор, передаю / завершаю строку в новый файл

2. вы указали «Словарь используется в другом месте программы», поэтому я предполагаю, что именно поэтому вы хотите сохранить его в памяти? Если к этому промежуточному словарю часто обращаются, вы бы не хотели, чтобы эти обращения осуществлялись по потенциально медленной ссылке на базу данных.

Ответ №6:

  • забудьте о MS access. Действительно.
  • попробуйте sqlite, его будет более чем достаточно для нескольких миллионов строк
  • если вы не можете проиндексировать свои данные, то не используйте базу данных, используйте внешнюю утилиту, такую как egrep, с соответствующим регулярным выражением для поиска по определенным полям. Это будет намного быстрее.