如何通过并行化数据库访问来提高吞吐量?

时间:2016-03-23 12:21:42

标签: c# multithreading async-await

我正在使用以下方法从MongoDB收集数据:

public IEnumerable<TOutput> GetMatchingJobs<TOutput>(JobInfoFilterParameters filterParameters, FetchOptions<JobInfoRecord, TOutput> fetchOptions)
{
    var filter = CreateFilterDefinition(filterParameters);
    var options = CreateFindOptions(fetchOptions, false);

    return MongoDatabaseStorageService.WithRecords<JobInfoRecord, List<TOutput>>
    (collection => collection.FindAsync(filter, options).Result.ToListAsync().Result);
}

/// <summary>
/// Performs an operation on the database, automatically disconnecting and retrying
/// if the database connection drops.
/// </summary>
/// <param name="operation"></param>
public TResult WithRecords<T, TResult>(Func<IMongoCollection<T>, TResult> operation)
{
    try { }
    finally { _lock.EnterUpgradeableReadLock(); }
    try
    {
        return WithRecordsInternal(operation);
    }
    finally
    {
        _lock.ExitUpgradeableReadLock();
    }
}

private TResult WithRecordsInternal<T, TResult>(Func<IMongoCollection<T>, TResult> operation)
{
    try
    {
        return operation(GetCollection<T>());
    }
    catch (IOException)
    {
        // There is an issue in mongo drivers when IOException is thrown instead of reconnection attempt.
        // Try repeat operation, it will force mongo to try to reconnect.
        return operation(GetCollection<T>());
    }
}

我想知道使用FindAsync()ToListAsync()等异步操作与.Result

如何使用async-await提升性能(或通过并行化数据库访问来提高吞吐量)或正确使用async正确的模式(如果我打破了它)?

2 个答案:

答案 0 :(得分:3)

使用异步IO无法提高数据库访问吞吐量。所有这一切都改变了呼叫启动和完成的方式。完全相同的数据通过网络传输。

您可以通过并行化数据库访问来提高吞吐量,但这与异步IO无关。

collection.FindAsync(filter, options).Result.ToListAsync().Result

在这里,你得到了最糟糕的性能:由于异步导致的呼叫开销增加,然后阻塞,这又是代价高昂的。如果这是个好主意,那么图书馆就会在内部为你做这个模式。

答案 1 :(得分:0)

在某种意义上,您可以并行化数据库调用,但是您需要从上到下使用Async-Await。这是一个做一组异步调用,捕获他们的任务承诺,等待他们全部完成的例子。

var tasks = new List<Task>();
tasks.Add(AsyncCall1);
tasks.Add(AsyncCall2);

await Task.WhenAll(tasks);

当您调用Async方法并使用.Result时,您会立即使调用线程等待异步方法完成。以上只是一个简单的例子来传达你可以并行放置异步操作的观点。一般来说,Async-Await是一种病毒,它往往会在整个代码库中传播,因为最好的优势来自于从上到下一直使用它。只使用.Result的Async实际上并没有以比使用同步调用时更昂贵的方式执行相同的操作。

您要测量的另一个潜在选项是将异步调用添加到任务集合,然后调用:

Task.WhenAll(tasks).Wait();

这会导致调用线程在.Wait上阻塞,但是一旦完成调用线程将继续,集合中的任务将并行处理。我个人更喜欢一直使用Async-Await,但你可能没有这个选择。

此博客系列可能对您有所帮助。 http://blog.stephencleary.com/2012/02/async-and-await.html