c#:阅读和更新文档时出现MongoDb性能问题

时间:2018-01-17 08:35:21

标签: c# asp.net-core mongodb-query asp.net-web-api2

我已使用solution = solution.WithDocumentText(currentDocument.Id, currentDocumentSyntaxTree.GetText()); webapi创建了一个小项目。我有一个控制器和一个插入,更新和读取文档的存储库。为了测试这一点,我创建了一个.net core 2.0测试来创建,更新和读取项目,并使用20个并行线程启动它。问题是有时,读取操作或更新操作需要4秒钟。这是我的代码:

jmeter

来自startup.cs的代码:

public class TransactionRepository : ITransactionRepository
    {
        private readonly MongoClient _mongoClient;

        public TransactionRepository(string connectionString)
        {
                _mongoClient = new MongoClient(connectionString);
        }
        public async Task<TransactionResponse> RetrieveResponse(string id)
        {
            using (IAsyncCursor<TransactionRequest> cursor = await this._mongoClient
                .GetDatabase("MyDatabase")
                .GetCollection<TransactionRequest>("Transactions")
                .FindAsync(t => t.Transaction.Id.Equals(id)))
            {
                while (await cursor.MoveNextAsync())
                {
                    IEnumerable<TransactionRequest> documents = cursor.Current.ToList();
                    if (documents.Any())
                    {
                        var request = documents.First();
                        return new TransactionResponse { InternalId = request._id.ToString(), TransactionId = request.Transaction.Id};
                    }
                    Console.WriteLine(documents.Count());
                }
            }

            return null;
        }

        public async Task<TransactionResponse> SaveAsync(TransactionRequest request)
        {

            await this._mongoClient
                .GetDatabase("MyDatabase")
                .GetCollection<TransactionRequest>("Transactions")
                .InsertOneAsync(request, null, CancellationToken.None);

            return new TransactionResponse
            {
                InternalId = request._id.ToString(),
                TransactionId = request.Transaction.Id
            };
        }

        public async Task<TransactionResponse> UpdateAsync(TransactionRequest request)
        {

            var findFilter = Builders<TransactionRequest>.Filter.Eq(t => t.Transaction.Id, request.Transaction.Id);

            var updateSettings = Builders<TransactionRequest>
                .Update
                .Set(t => t.Transaction.Status, request.Transaction.Status)
                .Set(t => t.Header.BusinessId, request.Header.BusinessId);

            var findOptions = new FindOneAndUpdateOptions<TransactionRequest>
            {
                ReturnDocument = ReturnDocument.Before
            };

            var updatedRecord  = await this._mongoClient
                .GetDatabase("MyDatabase")
                .GetCollection<TransactionRequest>("Transactions")
                .FindOneAndUpdateAsync(
                    findFilter, 
                    updateSettings, 
                    findOptions);

            string internalid = updatedRecord == null ? "-1" : updatedRecord._id.ToString();
            string transactionId = updatedRecord == null ? "-1" : updatedRecord.Transaction.Id;

            return new TransactionResponse {TransactionId = transactionId, InternalId = internalid };
        }
    }

控制器代码:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddTransient<ITransactionRepository, TransactionRepository>(option => new TransactionRepository(Configuration["ConnectionString"]));
}

以下是我的模特课程:

[Route("api/requests")]
    public class RequestsController : Controller
    {
        private readonly ITransactionRepository _repository;

        public RequestsController(ITransactionRepository repository)
        {
            this._repository = repository;
        }

        [HttpPost]
        [Route("")]
        public async Task<TransactionResponse> Add([FromBody]TransactionRequest request)
        {

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            var result = await this._repository.SaveAsync(request);
            stopwatch.Stop();

            Trace.WriteLine("#Trace# inserted item: " + stopwatch.ElapsedMilliseconds);
            return result;
        }

        [HttpPost]
        [Route("update")]
        public async Task<TransactionResponse> Update([FromBody]TransactionRequest request)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            var result = await this._repository.UpdateAsync(request);
            stopwatch.Stop();

            Trace.WriteLine("#Trace# updated item: " + stopwatch.ElapsedMilliseconds);
            return result;
        }

        [HttpGet]
        [Route("{id}")]
        public async Task<TransactionResponse> Get([FromRoute]string id)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            var result = await this._repository.RetrieveResponse(id);
            stopwatch.Stop();

            Trace.WriteLine("#Trace# read item: " + stopwatch.ElapsedMilliseconds);
            return result;
        }
    }

我是否需要在数据库中创建索引?更新操作或读取操作是否正常,3-4秒?

1 个答案:

答案 0 :(得分:0)

我会尽力帮助您,找出问题: 我假设你正在尝试使用最新的MongoDB(3.6)(如果没有,请告诉我,可能你有mongo引擎MMAP的问题)。

您需要分析您尝试在数据库级别执行的查询。尝试从终端窗口执行以下命令:

mongo
use MyDatabase    
db.setProfilingLevel(2, { slowms: 20 })

执行您的查询(来自应用程序)。之后,您可以查看结果(在同一个终端窗口中):

show profile

它会打印出以下结果(我这里只放了一部分):

query   test.products 3ms Thu Jan 18 2018 07:57:36
command:{ "find" : "products", "filter" : { "brand" : "ACME" }, "$db" : "test" } keysExamined:0 docsExamined:11

如您所见,我的请求已在3毫秒内执行,检查的文件数量为11个。

我可以接受我的请求,并查看索引的详细用法:

db.products.find({brand: "ACME"}).explain()

此查询将显示查询是使用COLLSCAN(基本光标)还是index(BTree)

在这些操作之后,我们可以找出问题的位置(在代码中或在数据库级别上),以及我们如何解决它。