【Mongodb】トランザクション

18646 ワード


概要
  • Mongodb 4.0レプリカセットのマルチドキュメントトランザクション
  • をサポート
  • Mongodb 4.2は、スライスクラスタのマルチドキュメントトランザクション
  • をサポートします.
    単一のサーバではトランザクションの使用はサポートされていないため、トランザクションを学習するには、レプリカセット/スライスクラスタを構築する必要があります.
    また、単一のドキュメント操作は原子操作であり、mongodbはドキュメント型データベースであり、単一のドキュメントには、複数のセットを使用してデータ間の関係を維持するのではなく、オブジェクト/配列というフォーマットを埋め込むことができます.mongodbのこのような特性のため、単一のドキュメント操作はトランザクションを必要とする多くのニーズを排除します.
     
    レプリカセットの作成
    次に、最も簡単な方法でレプリカセットを構築します.
    1.複数のmongodインスタンスを起動します.ここではcmdコマンドを使用して起動します.
    start "Mongodb Service - 27017" /min mongod --port 27017 --replSet "rs0" --dbpath "F:\Database\Mongodb\Data27017" --logpath "F:\Database\Mongodb\Log\mongod.27017.log"
    start "Mongodb Service - 27018" /min mongod --port 27018 --replSet "rs0" --dbpath "F:\Database\Mongodb\Data27018" --logpath "F:\Database\Mongodb\Log\mongod.27018.log"

    パラメータの説明
  • replset:レプリカセット名
  • を設定する
  • port:ポートを設定します.私はシングルマシンなので、異なるポート
  • しか設定できません.
  • dbpath:データファイルパス、注意:フォルダは存在する必要があります.mongodは
  • を自動的に作成しません.
  • logpath:ログファイル名、これは事前に新規作成する必要はありません.mongodが存在しない場合は
  • が自動的に作成されます.
    2.任意のインスタンスを接続し、ここでは27017というデフォルトポートを選択する
    mongo

     
    3.レプリカセットの起動
    rs.initiate({
        _id: "rs0",
        members: [
            { _id: 0, host: "127.0.0.1:27017" },
            { _id: 1, host: "127.0.0.1:27018" }
        ]
    })

    パラメータの説明
  • _id:レプリカセット名、すなわちインスタンスの起動時にその名前
  • を指定する
  • members:これがすべてのメンバーです.id各メンバーの識別、整数型[0255]
  • フィールド「OK」を返します.1は作成に成功したことを表します.
    rs.initiate({})は、デフォルトの構成を使用して起動する必要があるいくつかを除いて、replica-configurationを参照してください.
     
    4.現在の構成情報の表示
    rs.conf()

     
    5.レプリカセット情報の表示
    rs.status()

     
    これでコピーセットが完成します
     
    取引
    1.レプリカセットの接続
    mongo mongodb://127.0.0.1:27017,127.0.0.1:27018/?replicaSet=rs0

    プライマリ・レプリカのインスタンスを直接接続したり、url形式でプライマリ・レプリカを自動的に接続したりできます(後者を推奨します).
     
    2.データを2つ用意する
    db.balance.insert({ name: "Wilson", balance: 100 }, { writeConcern: { w: "majority", wtimeout: 2000 } });
    db.record.insert({ name: "Wilson", change: 100, balance: 100, }, { writeConcern: { w: "majority", wtimeout: 2000 } });

     
    正常なコミットのテスト
    お金を引く動作をシミュレートします.その中で、控除と流水は事務の中にあります.
    session = db.getMongo().startSession({ readPreference: { mode: "primary" } });
    balanceCol = session.getDatabase("mongo").balance;
    recordCol = session.getDatabase("mongo").record;
    session.startTransaction({ readConcern: { level: "local" }, writeConcern: { w: "majority" } });
    
    try {
        balanceCol.updateOne({ "name": "Wilson" }, { $set: { "balance": 50 } });
        recordCol.insertOne({ "name": "Wilson", change: -50, balance: 50 });
    } catch (error) {
        session.abortTransaction();
    } 
    session.commitTransaction();
    session.endSession();

     
    残高の表示
    db.balance.aggregate([
        { $lookup: { from: "record", localField: "name", foreignField: "name", as: "changs" } },
        { $project: { "_id": 0, "changs._id": 0, "changs.name": 0 } },
    ]);

    結局、残高が引かれて流れが1本増えたのが見えます
    { "name" : "Wilson", "balance" : 50, "changs" : [ { "change" : 100, "balance" : 100 }, { "change" : -50, "balance" : 50 } ] }

     
     
    テスト失敗ロールバック
    トランザクションに存在しないコレクションを挿入する操作を追加し、トランザクションをエラーにします.
    session.startTransaction({ readConcern: { level: "local" }, writeConcern: { w: "majority" } });
    try {
        balanceCol.updateOne({ "name": "Wilson" }, { $set: { "balance": -50 } });
        recordCol.insertOne({ "name": "Wilson", change: -50, balance: 0 });
        session.getDatabase("mongo").user.insert({ "time": new Date() });    //            
    } catch (error) {
        session.abortTransaction();
        throw error;
    }
    session.commitTransaction();
    session.endSession();

     
    エラーメッセージを返し、トランザクションが中断されたことを示します.
    2020-04-15T21:37:05.576+0800 E  QUERY    [js] uncaught exception: Error: command failed: {
            "errorLabels" : [
                    "TransientTransactionError"
            ],
            "operationTime" : Timestamp(1586957825, 1),
            "ok" : 0,
            "errmsg" : "Transaction 0 has been aborted.",
            "code" : 251,
            "codeName" : "NoSuchTransaction",
            "$clusterTime" : {
                    "clusterTime" : Timestamp(1586957825, 1),
                    "signature" : {
                            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                            "keyId" : NumberLong(0)
                    }
            }
    } :
    _getErrorWithCode@src/mongo/shell/utils.js:25:13
    doassert@src/mongo/shell/assert.js:18:14
    _assertCommandWorked@src/mongo/shell/assert.js:583:17
    assert.commandWorked@src/mongo/shell/assert.js:673:16
    commitTransaction@src/mongo/shell/session.js:971:17
    @(shell):1:1
     
      

    再查看当前余额情况

    db.balance.aggregate([
        { $lookup: { from: "record", localField: "name", foreignField: "name", as: "changs" } },
        { $project: { "_id": 0, "changs._id": 0, "changs.name": 0 } },
    ]);

    残高も流水も変わっていないことがわかります.
    { "name" : "Wilson", "balance" : 50, "changs" : [ { "change" : 100, "balance" : 100 }, { "change" : -50, "balance" : 50 } ] }

     
    参考記事
    Replication — MongoDB Manual
    Transactions — MongoDB Manual
    転送先は出典を明記してください.https://www.cnblogs.com/WilsonPan/p/12708710.html