Оптимизировать производительность считывателя данных ODBC
Поэтому для проекта мне нужно прочитать данные из драйвера Acomba odbc. Acomba - это старая бухгалтерская программа. За кулисами Акомба хранит свои данные в гекса в плоских файлах. Они предоставляют драйвер ODBC, который работает прилично, но это очень медленно.
Для конкретного клиента у них 17000 товаров. Ничего особенного. Но получение 17000 продуктов из драйвера odbc занимает более 2м30. Некоторые клиенты имеют около 1 миллиона продуктов, поэтому производительность становится огромной проблемой.
В основном этот код находится в вебапи. Он получает данные, создает csv и возвращает файл csv в httpResponse. Генерация CSV занимает более или менее 1 секунды. Так что это не проблема.
Я попробовал несколько вещей:
using (var db = new OdbcConnection($"DSN={_settings.DsnName}"))
{
await db.OpenAsync();
OdbcCommand comm = new OdbcCommand(sql, db);
OdbcDataReader dr = comm.ExecuteReader();
while (dr.Read())
{
var col1 = dr.GetValue(0).ToString();
var col2 = dr.GetValue(1).ToString();
var col3 = dr.GetValue(2).ToString();
}
}
То же самое, но с
dr.GetValues(destinationArray)
То же самое, но с адаптером odbc
var dt = new DataTable();
using (var cmd = new OdbcCommand(sql, db))
using (var adapter = new OdbcDataAdapter(cmd))
{
adapter.Fill(dt);
}
Все они в конечном итоге занимают от 2 минут до 2 минут 20 минут.
Проблема с этой конкретной таблицей состоит в том, что есть 150 столбцов. Обработка всех этих 150 столбцов - это то, что занимает все время.
Попытка найти способ оптимизировать это, но в конце концов, весь код, который я нашел, в основном то же самое, что я написал.
Вот поворот!
Если я открою соединение odbc в Excel или Access, они оба смогут построить таблицу и отобразить ее менее чем за 5 секунд. Я прокрутил весь путь вниз, так что данные фактически загружены. Это не только показ первых 50 строк.
Кто-нибудь знает, как получить такую же производительность в C#?
Спасибо за ваше время!
1 ответ
Я нашел способ сделать соединение ODBC намного быстрее. По сути, этот старый драйвер odbc является COM. Оказывается, что COM-объекты работают намного быстрее, когда вы находитесь в однопоточной квартире против многопоточной. Поэтому я использовал решение, опубликованное в этой статье: http://ryanhaugh.com/archive/2014/05/24/supporting-sta-threads-in-web-api/
public static class TaskFactoryExtensions
{
private static readonly TaskScheduler _staScheduler = new StaTaskScheduler(numberOfThreads: 1);
public static Task<TResult> StartNewSta<TResult>(this TaskFactory factory, Func<TResult> action)
{
return factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, _staScheduler);
}
}
И я мой вызов API, я делаю это:
Task<DataTable> responseTask = Task.Factory.StartNewSta(() => GetData(sqlQuery));
Фактические данные получения, которые идентичны старым:
private DataTable GetData(string value)
{
var dt = new DataTable();
using (var db = new OdbcConnection($"DSN={_settings.DsnName}"))
{
db.Open();
OdbcCommand comm = new OdbcCommand(value, db);
using (OdbcDataAdapter da = new OdbcDataAdapter(comm))
{
da.Fill(dt);
}
}
return dt;
}
Я использую метод расширения на все звонки, чтобы получить данные с помощью StartNewSta. С точно таким же кодом, оно прошло от 2 минут 30 до 9 секунд.
Так что ключ к быстрым COM-объектам - это потоки STA!