Я работаю над некоторыми корпоративными приложениями, которые каждый день обрабатывают большие объемы данных, и для этого у него есть приложение WINDOWS SERVICE, написанное на С# .NET 4. Оно также имеет подключение к SQL SERVER 2008 R2, но по какой-то причине оно (случайно) бросает меня эта ошибка в таблице синхронизации, в которой хранятся сериализованные данные JSON:
Exception of type 'System.OutOfMemoryException' was thrown.
at System.Data.SqlClient.TdsParser.ReadPlpUnicodeChars(Char[]& buff, Int32 offst, Int32 len, TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.ReadSqlStringValue(SqlBuffer value, Byte type, Int32 length, Encoding encoding, Boolean isPlp, TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.ReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, Int32 length, TdsParserStateObject stateObj)
at System.Data.SqlClient.SqlDataReader.ReadColumnData()
at System.Data.SqlClient.SqlDataReader.ReadColumn(Int32 i, Boolean setTimeout)
at System.Data.SqlClient.SqlDataReader.GetValueInternal(Int32 i)
at System.Data.SqlClient.SqlDataReader.GetValues(Object[] values)
Эта таблица является довольно общей таблицей для хранения данных LOB:
CREATE TABLE [dbo].[SyncJobItem](
[id_job_item] [int] IDENTITY(1,1) NOT NULL,
[id_job] [int] NOT NULL,
[id_job_item_type] [int] NOT NULL,
[id_job_status] [int] NOT NULL,
[id_c] [int] NULL,
[id_s] [int] NULL,
[job_data] [nvarchar](max) NOT NULL,
[last_update] [datetime] NOT NULL,
CONSTRAINT [PK_SyncJobItem] PRIMARY KEY CLUSTERED)
Неудачная запись LOB имеет 36 231 800 символов данных в столбце job_data
, что составляет (если мы говорим, что 1 символ равен 2 байтам, UTF-8) около 70 МБ данных, что немного.
Учтите, что изменение хранилища данных для задания (например, диска) или что-то подобное для меня не вариант. Я хотел бы исправить эту ошибку, поэтому, если кто-нибудь что-нибудь знает, пожалуйста, помогите!
Также эта ошибка возникает случайным образом с одними и теми же данными, работает система vmWare-vCloud, то есть, я думаю, какая-то большая блейд-система. У нас есть около 6 ГБ оперативной памяти, выделенной для нашей виртуальной машины (служба в большинстве случаев использует около 1-2 ГБ), служба скомпилирована как x64, а система — x64 Windows 2008R2 Standard. Я убедился, что ни один объект не имеет более 2 ГБ в памяти, так что это не так, также ошибка внутри SqlClient, и за мой 15-летний опыт разработки я никогда этого не видел, и Google ничего не выдает. Также ошибка не на стороне БД, поскольку БД имеет более 32 ГБ ОЗУ и использует только 20 ГБ в пике. Для особенностей, которые я использую в этой системе, которые не являются обычными, это многопоточность и GC.Collect() после каждого шага задания (есть несколько шагов для данных).
РЕДАКТИРОВАТЬ:
Вот полный код, который решает эту проблему:
internal static void ExecuteReader(IConnectionProvider conn, IList destination, IObjectFiller objectBuilder, string cmdText, DbParameterCollection parameters, CommandType cmdType, int cmdTimeout)
{
IDbCommand cmd = CreateCommand(conn.DBMS, cmdText, parameters, cmdType, cmdTimeout);
cmd.Connection = conn.Connection;
bool connIsOpennedLocally = EnsureOpenConnection(conn);
try
{
AssignExistingPendingTransactionToCommand(conn, cmd);
using (IDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleResult))
{
objectBuilder.FillCollection(reader, destination);
PopulateOutputParameterValues(parameters, cmd);
}
}
finally
{
CloseConnectionIfLocal(conn, connIsOpennedLocally);
cmd.Dispose();
}
}
...
private void FillFromAlignedReader(ICollection<TEntity> collection, IDataReader openedDataReader, IDbTable table)
{
// Fastest scenario: data reader fields match entity field completely.
// It's safe to reuse same array because GetValues() always overwrites all members. Memory is allocated only once.
object[] values = new object[openedDataReader.FieldCount];
while (openedDataReader.Read())
{
openedDataReader.GetValues(values);
TEntity entity = CreateEntity(table, EntityState.Synchronized, values);
collection.Add(entity);
}
}