我需要能够获得一个独特的增量计数器。我在想这个收藏是这样的:
{
"CounterName": "Test",
"CounterValue": 1001
}
我将通过获取“ Test”并增加“ CounterValue”来使用它。在RDBMS中,我将锁定行,增加CounterValue并释放。 我将如何在MongoDb中完成相同的工作?
我知道如何阅读文档并发布更新,但是我不知道如何进行锁定。
更新
如果我按如下方式使用FindOneAndUpdate,是否可以访问已找到的CounterValue?如果我能够执行x.CounterValue ++,那就太好了。
collection.FindOneAndUpdate(x => x.CounterName == "Test", Builders<InternalCounter>.Update.Set(s => s.CounterValue, XXXXXXX)
更新2
我找到了解决方案。我使用了提供的Inc. Update方法,该方法将值增加传入的值。在这种情况下,增加1。
var filter = Builders<InternalCounter>.Filter.Eq("CounterName", "Test");
var options = new FindOneAndUpdateOptions<InternalCounter> { IsUpsert = true };
InternalCounter ctn = collection.FindOneAndUpdate(filter,
Builders<InternalCounter>.Update.Inc(s => s.CounterValue, 1), options);
更新3
好像我很快就在说... 我认为我应该对此进行压力测试,以查看它是否确实可以锁定。我创建了一个简单的方法,该方法被5个不同的线程调用了20,000次。果然,我得到重复了。
static void Main(string[] args)
{
Console.WriteLine("Press Enter to begin...");
Console.ReadLine();
var t1 = Task.Run(() => TestWrites());
var t2 = Task.Run(() => TestWrites());
var t3 = Task.Run(() => TestWrites());
var t4 = Task.Run(() => TestWrites());
var t5 = Task.Run(() => TestWrites());
Console.WriteLine("Waiting for tasks to complete...");
Task.WaitAll(t1, t2, t3, t4, t5);
Console.WriteLine("Done. Press Enter to exit.");
Console.ReadLine();
}
private static void TestWrites()
{
var tester = new ConcurrentTester();
int writes = 20000;
for (int i = 0; i < writes; i++)
tester.InsertCounter(tester.GetCounter());
Console.WriteLine($"Wrote {writes}");
}
public int GetCounter()
{
var collection = database.GetCollection<InternalCounter>("Concurrency");
var filter = Builders<InternalCounter>.Filter.Eq("CounterName", "Test");
var mkt = collection.Find(filter).FirstOrDefault();
var options = new FindOneAndUpdateOptions<InternalCounter> { IsUpsert = true, ReturnDocument = ReturnDocument.After };
InternalCounter ctn = collection.FindOneAndUpdate(filter,
Builders<InternalCounter>.Update.Inc(s => s.CounterValue, 1), options);
return ctn.CounterValue;
}