How to work around the lack of transactions in MongoDB?

后端 未结 10 594
梦毁少年i
梦毁少年i 2020-11-27 09:19

I know there are similar questions here but they are either telling me to switch back to regular RDBMS systems if I need transactions or use atomic operations or two-phase c

相关标签:
10条回答
  • 2020-11-27 10:01

    This is probably the best blog I found regarding implementing transaction like feature for mongodb .!

    Syncing Flag: best for just copying data over from a master document

    Job Queue: very general purpose, solves 95% of cases. Most systems need to have at least one job queue around anyway!

    Two Phase Commit: this technique ensure that each entity always has all information needed to get to a consistent state

    Log Reconciliation: the most robust technique, ideal for financial systems

    Versioning: provides isolation and supports complex structures

    Read this for more info: https://dzone.com/articles/how-implement-robust-and

    0 讨论(0)
  • 2020-11-27 10:03

    Bring it to the point: if transactional integrity is a must then don't use MongoDB but use only components in the system supporting transactions. It is extremely hard to build something on top of component in order to provide ACID-similar functionality for non-ACID compliant components. Depending on the individual usecases it may make sense to separate actions into transactional and non-transactional actions in some way...

    0 讨论(0)
  • 2020-11-27 10:08

    Living Without Transactions

    Transactions support ACID properties but although there are no transactions in MongoDB, we do have atomic operations. Well, atomic operations means that when you work on a single document that that work will be completed before anyone else sees the document. They'll see all the changes we made or none of them. And using atomic operations, you can often accomplish the same thing we would have accomplished using transactions in a relational database. And the reason is that, in a relational database, we need to make changes across multiple tables. Usually tables that need to be joined and so we want to do that all at once. And to do it, since there are multiple tables, we'll have to begin a transaction and do all those updates and then end the transaction. But with MongoDB, we're going to embed the data, since we're going to pre-join it in documents and they're these rich documents that have hierarchy. We can often accomplish the same thing. For instance, in the blog example, if we wanted to make sure that we updated a blog post atomically, we can do that because we can update the entire blog post at once. Where as if it were a bunch of relational tables, we'd probably have to open a transaction so that we can update the post collection and comments collection.

    So what are our approaches that we can take in MongoDB to overcome a lack of transactions?

    • restructure - restructure the code, so that we're working within a single document and taking advantage of the atomic operations that we offer within that document. And if we do that, then usually we're all set.
    • implement in software - we can implement locking in software, by creating a critical section. We can build a test, test and set using find and modify. We can build semaphores, if needed. And in a way, that is the way the larger world works anyway. If we think about it, if one bank need to transfer money to another bank, they're not living in the same relational system. And they each have their own relational databases often. And they've to be able to coordinate that operation even though we cannot begin transaction and end transaction across those database systems, only within one system within one bank. So there's certainly ways in software to get around the problem.
    • tolerate - the final approach, which often works in modern web apps and other applications that take in a tremendous amount of data is to just tolerate a bit of inconsistency. An example would, if we're talking about a friend feed in Facebook, it doesn't matter if everybody sees your wall update simultaneously. If okey, if one person's a few beats behind for a few seconds and they catch up. It often isn't critical in a lot of system designs that everything be kept perfectly consistent and that everyone have a perfectly consistent and the same view of the database. So we could simply tolerate a little bit of inconsistency that's somewhat temporary.

    Update, findAndModify, $addToSet (within an update) & $push (within an update) operations operate atomically within a single document.

    0 讨论(0)
  • Transactions are available now in MongoDB 4.0. Sample here

    // Runs the txnFunc and retries if TransientTransactionError encountered
    
    function runTransactionWithRetry(txnFunc, session) {
        while (true) {
            try {
                txnFunc(session);  // performs transaction
                break;
            } catch (error) {
                // If transient error, retry the whole transaction
                if ( error.hasOwnProperty("errorLabels") && error.errorLabels.includes("TransientTransactionError")  ) {
                    print("TransientTransactionError, retrying transaction ...");
                    continue;
                } else {
                    throw error;
                }
            }
        }
    }
    
    // Retries commit if UnknownTransactionCommitResult encountered
    
    function commitWithRetry(session) {
        while (true) {
            try {
                session.commitTransaction(); // Uses write concern set at transaction start.
                print("Transaction committed.");
                break;
            } catch (error) {
                // Can retry commit
                if (error.hasOwnProperty("errorLabels") && error.errorLabels.includes("UnknownTransactionCommitResult") ) {
                    print("UnknownTransactionCommitResult, retrying commit operation ...");
                    continue;
                } else {
                    print("Error during commit ...");
                    throw error;
                }
           }
        }
    }
    
    // Updates two collections in a transactions
    
    function updateEmployeeInfo(session) {
        employeesCollection = session.getDatabase("hr").employees;
        eventsCollection = session.getDatabase("reporting").events;
    
        session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } );
    
        try{
            employeesCollection.updateOne( { employee: 3 }, { $set: { status: "Inactive" } } );
            eventsCollection.insertOne( { employee: 3, status: { new: "Inactive", old: "Active" } } );
        } catch (error) {
            print("Caught exception during transaction, aborting.");
            session.abortTransaction();
            throw error;
        }
    
        commitWithRetry(session);
    }
    
    // Start a session.
    session = db.getMongo().startSession( { mode: "primary" } );
    
    try{
       runTransactionWithRetry(updateEmployeeInfo, session);
    } catch (error) {
       // Do something with error
    } finally {
       session.endSession();
    }
    
    0 讨论(0)
提交回复
热议问题