【问题标题】:c#: MongoDb performance issues when reading and updating documentsc#:读取和更新文档时出现MongoDb性能问题
【发布时间】:2018-01-17 08:35:20
【问题描述】:

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

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 };
        }
    }

startup.cs 中的代码:

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;
        }
    }

这是我的模型类:

public class TransactionRequest
    {
        public ObjectId _id { get; set; }

        public Header Header { get; set; }

        public Transaction Transaction { get; set; }
    }

    public class Header
    {
        public string BusinessId { get; set; }
    }

    public class Transaction
    {
        public string Id { get; set; }
        public string Status { get; set; }
    }

public class TransactionResponse
    {
        public string InternalId { get; set; }
        public string TransactionId { get; set; }
    }

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

【问题讨论】:

  • 您是否分析过您的应用程序以验证 MongoDB 确实是瓶颈?
  • 正如您在控制器代码中看到的那样,我正在使用秒表并且在那里我看到了这个
  • 正如 Yuriy 和 Leonid 所提到的,您需要分析您的查询以找到瓶颈。

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


【解决方案1】:

我会尽力帮助您找出问题所在: 我有一个假设,您正在尝试使用最新的 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)

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

【讨论】:

  • 所以在我启动 jmeter 文件后,我在 show profile 命令中看到了结果:nreturned:1 responseLength:259 protocol:op_query planSummary:IXSCAN { Transaction._id: 1, Transaction.Status: -1,事务:-1,标题:-1,Header.BusinessId:-1 }
  • 这对你来说够了吗,还是你需要所有的回应?
  • 我看到在您的查询中检查的文档数量为 1。我没有看到 Db 级别的性能下降。你的方法有什么变化吗?(我的意思是持续时间)。
  • 问题是我正在启动一套 jmeter 测试,每个测试都包含一个插入、一个更新和一个读取,我从 20 个线程并行执行此操作,并且测试永远循环.这(“高”输入)可能是个问题吗?
  • 这个问题主要与方式有关,我们如何从 Mongo 中插入、删除和查询数据。我想在本地测试它并通知你结果。
猜你喜欢
  • 1970-01-01
  • 2017-01-05
  • 1970-01-01
  • 1970-01-01
  • 2015-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多