Оптимизировать производительность считывателя данных 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!

Другие вопросы по тегам