#c# #memory #datatable #temporary-files
#c# #память #datatable #временные файлы
Вопрос:
Я хотел бы заменить DataTable пользовательским классом, который реализует DataRowCollection, сохраняя строки во временном файле данных вместо того, чтобы хранить их в памяти.
Я понимаю, что это будет медленным по сравнению с таблицами в памяти, но мне иногда приходится работать с таблицами, которые просто не помещаются в оперативную память (> 4 ГБ данных). Я удалю таблицу и временный файл в конце выполнения.
Данные таблицы поступают из запроса к базе данных. Я знаю, что могу изменять запросы, чтобы уменьшить размер возвращаемого набора данных. Дело не в этом. Дело в том, что всегда будет какое-то ограничение на объем памяти, и я хотел бы иметь возможность использовать медленный временный файл, а не просто говорить «вы не можете этого сделать».
Существует ли предварительно написанный класс или метод для этого? Похоже, я изобретаю велосипед здесь…
Вот мой скелетный старт:
/// <summary>
/// like DataTable, but storing data in a file instead of memory
/// </summary>
public class FileBackedDataTable : DataTable, IIntegrationTest
{
new public FileBackedDataRowCollection Rows = null;
// Summary:
// Initializes a new instance of the System.Data.DataTable class with no arguments.
public FileBackedDataTable()
{
Rows = new FileBackedDataRowCollection(this);
}
}
/// <summary>
/// like a DataRowCollection but data is stored in a file, not in memory
/// </summary>
public class FileBackedDataRowCollection : ICollection, IEnumerable, IDisposable
{
/// <summary>
/// internally track each file record
/// </summary>
class recordInfo
{
public long recordPosition;
public int recordLength;
public int recordMaxLength;
public long hash;
}
DataTable table;
ArrayList rows = new ArrayList();
public FileBackedDataRowCollection(DataTable table)
{
this.table = table;
openBackingFile(table);
}
public int Count
{
get { return rows.Count; }
}
public void Clear()
{
rows.Clear();
truncateBackingFile();
}
public DataRow this[int index]
{
get
{
recordInfo info = (recordInfo)rows[index];
return readRow(info);
}
set
{
writeRow(index, value);
}
}
private void writeRow(int index, DataRow value)
{
byte[] bytes = rowToBytes(value);
recordInfo info = (recordInfo)rows[index];
if (bytes.Length <= info.recordMaxLength)
{
info.recordLength = bytes.Length;
info.hash = value.GetHashCode();
writeBytes(info.recordPosition, bytes);
}
else
{
rows[index] = appendRow(bytes, value.GetHashCode());
}
}
private DataRow readRow(recordInfo recordInfo)
{
byte[] bytes = readBytes(recordInfo.recordPosition, recordInfo.recordLength);
DataRow row = bytesToRow(bytes);
return row;
}
public void Add(DataRow r)
{
byte[] bytes = rowToBytes(r);
recordInfo info = appendRow(bytes, r.GetHashCode());
rows.Add(info);
}
private recordInfo appendRow(byte[] bytes, long hash)
{
recordInfo info = new recordInfo();
info.recordLength = bytes.Length;
info.recordMaxLength = info.recordLength;
info.recordPosition = appendBytes(bytes);
info.hash = hash;
return info;
}
Комментарии:
1. Я понятия не имею, что вы делаете, но я действительно думаю,
PAGING
что это решение вашей проблемы2. Вы делаете что-то с DataTable, что вы можете сделать в базе данных? Также возможно запрашивать у базы данных определенный диапазон строк, поэтому вам не нужно хранить всю таблицу в памяти.
3. Вам нужно обрабатывать строки по одной за раз, или они влияют друг на друга? Если вы можете обрабатывать каждый из них отдельно, используйте DataReader для получения каждой строки по очереди — очень быстро, занимая очень мало памяти.
4. По-моему, это плохая идея. Я почти уверен, что обработку можно выполнить на стороне СУБД … уверен, что вы не собираетесь показывать пользователю таблицу объемом более 4 ГБ. 🙂 Отложив это в сторону, рассматриваете ли вы возможность использования файлов с отображением в память? Это может немного ускорить работу вашего кэшированного datatable…
5. Я изо всех сил пытаюсь увидеть выгоду в том, чтобы делать это таким образом. Я понимаю, что временами трудно найти оперативную память, особенно когда у вас есть очень большие таблицы для запроса. Я что-то упускаю?
Ответ №1:
Недавно я просматривал System.Data.SQLite для сохранения некоторых данных приложения вместо того, чтобы писать их самостоятельно.
Как насчет создания временного файла с помощью SQLite и загрузки туда ваших устаревших данных? Затем вы можете использовать его как локальный файл и удалять после обработки.
Комментарии:
1. Вот ссылка на видео от парня, который написал оригинальный SQLite. youtube.com/watch?v=giAMt8Tj-84
Ответ №2:
Почти на 100% ваш план — плохой дизайн. Потратьте некоторое время на редизайн, используйте свою другую базу данных вместо ФАЙЛА, они были своего рода созданы для манипулирования большими кусками данных. ПРИ необходимости вы можете написать хранимые процедуры на C # или другом языке, если ваша база данных позволяет это.
опишите способ, которым вы хотите управлять своими данными, и вы получите реальный ответ на вашу реальную проблему. Для этого либо потребуется SQL-запрос, либо, если это не может быть выполнено в SQL, это можно сделать в каком-то цикле, почти наверняка работающем с меньшим размером данных.
Комментарии:
1. Принято, потому что правильный ответ на мой вопрос действительно «это невозможно сделать». На протяжении многих лет я часто сожалел, что ответил «да» на ошибочный запрос функции, и я думаю, что этот присоединится к этой группе.
2. отлично, я думаю, что если вы воспользуетесь этим подходом и вообще откажетесь от своей идеи, ваше решение не станет «устаревшим» так скоро, как мог бы первоначальный план!
Ответ №3:
Вы можете использовать DataTable.WriteXml. Но я поддержу других людей, в первую очередь лучше ограничить количество записей, которые вы получаете из базы данных.
Комментарии:
1. Спасибо, но это не поможет, потому что предполагается, что DataTable уже помещается в оперативную память.
2. Правильно… ну, вы можете загрузить его частично и сохранить затем. Плюс на жестком диске есть виртуальная память