LapTrinhBlockchain

Chia sẻ kiến thức về Lập Trình Blockchain

Kiến thức phần mềm, Nâng cao Kiến thức

Hướng dẫn cài đặt sử dụng MongoDB và thư viện Mongoose trên NodeJs

Hướng dẫn cài đặt và sử dụng MongoDB

Hướng dẫn cài đặt và sử dụng MongoDB

Chia sẻ bài viết
5
(6)

Bài viết này tập trung về việc cài đặt và sử dụng thư viện MongoDB trên NodeJs. Nếu bạn quan tâm tới cài đặt NodeJs, hãy tham khảo bài viết: Lập trình NodeJs: Hướng dẫn cài đặt NodeJs -NPM – NVM-PM2, thư viện hay dùng, xử lý lỗi và kinh nghiệm thực tế

Mục lục

Cài đặt MongoDB

Cài đặt trên Ubuntu

Để cài đặt, bạn thực hiện theo hướng dẫn: Install MongoDB Community Edition on Ubuntu. Mình thử thấy chạy luôn, chỉ có điều lệnh mongo thì phải đổi lại thành mongosh. Bản này cài đặt MongoDB 7.0, cài được trên Ubuntu 22 và Ubuntu 20.

Với các Ubuntu khác bạn tham khảo thêm trên Google. Ví dụ chi tiết cài đặt tham khảo bài viết: How to Install MongoDB on Ubuntu 16.04. Hoặc các lệnh cài đặt như sau trên Ubuntu 18:

// Cài đặt
wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list
sudo apt-get update
sudo apt-get install -y mongodb-org

// Kiểm tra
mongo --version

Các lệnh start/stop server:

// Mongo tự động chạy khi server bật
sudo systemctl enable mongod

// Start mongo
sudo systemctl start mongod

// Restart mongo
sudo systemctl restart mongod

// Xem trạng thái mongo
sudo systemctl status mongod

Để cài đặt bạn thực hiện theo hướng dẫn trên trang: How to Install MongoDB on Ubuntu 22.04.

Mặc định sau khi cài đặt thì chỉ cho phép kết nối DB qua local thôi, để có thể kết nối từ client bất kỳ bạn phải sửa têp /etc/mongod.conf:
Tìm dòng:
bindIp: 127.0.0.1
thành
bindIp: 0.0.0.0

Sau đó restart lại mongo. Việc mở thế này rất rủi ro, vì thế bạn phải cấu hình Firewall để chỉ một số IP có thể kết nối đến thôi.

Cài đặt trên Windows

Trên Windows bạn chỉ cần tải tệp cài đặt về rùi chạy. Chi tiết xem: Cài đặt cơ sở dữ liệu MongoDB trên Windows

Một số thao tác cơ bản trên MongoDB

Lệnh kết nối vào tạo Database

Lệnh kết nối database:

// Kết nối DB trên local, không yêu cầu xác thực
// Một số phiên bản phải dùng mongosh
mongo mongodb://127.0.0.1/MyDatabase

// Kết nối DB trên local, có yêu cầu xác thực
mongo mongodb://testuser:MatKhauManh@127.0.0.1/MyDatabase

Một số lệnh cơ bản trên DB

// Hiển thị các DB hiện có
show databases
show dbs

// Chọn một DB
use MyDatabase

// Xóa một Database
user MyDatabase
db.dropDatabase()

// Hiển thị các Collections trong DB hiện tại
show collections

// Lệnh tạo một collection mới
db.createCollection("NewCollection")

// Lệnh xóa 1 collection
db.NewCollection.drop()

// Thao tác trên 1 collection
db.<Collection>.<function>(<param>)
db.getCollection("<Collection>".<function>(<param>)

// Liệt kê dữ liệu, loại bỏ trùng lặp
// Nhưng chỉ loại bỏ trùng lặp của 1 trường dữ liệu
db.<Collection>.distinct("appName")
db.<Collection>.distinct("appName", {network: "DOGECHAIN"})

Để tạo DB mới thì bạn sử dụng lệnh use, sau đó tạo ít nhất 1 collection trên DB mới:

use NewDatabase
db.createCollection("users")

Tạo user cho một database

Để tạo user cho DB, bạn sử dụng lệnh sau:

use MyDatabase
db.createUser({user:"testuser", pwd:"MatKhauManh", roles:[{ role: "root", db: "admin" }]});

Sau khi tạo xong bạn có thể thử kết nối đến DB bằng lệnh:

mongo mongodb://testuser:MatKhauManh@localhost/NewDatabase

Lệnh kiểm tra số lượng kết nối tới DB

Để xem thông tin kết nối tới DB, bạn sử dụng lệnh sau:

mongo
db.serverStatus().connections

Xuất nhập dữ liệu

Để xuất dữ liệu ta dụng lệnh mongodump trên command line:

mongodump --uri mongodb://testuser:MatKhauManh@localhost/MyDatabase -o backup

Để xuất dữ liệu từ lệnh trên mongo ra tệp, sử dụng lệnh sau:

mongo mongodb://testuser:MatKhauManh@localhost/MyDatabase --eval "db.currencystatuses.find().pretty()" > sample.json 

Để import dữ liệu ta dùng lệnh mongostore:

mongorestore --uri mongodb://testuser:MatKhauManhj@127.0.0.1/MyDatabase --dir ./backup

Nhiều khi bạn muốn xóa hết dữ liệu cũ và import dữ liệu mới bạn có thể dùng lệnh sau:

mongorestore --uri mongodb://testuser:MatKhauManhj@127.0.0.1/MyDatabase --drop --dir ./backup

Thao tác trên các Collections

Một số lệnh thao tác cơ bản

Cú pháp chung thao tác chung với Collection:

// Cú pháp chung
db.[Tên collection].[Tên hàm 1]([Tham số]).[Tên hàm 2]([Tham số])...

Một số lệnh hay dùng:

// Đếm số bản ghi trong một collection
db.MyCollection.countDocuments({})
db.MyCollection.countDocuments({name: "test"})

// Đổi tên collection
db.MyCollection.renameCollection("NewCollection")

// Hiển thị tất cả bản ghi
db.MyCollection.find({})

// Tìm và hiển thị 10 bản ghi
db.MyCollection.find({}).limit(10)

// Tìm và hiển thị 10 bản ghi, mỗi bản ghi chỉ lấy một số trường xác định
// Ví dụ: item, status và _id
// Trường _id mặc định luôn có trừ khi bạn báo rõ là không lấy nó.
db.MyCollection.find({}, { item: 1, status: 1}).limit(10)

// Tìm và hiển thị 10 bản ghi, mỗi bản ghi chỉ lấy một số trường xác định
// VD: item, status
db.MyCollection.find({}, { item: 1, status: 1, _id: 0}).limit(10)

// Tìm và hiển thị 10 bản ghi nhưng hiển thị dễ nhìn hơn
db.MyCollection.find({}).limit(10).pretty()

// Hiển thị 10 bản ghi mới nhất
db.MyCollection.find({}).sort({createdAt: -1}).limit(10).pretty()

Copy bản ghi collection sang collection khác

Cách 1: Sử dụng lệnh clone qua hàm:

// Chạy 1 lệnh
db.runCommand(command)

// Ví dụ => Chưa chạy thật :))
use marketing
db.runCommand( { clone: "db1.example.net:27017" })

Cách 2: Sử dụng kết hợp lệnh find() và insert() như sau:

Copy trong cùng Database:

use MyDatabase
db.createCollection("MyCollection_BK");
db.MyCollection.find({}).forEach(function(item) {
  db.MyCollection_BK.insert(item);
});

Copy trên Database khác nhau:

use MyDatabase
db.MyCollection.find({}).forEach(function(item) {
  db.getSiblingDB('MyOtherDatabase')['MyOtherCollection'].insert(item);
});

Tìm kiếm các bản ghi theo yêu cầu

Bạn sử dụng lệnh find(), findOne() để tìm kiếm các bản ghi theo yêu cầu của mình. Ngoài ra bạn có thể kết hợp với lệnh sort(), limit(), pretty() để nhiều cách hiển thị hơn. Cú pháp như sau:

db.<Collection>.find(<CONDITION>)
db.<Collection>.findOne(<CONDITION>)

Trong đó CONDITION theo chuẩn JSON và khá linh hoạt:

// Điều kiện bằng đơn giản
{name: "test"}

// Điều kiện lớn hơn nhỏ hơn với $lt, $lte, $gt, $gte
{name: "test", quantity: { $lt: 1250 } }
{name: "test", quantity: { $lt: 1550, $gt: 3000 } }

Thêm bản ghi vào collection

Sử dụng hàm insert() để thêm dữ liệu cho 1 collection:

// Cú pháp lệnh
db.<Collection>.insert(document)

// Ví dụ 1 lệnh thêm
db.softwares.insert({
   title: "MongoDB",
   description: "MongoDB is no sql database",
})

Cập nhật bản ghi vào collection

Sử dụng hàm update(), updateOne() để cập nhật dữ liệu cho 1 bản ghi. Sử dụng updateMany() để cập nhật dữ liệu cho nhiều bản ghi.

// Cú pháp lệnh
db.<COLLECTION_NAME>.update(<CONDITION>, <UPDATED_DATA>)
db.<COLLECTION_NAME>.updateOne(<CONDITION>, <UPDATED_DATA>)
db.<COLLECTION_NAME>.updateMany(<CONDITION>, <UPDATED_DATA>)

// Ví dụ lệnh
db.softwares.update({title: "MongoDB"}, { $set: { description: "It is only MongoDB" } })

Phần CONDITION bạn có thể xem ở phần trên, phần UPDATED_DATA cũng rất linh hoạt:

// Thiết lập giá trị mới cho 1 trường dữ liệu sử dụng $set
{ $set: { orderId: "678974329166" } }

// Cập nhật giá trị cho 1 phần tử trong mảng
// Dữ liệu bảng có trường symbol và transactions. Trong đó transactions là một mảng
// Cập nhật trường entryTime của phần tử thứ 0 trong mảng
db.investingtrans.updateOne({symbol: "TKOUSDT"}, { $set: { "transactions.0.entryTime": "2021-09-07 22:39:44" }}, { upsert: true })
// Cập nhật trường entryTime cho tất cả các phần tử trong mảng
db.investingtrans.updateOne({symbol: "TKOUSDT"}, { $set: { "transactions.$[].entryTime": "2021-09-07 22:39:44" }}, { upsert: true })
// Bỏ một trường dữ liệu sử dụng $unset
{ $unset: { orderId: "" } }
{ $unset: { "assets.BNB": "" } }

Một số thao tác xử lý dữ liệu

Thao tác chuyển sang chữ HOA / chữ THƯỜNG của một số trường nào đó

Bạn dùng lệnh sau:

db.myCollection.find().forEach(
  function(e) {
    e.UserName = e.UserName.toLowerCase();
    db.myCollection.save(e);
  }
)

Sao chép dữ liệu trong cùng một database

db.MyCollection.find({}).forEach(function(item) {
    db.MyCollection_BK.insertOne(item);
});

Xử lý theo lô trong MongoDB

Hầu hết các CSDL đều hỗ trợ xử lý theo lô (Batch) và MongoDB cũng vậy. Nhưng tại sao ta cần xử lý theo lô? Có 2 lý do chính mà chúng ta cần phải xử lý theo lô:

  • Xử lý lượng dữ liệu lớn với thời gian ngắn: Ta dùng xử lý theo lô trong trường hợp cần UPDATE / INSERT một lượng lớn bản ghi vào CSDL. Nếu chúng ta update từng bản ghi 1 sẽ rất chậm, nhưng ta update đồng thời cùng một lúc sẽ rất nhanh. Tốc độ thường sẽ nhanh hơn 10 lần đến cả 100 lần.
  • Đảm bảo tính toàn vẹn dữ liệu: Trong nhiều trường hợp một nghiệp vụ thực tế đòi hỏi phải cập nhật nhiều bảng dữ liệu khác nhau, nếu ta thực hiện từng bảng riêng lẻ trong trường hợp có 1 bảng lỗi ta phải revert lại dữ liệu các bàng trước đó rất phức tạp, nếu không revert dữ liệu sẽ bị sai. Trong trường hợp này ta đóng gói tất cả các giao dịch này trong 1 Lô (Batch) để xử lý, nếu lỗi thì cả lô sẽ bị lỗi, thành công thì cả lô sẽ thành công. Như vậy đảm bảo được tính toàn vẹn của dữ liệu.

Xử lý theo lô trong MongoDB qua Command line

Trong MongoDB hỗ trợ hai cách để xử lý theo lô:

C1: Sử dụng Bulk()

Cách này được hỗ trợ trực tiếp trong MongoDB, chỉ thao tác được với từng Collection. Chi tiết xem: Bulk OperationBulk.execute. Để thực hiện một xử lý theo lô bạn làm theo hướng dẫn dưới:

// Khởi tạo 1 Bulk dùng 1 trong 2 lệnh ở dưới
// Tùy thuộc vào các lệnh có cần đúng thứ tự hay không?
db.collection.initializeOrderedBulkOp()
db.collection.initializeUnorderedBulkOp()

// Sử dụng các hàm dưới để thực hiện thao tác thay đổi
// Cú pháp và tham số giống như bình thương
Bulk.insert()
Bulk.find.delete()
Bulk.find.removeOne()
Bulk.find.update()
...

// Cuối cùng dùng lệnh sau để cập nhật các thao đổi vào DB
Bulk.execute()

C2: Sử dụng db.collection.bulkWrite()

Cách này chỉ hỗ trợ trên mongsh giúp tiện lợi hơn khi thao tác. Chi tiết bạn xem tại: db.collection.bulkWrite(). Cú pháp như sau:

db.collection.bulkWrite(
   [ <operation 1>, <operation 2>, ... ],
   {
      writeConcern : <document>,
      ordered : <boolean>
   }
)

Xử lý theo lô qua thư viện mongoose trên NodeJs

Đây là vấn đề mà các Dev sẽ quan tâm hơn nhiều. Trong Mongoose có nhiều cách để xử lý theo lô.

Để đơn giản, trong phần này tôi mặc định là các bạn đã khai báo PairInfoPairInfoHistory là hai model trong Mongoose đã được khai báo.

Cách nguyên thủy nhất chúng ta dùng hàm initializeOrderedBulkOp() hoặc hàm initializeUnorderedBulkOp(). Dưới đây là ví dụ chúng ta cần cập nhật trường data của 1000 đối tượng PairInfo trong mảng pairInfos vào DB:

var ObjectId = require('mongodb').ObjectId;

//...

let bulk = PairInfo.collection.initializeUnorderedBulkOp();
pairInfos.forEach(item => {
    // Update PairInfo
    bulk.find({ _id: new ObjectId(item._id) }).update({
        $set: {
            data: item.data
        }
    });
});
bulk.execute(function(err, result) {
    console.log(err, result);
});

Bây giờ chúng ta sẽ mở rộng hơn bài toán, ngoài yêu cầu trên chúng ta đồng thơi thêm 1000 đối tượng này vào bảng PairInfoHistory:

var ObjectId = require('mongodb').ObjectId;

// Declare here
// ...

// Save bulk to database
// Return number of inserted / updated documents
function saveBulk(bulk) {

    return new Promise(function(resolve, reject) {
        bulk.execute(function(err, result) {
            if (err) {
                console.error("Unable to save Bulk", err, result);
                resolve(-1);
                return;
            }
            // console.log("Bulk Result", result);
            let counter = 0;
            if (result && result.ok==1) {
                counter = result.nInserted + result.nModified + result.nRemoved;
            }
            resolve(counter);
        });
    });
}

async function updatePairsToDb(pairInfos) {
    let bulk1 = PairInfo.collection.initializeUnorderedBulkOp();
    let bulk2 = PairInfoHistory.collection.initializeUnorderedBulkOp();
    pairInfos.forEach(item => {
        // Update PairInfo
        bulk1.find({ _id: new ObjectId(item._id) }).update({
            $set: {
                data: item.data
            }
        });

        // Insert PairInfoHistory
        let newItem = new PairInfoHistory(item);
        newItem._id = mongoose.Types.ObjectId();
        bulk2.insert(newItem);
    });
    
    let promises = [
        saveBulk(bulk1),
        saveBulk(bulk2)
    ];
    let results = await Promise.all(promises);
    console.log("Number of PairInfo are updated", results[0], pairInfos.length);
    console.log("Number of PairInfoHistory are inserted", results[1], pairInfos.length);
}

Ngoài ra bạn có thể sử dụng các hàm dưới, thao tác sẽ đơn giản hơn nhiều:

  • Model.bulkWrite(): Cho phép bạn insert/update/delete nhiều bản ghi với điều kiện khác nhau trong một lô, nhưng giảm một số thao tác so với Bulk ở trên.
  • Model.bulkSave(): Đơn giản hóa hàm Model.bulkWrite() và chỉ hỗ trợ trong phiên bản mới của thư viện Mongoose.

Một số chú ý khi sử dụng NodeJs để thao tác Mongo

Trên NodeJs hầu hết mọi người sử dụng thư viện mongoose. Nói chung thư viện nào cũng được, nhưng ở đây có một số chú ý phần hiệu năng:

Nên đánh Index để tăng tốc độ truy vấn

Để đánh index trường nào thì bạn xem xét lại code các truy vấn DB của mình, từ đó chọn trường cho hợp lý.

Lệnh đánh index như sau:

db.pairinfos.createIndex({ network: 1, dexName: 1 });

Chúng ta có thể kiểm tra dữ liệu thống kê của việc sử dụng index:

# Xem collection đã có index nào
db.collection.getIndexes()

# Bạn nhớ thay collection bằng tên một collection cụ thể nào đó
db.collection.aggregate( [ { $indexStats: { } } ] )

# Ví dụ
db.pairinfos.aggregate( [ { $indexStats: { } } ] )

Cập nhật từng bản ghi hay nhiều bản ghi

Bạn có thể cập nhật bản ghi bằng hàm sau:

  • Hàm save() => Cập nhật từng bản ghi 1
  • Hàm updateMany() => Cập nhật nhiều bản ghi theo điều kiện tìm thấy

Thông thường cập nhật 1 lúc nhiều bản ghi thì sẽ nhanh hơn, nhưng nhiều khi nó lại chậm hơn. Ví dụ: Bạn có CSDL hàng triệu bản ghi, lệnh của bạn trung bình chỉ cập nhật 10 bản ghi thì có khi cập nhật từng bản ghi sẽ nhanh hơn. Bởi chi phí tìm kiếm dữ liệu lớn trừ khi bạn có đánh index.

Ưu tiên thêm dữ liệu theo lô thay vì thêm từng bản ghi

Nếu bạn thêm đồng thời nhiều dữ liệu một lúc thì bạn không nên thêm từng bản ghi 1 mà phải thêm cả lô một lúc bằng hàm insertMany().

Xử lý một số lỗi trên MongoDB

Dưới đây là một số lỗi liên quan tới MongoDB mà mình từng gặp.

An incomplete repair has been detected! This is likely because a repair operation unexpectedly failed before completing

Lỗi này do một DEV của mình không hiểu bị lỗi DB gì đó mới lên mạng search để sửa, nhưng không hiểu các lệnh trên mạng lên đã thực hiện lệnh sau (Cũng không nhớ hết còn lệnh nào không):

// Lần 1 sửa
ps ax | grep mongod
sudo killall -15
sudo mkdir -p /data/db
sudo systemctl unmask mongodb
sudo mongod --dbpath /usr/local/var/mongodb --logpath /usr/local/var/log/mongodb/mongo.log --fork
sudo mongod --dbpath /var/lib/mongodb/ --repair
sudo mongod --dbpath /var/lib/mongodb/ --journal

// Chạy tiếp lệnh
sudo rm /var/lib/mongodb/mongod.lock 
sudo mongod --repair --dbpath /var/lib/mongodb 
sudo mongod --fork --logpath /var/lib/mongodb/mongodb.log --dbpath /var/lib/mongodb 
sudo service mongodb start

// Vẫn lỗi cài đè DB
brew install mongodb-community@4.2

Khi mình vào xem thì không thể start được server, xem log mongo từ tệp “/var/log/mongodb/mongod.log” có thông báo lỗi bằng lệnh:
tail -n 1000 /var/log/mongodb/mongod.log -f
Thấy log lỗi như sau

{"t":{"$date":"2022-06-23T08:48:33.228+00:00"},"s":"I",  "c":"CONTROL",  "id":20698,   "ctx":"main","msg":"***** SERVER RESTARTED *****"}
{"t":{"$date":"2022-06-23T08:48:33.230+00:00"},"s":"I",  "c":"CONTROL",  "id":23285,   "ctx":"main","msg":"Automatically disabling TLS 1.0, to force-enable TLS 1.0 specify --sslDisabledProtocols 'none'"}
{"t":{"$date":"2022-06-23T08:48:33.302+00:00"},"s":"W",  "c":"ASIO",     "id":22601,   "ctx":"main","msg":"No TransportLayer configured during NetworkInterface startup"}
{"t":{"$date":"2022-06-23T08:48:33.303+00:00"},"s":"I",  "c":"NETWORK",  "id":4648601, "ctx":"main","msg":"Implicit TCP FastOpen unavailable. If TCP FastOpen is required, set tcpFastOpenServer, tcpFastOpenClient, and tcpFastOpenQueueSize."}
{"t":{"$date":"2022-06-23T08:48:33.303+00:00"},"s":"I",  "c":"STORAGE",  "id":4615611, "ctx":"initandlisten","msg":"MongoDB starting","attr":{"pid":9684,"port":27017,"dbPath":"/var/lib/mongodb","architecture":"64-bit","host":"TRADE-SERVER-09"}}
{"t":{"$date":"2022-06-23T08:48:33.303+00:00"},"s":"I",  "c":"CONTROL",  "id":23403,   "ctx":"initandlisten","msg":"Build Info","attr":{"buildInfo":{"version":"4.4.9","gitVersion":"b4048e19814bfebac717cf5a880076aa69aba481","openSSLVersion":"OpenSSL 1.1.1  11 Sep 2018","modules":[],"allocator":"tcmalloc","environment":{"distmod":"ubuntu1804","distarch":"x86_64","target_arch":"x86_64"}}}}
{"t":{"$date":"2022-06-23T08:48:33.303+00:00"},"s":"I",  "c":"CONTROL",  "id":51765,   "ctx":"initandlisten","msg":"Operating System","attr":{"os":{"name":"Ubuntu","version":"18.04"}}}
{"t":{"$date":"2022-06-23T08:48:33.303+00:00"},"s":"I",  "c":"CONTROL",  "id":21951,   "ctx":"initandlisten","msg":"Options set by command line","attr":{"options":{"config":"/etc/mongod.conf","net":{"bindIp":"127.0.0.1","port":27017},"processManagement":{"timeZoneInfo":"/usr/share/zoneinfo"},"storage":{"dbPath":"/var/lib/mongodb","journal":{"enabled":true}},"systemLog":{"destination":"file","logAppend":true,"path":"/var/log/mongodb/mongod.log"}}}}
{"t":{"$date":"2022-06-23T08:48:33.305+00:00"},"s":"E",  "c":"NETWORK",  "id":23024,   "ctx":"initandlisten","msg":"Failed to unlink socket file","attr":{"path":"/tmp/mongodb-27017.sock","error":"Operation not permitted"}}
{"t":{"$date":"2022-06-23T08:48:33.305+00:00"},"s":"F",  "c":"-",        "id":23091,   "ctx":"initandlisten","msg":"Fatal assertion","attr":{"msgid":40486,"file":"src/mongo/transport/transport_layer_asio.cpp","line":919}}
{"t":{"$date":"2022-06-23T08:48:33.305+00:00"},"s":"F",  "c":"-",        "id":23092,   "ctx":"initandlisten","msg":"\n\n***aborting after fassert() failure\n\n"}

Lỗi này liên quan tới quyền tệp “/tmp/mongodb-27017.sock“, mình sửa bằng lệnh:

sudo chown mongodb:mongodb /tmp/mongodb-27017.sock

Tiếp theo lại báo lỗi “An incomplete repair has been detected! This is likely because a repair operation unexpectedly failed before completing. MongoDB will not start up again without –repair.“:

{"t":{"$date":"2022-06-23T08:09:21.512+00:00"},"s":"I",  "c":"CONTROL",  "id":20698,   "ctx":"main","msg":" SERVER RESTARTED "}
 {"t":{"$date":"2022-06-23T08:09:21.515+00:00"},"s":"I",  "c":"CONTROL",  "id":23285,   "ctx":"main","msg":"Automatically disabling TLS 1.0, to force-enable TLS 1.0 specify --sslDisabledProtocols 'none'"}
 {"t":{"$date":"2022-06-23T08:09:21.525+00:00"},"s":"W",  "c":"ASIO",     "id":22601,   "ctx":"main","msg":"No TransportLayer configured during NetworkInterface startup"}
 {"t":{"$date":"2022-06-23T08:09:21.525+00:00"},"s":"I",  "c":"NETWORK",  "id":4648601, "ctx":"main","msg":"Implicit TCP FastOpen unavailable. If TCP FastOpen is required, set tcpFastOpenServer, tcpFastOpenClient, and tcpFastOpenQueueSize."}
 {"t":{"$date":"2022-06-23T08:09:21.527+00:00"},"s":"I",  "c":"STORAGE",  "id":4615611, "ctx":"initandlisten","msg":"MongoDB starting","attr":{"pid":8561,"port":27017,"dbPath":"/var/lib/mongodb","architecture":"64-bit","host":"TRADE-SERVER-09"}}
 {"t":{"$date":"2022-06-23T08:09:21.527+00:00"},"s":"I",  "c":"CONTROL",  "id":23403,   "ctx":"initandlisten","msg":"Build Info","attr":{"buildInfo":{"version":"4.4.9","gitVersion":"b4048e19814bfebac717cf5a880076aa69aba481","openSSLVersion":"OpenSSL 1.1.1  11 Sep 2018","modules":[],"allocator":"tcmalloc","environment":{"distmod":"ubuntu1804","distarch":"x86_64","target_arch":"x86_64"}}}}
 {"t":{"$date":"2022-06-23T08:09:21.527+00:00"},"s":"I",  "c":"CONTROL",  "id":51765,   "ctx":"initandlisten","msg":"Operating System","attr":{"os":{"name":"Ubuntu","version":"18.04"}}}
 {"t":{"$date":"2022-06-23T08:09:21.527+00:00"},"s":"I",  "c":"CONTROL",  "id":21951,   "ctx":"initandlisten","msg":"Options set by command line","attr":{"options":{"config":"/etc/mongod.conf","net":{"bindIp":"127.0.0.1","port":27017},"processManagement":{"timeZoneInfo":"/usr/share/zoneinfo"},"storage":{"dbPath":"/var/lib/mongodb","journal":{"enabled":true}},"systemLog":{"destination":"file","logAppend":true,"path":"/var/log/mongodb/mongod.log"}}}}
 {"t":{"$date":"2022-06-23T08:09:21.529+00:00"},"s":"F",  "c":"STORAGE",  "id":50922,   "ctx":"initandlisten","msg":"An incomplete repair has been detected! This is likely because a repair operation unexpectedly failed before completing. MongoDB will not start up again without --repair."}
 {"t":{"$date":"2022-06-23T08:09:21.529+00:00"},"s":"F",  "c":"-",        "id":23091,   "ctx":"initandlisten","msg":"Fatal assertion","attr":{"msgid":50922,"file":"src/mongo/db/storage/storage_engine_init.cpp","line":87}}
 {"t":{"$date":"2022-06-23T08:09:21.529+00:00"},"s":"F",  "c":"-",        "id":23092,   "ctx":"initandlisten","msg":"\n\n***aborting after fassert() failure\n\n"}

Mình phải chạy lệnh sau để sửa:
/usr/bin/mongod –config /etc/mongod.conf –repair
Lệnh này khá lâu bạn phải chờ nó chạy xong nhé. Check log để biết nó chạy xong hay chưa.

Sau đó lại có báo lỗi khác:

{"t":{"$date":"2022-06-23T08:50:15.270+00:00"},"s":"I",  "c":"CONTROL",  "id":20698,   "ctx":"main","msg":" SERVER RESTARTED "}
 {"t":{"$date":"2022-06-23T08:50:15.272+00:00"},"s":"I",  "c":"CONTROL",  "id":23285,   "ctx":"main","msg":"Automatically disabling TLS 1.0, to force-enable TLS 1.0 specify --sslDisabledProtocols 'none'"}
 {"t":{"$date":"2022-06-23T08:50:15.282+00:00"},"s":"W",  "c":"ASIO",     "id":22601,   "ctx":"main","msg":"No TransportLayer configured during NetworkInterface startup"}
 {"t":{"$date":"2022-06-23T08:50:15.283+00:00"},"s":"I",  "c":"NETWORK",  "id":4648601, "ctx":"main","msg":"Implicit TCP FastOpen unavailable. If TCP FastOpen is required, set tcpFastOpenServer, tcpFastOpenClient, and tcpFastOpenQueueSize."}
 {"t":{"$date":"2022-06-23T08:50:15.283+00:00"},"s":"I",  "c":"STORAGE",  "id":4615611, "ctx":"initandlisten","msg":"MongoDB starting","attr":{"pid":9746,"port":27017,"dbPath":"/var/lib/mongodb","architecture":"64-bit","host":"TRADE-SERVER-09"}}
 {"t":{"$date":"2022-06-23T08:50:15.283+00:00"},"s":"I",  "c":"CONTROL",  "id":23403,   "ctx":"initandlisten","msg":"Build Info","attr":{"buildInfo":{"version":"4.4.9","gitVersion":"b4048e19814bfebac717cf5a880076aa69aba481","openSSLVersion":"OpenSSL 1.1.1  11 Sep 2018","modules":[],"allocator":"tcmalloc","environment":{"distmod":"ubuntu1804","distarch":"x86_64","target_arch":"x86_64"}}}}
 {"t":{"$date":"2022-06-23T08:50:15.283+00:00"},"s":"I",  "c":"CONTROL",  "id":51765,   "ctx":"initandlisten","msg":"Operating System","attr":{"os":{"name":"Ubuntu","version":"18.04"}}}
 {"t":{"$date":"2022-06-23T08:50:15.283+00:00"},"s":"I",  "c":"CONTROL",  "id":21951,   "ctx":"initandlisten","msg":"Options set by command line","attr":{"options":{"config":"/etc/mongod.conf","net":{"bindIp":"127.0.0.1","port":27017},"processManagement":{"timeZoneInfo":"/usr/share/zoneinfo"},"storage":{"dbPath":"/var/lib/mongodb","journal":{"enabled":true}},"systemLog":{"destination":"file","logAppend":true,"path":"/var/log/mongodb/mongod.log"}}}}
 {"t":{"$date":"2022-06-23T08:50:15.290+00:00"},"s":"I",  "c":"STORAGE",  "id":22270,   "ctx":"initandlisten","msg":"Storage engine to use detected by data files","attr":{"dbpath":"/var/lib/mongodb","storageEngine":"wiredTiger"}}
 {"t":{"$date":"2022-06-23T08:50:15.290+00:00"},"s":"I",  "c":"STORAGE",  "id":22297,   "ctx":"initandlisten","msg":"Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem","tags":["startupWarnings"]}
 {"t":{"$date":"2022-06-23T08:50:15.290+00:00"},"s":"I",  "c":"STORAGE",  "id":22315,   "ctx":"initandlisten","msg":"Opening WiredTiger","attr":{"config":"create,cache_size=1460M,session_max=33000,eviction=(threads_min=4,threads_max=4),config_base=false,statistics=(fast),log=(enabled=true,archive=true,path=journal,compressor=snappy),file_manager=(close_idle_time=100000,close_scan_interval=10,close_handle_minimum=250),statistics_log=(wait=0),verbose=[recovery_progress,checkpoint_progress,compact_progress],"}}
 {"t":{"$date":"2022-06-23T08:50:16.034+00:00"},"s":"E",  "c":"STORAGE",  "id":22435,   "ctx":"initandlisten","msg":"WiredTiger error","attr":{"error":13,"message":"[1655974216:34198][9746:0x7fcaa69e7ac0], wiredtiger_open: __posix_open_file, 805: /var/lib/mongodb/WiredTiger.turtle: handle-open: open: Permission denied"}}
 {"t":{"$date":"2022-06-23T08:50:16.034+00:00"},"s":"E",  "c":"STORAGE",  "id":22435,   "ctx":"initandlisten","msg":"WiredTiger error","attr":{"error":13,"message":"[1655974216:34812][9746:0x7fcaa69e7ac0], wiredtiger_open: __posix_open_file, 805: /var/lib/mongodb/WiredTiger.turtle: handle-open: open: Permission denied"}}
 {"t":{"$date":"2022-06-23T08:50:16.035+00:00"},"s":"E",  "c":"STORAGE",  "id":22435,   "ctx":"initandlisten","msg":"WiredTiger error","attr":{"error":13,"message":"[1655974216:35083][9746:0x7fcaa69e7ac0], wiredtiger_open: __posix_open_file, 805: /var/lib/mongodb/WiredTiger.turtle: handle-open: open: Permission denied"}}
 {"t":{"$date":"2022-06-23T08:50:16.035+00:00"},"s":"W",  "c":"STORAGE",  "id":22347,   "ctx":"initandlisten","msg":"Failed to start up WiredTiger under any compatibility version. This may be due to an unsupported upgrade or downgrade."}
 {"t":{"$date":"2022-06-23T08:50:16.036+00:00"},"s":"F",  "c":"STORAGE",  "id":28595,   "ctx":"initandlisten","msg":"Terminating.","attr":{"reason":"13: Permission denied"}}
 {"t":{"$date":"2022-06-23T08:50:16.036+00:00"},"s":"F",  "c":"-",        "id":23091,   "ctx":"initandlisten","msg":"Fatal assertion","attr":{"msgid":28595,"file":"src/mongo/db/storage/wiredtiger/wiredtiger_kv_engine.cpp","line":954}}
 {"t":{"$date":"2022-06-23T08:50:16.036+00:00"},"s":"F",  "c":"-",        "id":23092,   "ctx":"initandlisten","msg":"\n\n***aborting after fassert() failure\n\n"}

Mình kiểm tra quyền các tệp và thực mục con trong thư muc “/var/lib/mongodb” bằng lệnh:
ls -l /var/lib/mongodb/
thì thấy sai rất nhiều tệp và thư mục bị sai quyền:

Tệp bị sai quyền trong thu mục dữ liệu của mongo
Tệp bị sai quyền trong thu mục dữ liệu của mongo
Tệp bị sai quyền trong thu mục dữ liệu của mongo
Tệp bị sai quyền trong thu mục dữ liệu của mongo

Mình thực hiện các lệnh sau để cập nhật lại quyền (Tùy vào thực tế các file có thể khác nhau):

sudo chown mongodb:mongodb /var/lib/mongodb/WiredTiger.turtle
sudo chown mongodb:mongodb /var/lib/mongodb/journal/WiredTigerLog.0000001162
sudo chown mongodb:mongodb /var/lib/mongodb/journal/WiredTigerLog.0000000001
sudo chown mongodb:mongodb /var/lib/mongodb/journal/WiredTigerLog.0000000002
sudo chown mongodb:mongodb /var/lib/mongodb/mongodb.log
sudo chown mongodb:mongodb /var/lib/mongodb/mongodb.log.2022-06-19T16-10-48

Vì ông DEV cài lại DB bằng lệnh “brew install mongodb-community@4.2” nên mình quyết định cài lại DB bằng lệnh trước đây:

wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list
sudo apt-get update
sudo apt-get install -y mongodb-org

Cuối cùng thì mongo đã chạy lại bình thường với lệnh:
sudo service mongod start

Mongo cứ chạy 1 thời gian là bị CRASH

Mình đã gặp trường hợp scale ứng dụng thành nhiều process, nhưng cùng kết nối tới 1 DB thì cứ chạy được 1 thời gian thì MongoDB bị tèo (CRASH).

Kiểm tra log /var/log/mongodb/mongod.log thì thấy nhiều log báo “Slow query” khi truy vấn tới collection “scheduleJobs“, còn lại không thấy log crash nào cả.

Sau đó kiểm tra tiếp trong tệp /var/log/kern.log bằng lệnh sau:
grep mongod /var/log/kern.log*
Thì thấy log sau:

/var/log/kern.log:Aug 29 02:44:07 TRADE-SERVER-02-US kernel: [25901205.303869] [17224]   111 17224   901372   418817  4739072        0             0 mongod
/var/log/kern.log:Aug 29 02:44:07 TRADE-SERVER-02-US kernel: [25901205.303885] Out of memory: Kill process 17224 (mongod) score 415 or sacrifice child
/var/log/kern.log:Aug 29 02:44:07 TRADE-SERVER-02-US kernel: [25901205.305166] Killed process 17224 (mongod) total-vm:3605488kB, anon-rss:1675268kB, file-rss:0kB, shmem-rss:0kB
/var/log/kern.log:Aug 29 02:44:07 TRADE-SERVER-02-US kernel: [25901205.479209] oom_reaper: reaped process 17224 (mongod), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
/var/log/kern.log:Sep 1 11:27:01 TRADE-SERVER-02-US kernel: [26191782.294277] [20522] 111 20522 819363 393267 4046848 0 0 mongod
/var/log/kern.log:Sep 1 11:27:01 TRADE-SERVER-02-US kernel: [26191782.294298] Out of memory: Kill process 20522 (mongod) score 390 or sacrifice child
/var/log/kern.log:Sep 1 11:27:01 TRADE-SERVER-02-US kernel: [26191782.295455] Killed process 20522 (mongod) total-vm:3277452kB, anon-rss:1573068kB, file-rss:0kB, shmem-rss:0kB
/var/log/kern.log:Sep 1 11:27:01 TRADE-SERVER-02-US kernel: [26191782.431652] oom_reaper: reaped process 20522 (mongod), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
/var/log/kern.log.1:Aug 27 12:40:58 TRADE-SERVER-02-US kernel: [25764215.220476] [12106] 111 12106 856067 406009 4431872 0 0 mongod
/var/log/kern.log.1:Aug 27 12:40:58 TRADE-SERVER-02-US kernel: [25764215.220489] Out of memory: Kill process 12106 (mongod) score 403 or sacrifice child
/var/log/kern.log.1:Aug 27 12:40:58 TRADE-SERVER-02-US kernel: [25764215.221667] Killed process 12106 (mongod) total-vm:3424268kB, anon-rss:1624036kB, file-rss:0kB, shmem-rss:0kB
/var/log/kern.log.1:Aug 27 12:40:58 TRADE-SERVER-02-US kernel: [25764215.476922] oom_reaper: reaped process 12106 (mongod), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

Nếu tệp log được nén lại thì bạn phải sử dụng lệnh sau:
find -name \*kern.log.*.gz -print0 | xargs -0 zgrep “mongod”

Nguyên nhân chính là “Out of memory“, mongod sử dụng quá nhiều Memory nên hệ thống tự động KILL. Trường hợp này có cách giải quyết như sau:

  • Tăng RAM cho server => Hơi tốn kém
  • Kiểm tra tạo index cho các query “Slow query” để giảm bộ nhớ sử dụng.
  • Do chia thành quá nhiều process của NodeJs gây tốn RAM, không còn nhiều RAM cho MongoDB nên MongoDB dễ bị TÈO => Giảm process của NodeJs đi.
  • Trường hợp bạn chấp nhận ko tăng RAM bạn có thể cấu hình để MongoDB tự động restart khi bị CRASH bằng cách thêm hai dòng dưới vào sau dòng “ExecStart=/usr/bin/mongod –config /etc/mongod.conf” trong tệp /lib/systemd/system/mongod.service:
    Restart=on-failure
    RestartSec=3s

    Sau đó gọi lệnh:
    systemctl daemon-reload
    service mongod start

E: The repository ‘http://security.ubuntu.com/ubuntu impish-security Release’ does not have a Release file

Lỗi khi thực hiện lệnh sau:
sudo apt-get update && sudo apt-get dist-upgrade

Ubuntu cứ báo lỗi:

Hit:1 http://vn.archive.ubuntu.com/ubuntu jammy InRelease
 Ign:2 https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/4.4 InRelease
 Hit:3 http://security.ubuntu.com/ubuntu jammy-security InRelease
 Hit:4 https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/4.4 Release
 Ign:5 http://security.ubuntu.com/ubuntu impish-security InRelease
 Err:6 http://security.ubuntu.com/ubuntu impish-security Release
   404  Not Found [IP: 2620:2d:4000:1::19 80]
 Reading package lists… 98%
 Reading package lists… 98%
 Reading package lists… Done
 E: The repository 'http://security.ubuntu.com/ubuntu impish-security Release' does not have a Release file.
 N: Updating from such a repository can't be done securely, and is therefore disabled by default.
 N: See apt-secure(8) manpage for repository creation and user configuration details.
 W: https://repo.mongodb.org/apt/ubuntu/dists/xenial/mongodb-org/4.4/Release.gpg: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details.

Gặp lỗi này bạn chỉ cần sửa tệp /etc/apt/sources.list.d/impish-security.list. Thực hiện comment dòng hiện tại và thêm dòng mới như dưới:
deb http://old-releases.ubuntu.com/ubuntu impish-security main

SetOptionError: “useCreateIndex” is not a valid option to set

Khi sử dụng NodeJs thiết lập cấu hình như dưới cho Mongoose trong code:

mongoose.set('useCreateIndex', true);

Thì báo lỗi sau:

SetOptionError: useCreateIndex: "useCreateIndex" is not a valid option to set
    at Mongoose.set (/home/ubuntu/git-data/dexscanner/collector/node_modules/mongoose/lib/index.js:250:17)
    at Object.<anonymous> (/home/ubuntu/git-data/dexscanner/collector/utils/database/MongoDB.js:22:10)

Lỗi này do option useCreateIndex đã bị xóa từ Mongoose 6. Bỏ option này trong phiên bản mới.

Có hai cách sử lý lỗi này:

  • C1: Nếu bạn muốn dùng thư viện mongo mới thì bỏ code sử dụng useCreateIndex() đi
  • C2: Bạn lùi phiên bản của Mongoose về bản 5
    npm rm mongoose
    npm i mongoose@5.10.15

MongooseError: Model.findOne() no longer accepts a callback

Trong NodeJs sử dụng Mongoose code như dưới:

User.findOne({ email: email }, function( error, user ) {
    // ...
})

Thì có thông báo lỗi:

MongooseError: Model.findOne() no longer accepts a callback

Do Mongoose không hỗ trợ callback từ phiên bản 5.0.

Có hai cách để xử lý:

  • C1: Bạn sử dụng async/await thay cho callback
  • C2: Bạn lùi phiên bản của Mongoose về bản 5
    npm rm mongoose
    npm i mongoose@5.10.15

DeprecationWarning: collection.ensureIndex is deprecated. Use createIndexes instead

Do sử dụng phiên bản mongoose cũ.

Tham khảo:

Bài viết này có hữu ích với bạn?

Kích vào một biểu tượng ngôi sao để đánh giá bài viết!

Xếp hạng trung bình 5 / 5. Số phiếu: 6

Bài viết chưa có đánh giá! Hãy là người đầu tiên đánh giá bài viết này.

Trả lời

Giao diện bởi Anders Norén