MongoDb c#驱动程序:如何在一个原子操作中更新多个文档

3
我想问一下以下代码是否会在一个原子操作中执行。我正在使用mongodb c#驱动程序。 该方法的输入是我想要更新的对象id列表。
public void Update(IEnumerable<string> ids)
{
    var query = Query<T>.Where(t => ids.Contains(t.Id));
    var update = Update.Set("Modified", DateTime.Now); //this is just example of update

    var options = new MongoUpdateOptions {Flags = UpdateFlags.Multi};

    Collection.Update(query, update, options);
}

我对一个情况很感兴趣,即当我有数百万个文档需要更新时。如果在此更新期间出现故障(电力或硬件问题),会发生什么?数据库是否处于一致状态?

谢谢。


可能是MongoDB事务的重复。 - Mathias
2个回答

1

需求

MongoDB >= 4.0 C# Driver >= 2.7 以下是我的操作步骤...

简短版本

前往 "示例代码"


会话

在我的DbContext类中,我可以访问我的客户端(IMongoClient),我定义了会话:

public IClientSessionHandle StartSession()
{
    return _client.StartSession(new ClientSessionOptions());
}

public async Task<IClientSessionHandle> StartSessionAsync()
{
    return await _client.StartSessionAsync(new ClientSessionOptions());
}

根据文档,如果这些事务是一个接一个地到达的,它们可以执行多个事务。
会话用于将一系列操作分组在一起,这些操作彼此相关并应使用相同的会话选项执行。会话也用于事务。
这些会话应该在您结束操作后立即关闭。因此,您应该像以下方式编写代码,或根据您的情况手动处理清理:
// db is what i named my context, where i defined all my collections and database related stuffs. 
// if you have direct access to client, you can call `StartSession/Async` exactly over the `client` object
using(var session = _db.StartSessionAsync()) {
    //... All transactional code will go here
} 
// Here, on ending the block, Will automatically call `Dispose` method, and the object will no longer exists

关于 session 对象

事务

使用 IClientSession 的方法启动、提交或中止事务。一个会话一次只能执行一个事务,但是只要每个事务在下一个事务开始之前被提交或中止,一个会话可以执行多个事务。

在这一步中,您需要在实际对数据库进行更改之前启动事务...

session.StartTransaction();

一旦会话开始,您应该执行您的操作,并在最后...。
如果进程成功,您应该调用:
session.CommitTransaction();

否则,您需要回滚。
session.AbortTransaction();

示例代码

正如您所看到的,我在MongoDB上有两个写操作,还有另一个对我来说很关键的外部进程,我需要这三个操作一起执行。前两个由事务管理,只要第三个不抛出异常,数据库应该保持其新状态。

bool error;
using (var session = await _db.StartSessionAsync())
{
    session.StartTransaction();

    try
    {
        var deletedImage = _db.GetUserStore<ApplicationUser>().CollectionInstance.UpdateOneAsync(
            Builders<ApplicationUser>.Filter.Where(w => w.Id == userId),
            Builders<ApplicationUser>.Update.Pull(x => x.ProfilePictures, photoFromRepo));

        await _db.ProfilePicture().DeleteAsync(new ObjectId(photoFromRepo.ImageId));

        if (photoFromRepo.CloudinaryPublicId != null)
        {
            var deleteParams = new DeletionParams(photoFromRepo.CloudinaryPublicId);
            var result = _cloudinary.Destroy(deleteParams);
            if (result.Result == "ok")
            {
                // ignore
            }
            else
            {
                throw new Exception("Cannot delete file from cloud service...");
            }
        }

        await session.CommitTransactionAsync();
        error = false;
    }
    catch (Exception ex)
    {
        await session.AbortTransactionAsync();
        error = true;
    }
}

这个能用吗?它支持多个集合吗?只有上帝知道,我根据文档和今天回家路上看到的一些示例编写了这个,并且我认为可能是正确和可行的...

额外说明:

有一些选项可以传递会话,其中一个选项控制读/写关注点,另一个选项控制在执行事务之前应提前多少数据(什么意思?我自己没有理解,如果你理解了,请编辑我的帖子)。

public class ClientSessionOptions
{
    public bool? CausalConsistency { get; set; }
    public TransactionOptions DefaultTransactionOptions { get; set; }
}

public class TransactionOptions
{
    public ReadConcern ReadConcern { get; };
    public ReadPreference ReadPreference { get; };
    public WriteConcern WriteConcern { get; };

    public TransactionOptions(
        Optional<ReadConcern> readConcern = default(Optional<ReadConcern>),
        Optional<ReadPreference> readPreference = default(Optional<ReadPreference>),
        Optional<WriteConcern> writeConcern = default(Optional<WriteConcern>));

    public TransactionOptions With(
        Optional<ReadConcern> readConcern = default(Optional<ReadConcern>),
        Optional<ReadPreference> readPreference = default(Optional<ReadPreference>),
        Optional<WriteConcern> writeConcern = default(Optional<WriteConcern>))
}

0

MongoDB不支持事务原子多文档操作。 MongoDB仅对单个文档执行原子操作。

您可以在Mongodb文档中检查此内容。

因此,如果您使用查询更新了1000个文档,并且在此操作期间服务器崩溃,则某些文档可能已更新,而其他文档则不会。


1
从4.0版本开始,MongoDB提供了针对副本集执行多文档事务的功能。请参阅文档 - Mathias
1
从4.0版本开始,MongoDB提供了针对副本集执行多文档事务的功能。我们该如何使用它? - Hassan Faghihi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接