Transaction là gì? Thực hành trên MongoDB 4.0 đơn giản và dễ hiểu

Nội dung bài viết

Tôi tin rằng những lập trình viên đã sử dụng cơ sở dữ liệu quan hệ chính thống không quá xa lạ với Transaction. Nhiều hoạt động dữ liệu can thiệp vào nhau và khi xảy ra lỗi trong bất kỳ hàm thực thi nào thì giao dịch sẽ bị hủy bỏ và dữ liệu sẽ được khôi phục, đảm bảo tính nhất quán của dữ liệu khi sửa đổi dữ liệu trong nhiều bảng cùng một lúc đó chính là Transaction.


Những trường hợp thực tế phải sử dụng Transaction

Thực tế có nhiều trường hợp xảy ra và được sử dụng Transaction nhiều nhất. Cụ thể là hai trường hợp mà tôi sẽ nhắc tới đó là việc chuyển tiền trong ngân hàng và trường hợp thứ 2 đó là đặt hàng trực tuyến như tiki, shoppee, lazada. 


Trường hợp 1: 


Hiện tại có hai người dùng, người dùng A (anonystick) có số dư là 50 đồng, người dùng B có số dư là 10 đồng và người dùng A chuyển 10 đồng sang B. Giả sử rằng kịch bản chuyển tiền rất an toàn, mạng cũng thông suốt, không có hacker đánh chặn và không có tai nạn gì về code và lần này chuyển khoản thành công, lúc này người dùng A có số dư là 40 đồng và người dùng B có số dư là 20. Tuyệt vời, giá như cuộc sống diễn ra luôn là như vậy nhưng... 


A cảm thấy rất an toàn, sau đó tiếp tục chuyển cho B. A quên số dư của mình hiện tại là bao nhiêu và chuyển tiếp 50 đồng cho B, nhưng giả sử đã xảy ra lỗi. Khi giao dịch xảy ra thì cơ sở dữ liệu hoạt động như sau: 


1. Số dư của tài khoản A là -50 

2. Tài khoản B tăng thêm 50. 


Khi số dư của tài khoản A thiếu hoặc xảy ra lỗi mạng sau khi hoạt động của tài khoản A đã bị trừ nhưng đã xảy ra lỗi thì ngay tại đây Transaction xảy ra thì B không có cơ hội để nhận được số tiền trên. Trong trường hợp của một giao dịch (Transaction), ngay cả khi lỗi xảy ra sau khi vận hành số tiền của tài khoản A, giao dịch sẽ khôi phục toàn bộ quá trình chuyển về trước khi sửa đổi. 


Có nghĩa là sau khi trừ 50 thì A còn -10 những sẽ được khôi phục lại sau khi xảy ra lỗi nhờ tính năng transaction của database.


Transaction là gì?

Qua ví dụ thực tế thì các bạn cũng hiểu ý nghĩa về Transaction là gì rồi đúng không? 


Transaction là được thực hiện bởi phiên khách hàng được kết nối bởi Database (MongoDB, Mysql...). Quá trình thực hiện Transaction đại khái là thiết lập một phiên, bắt đầu giao dịch thông qua session startTransaction, nếu một loạt Transaction hoàn thành thì commitTransaction hoàn thành thao tác Transaction và kết thúc phiên Transaction hiện tại; Nếu bất kỳ sự kiện nào không thành công trong Transaction, abortTransaction sẽ hủy bỏ Transaction, chuyển nội bộ nhiệm vụ đã hoàn thành về trước khi sửa đổi và kết thúc phiên Transaction hiện tại. Nói ít mà hiểu nhiều. 


session = client.startSession(); 

session.startTransaction(); 

session.commitTransaction(); 

session.abortTransaction(); 

session.endSession();


Thực hành Transaction với mongodb 4.0

Đầu tiên bạn phải install mongodb và cài đặt MongoDB

Notes: Transaction chỉ áp dụng cho replica set, vì vậy trước tiên bạn phải xây dựng replica set trong mongodb

Setup replica set trong mongodb

mkdir -p data/1301
mkdir -p data/1302 
mkdir -p data/1303

// Tao nhieu db
./bin/mongod --replSet shard1  --dbpath=./data/1301 --port=1301
./bin/mongod --replSet shard1  --dbpath=./data/1302 --port=1302
./bin/mongod --replSet shard1  --dbpath=./data/1303 --port=1303

// Config nhieu phien ban
./bin/mongo --port 1301
rsconf = {_id: "shard1", members: [ { _id: 0, host: "127.0.0.1:1301" } ] }
rs.initiate( rsconf )
rs.add("127.0.0.1:1302")
rs.add("127.0.0.1:1303")


//Kiểm tra xem nó có phải là db chính không
rs.isMaster() 

//Xem trạng thái bản sao
rs.status()

setup replica set mongoDB

demo transaction mongodb

Tạo project test transaction với nodejs

Cứ copy về làm theo thôi...

AnonyStick$ 
AnonyStick$ mkdir mongodb4Transaction
AnonyStick$ cd mongodb4Transaction/
AnonyStick$ npm init
AnonyStick$ npm i mongodb -S
AnonyStick$ vi app.js

File app.js

(async function()  {

  // Connect
  const { MongoClient } = require('mongodb');
  const uri = 'mongodb://localhost:1301/dbfour';
  const client = await MongoClient.connect(uri, { useNewUrlParser: true });

  const db = client.db();
  await db.dropDatabase();
  console.log('(1) Xoá hết database nếu tồn tại \n')

  // Tạ dữ liệu cho hai tài khoản
  await db.collection('Account').insertMany([
    { name: 'A', balance: 50 },
    { name: 'B', balance: 10 }
  ]);
  console.log('(2) Thực hiện insertMany,  Tài khoản Anonystick có 50 đồng, Tài khoản B  có 10 đồng\n')


  await transfer('A', 'B', 10); // lần 1
  console.log('(3) Sau đó Anonystick chuyển tiền cho B 10 đồng\n')

  try {
    // test chuyển tiền sô dư không đủ
    console.log('(4) Anonystick lại chuyển tiền cho B 50\n')
    await transfer('A', 'B', 50);

  } catch (error) {
    //error.message; // "Không đủ tiền: 40"
    console.log(error.message)
    console.log('\n(5) Thông báo cho tài khoản anonystick - Số dư không đủ nên thao tác chuyển này không thành công')
  }

  // Lệnh chuyển tiền
  async function transfer(from, to, amount) {
    const session = client.startSession();
    session.startTransaction();
    try {
      const opts = { session, returnOriginal: false };
      const A = await db.collection('Account').
        findOneAndUpdate({ name: from }, { $inc: { balance: -amount } }, opts).
        then(res => res.value);
        console.log('A>>>A', A)
      if (A.balance < 0) {
        // Nếu số dư của Anonystick không đủ, việc chuyển tiền không thành công và giao dịch bị hủy bỏ
        // `session.abortTransaction()` có nhiềm vụ Sẽ hoàn tác thao tác `findOneAndUpdate () 'ở trên

        //select lại lần nữa?
        console.log(await db.collection('Account').findOne({name: from}))
        throw new Error('Không đủ tiền: ' + (A.balance + amount));
      }

      const B = await db.collection('Account').
        findOneAndUpdate({ name: to }, { $inc: { balance: amount } }, opts).
        then(res => res.value);

      await session.commitTransaction();
      session.endSession();
      return { from: A, to: B };
    } catch (error) {
      // Nếu xảy ra lỗi, hãy hủy bỏ tất cả các giao dịch và quay trở lại trước khi sửa đổi
      console.log('Loi ne');
      await session.abortTransaction();
      session.endSession();
      throw error; // catch error
    }
  }

})()

Xem kết quả

node app.js

transaction la gi?

Như vậy bài viết vừa rồi giúp bạn hiểu được "Transaction là gì?" và demo nho nhỏ giúp bạn hình dung rõ hơn rất nhiều thông qua việc setup mongodb. Nếu các bạn cần code thì liên hệ tại gmail nhé. Thanks for reading!

Có thể bạn đã bị missing