Bối cảnh sự cố
Gần đây, hệ thống vận hành thực tế gặp phải tình trạng người dùng phản ánh tốc độ truy xuất dữ liệu từ MongoDB bị treo và mất nhiều thời gian chờ đợi bất thường. Cấu trúc hạ tầng hiện tại đang sử dụng mô hình chủ-tớ (Master-Slave) đơn giản.
Dù mô hình này đảm bảo tính toàn vẹn của dữ liệu không bị mất mát, nhưng hiệu suất đọc giảm mạnh khiến trải nghiệm người dùng kém đi đáng kể. Ban đầu chúng tôi nghi ngờ việc cấu hình phân tách ghi-đọc (Read-Write Separation) trong ứng dụng Tomcat đã được thiết lập sai, dẫn đến tất cả các thao tác đều dồn về máy chủ chính, gây quá tải nghiêm trọng cho node Master.
Quy trình chẩn đoán lỗi
Để xác định nguyên nhân gốc rễ, nhóm kỹ thuật đã thực hiện tuần tự kiểm tra các tài nguyên hệ thống:
1. Giám sát băng thông mạng
Sử dụng lệnh sar để theo dõi thông tin lưu lượng trên thẻ mạng với tần suất 1 giây:
sar -n DEV 1
| Thời gian | Interface | Packets Nhận/xung | Kbyte Nhận/xung |
|---|---|---|---|
| 23:05:27 | eth1 | 909.18 | 64.31 |
Dữ liệu hiển thị lưu lượng mạng nằm trong ngưỡng bình thường, không có dấu hiệu nghẽn cổ chai ở lớp kết nối vật lý.
2. Kiểm tra tài nguyên CPU
Sử dụng lệnh top quan sát tỷ lệ sử dụng CPU trên server 8 nhân:
top -b -n 1 | head -n 20
Kết quả cho thấy tổng mức tiêu thụ tài nguyên xử lý rất thấp, hầu hết các lõi đều duy trì ở trạng thái idle (rảnh), khoảng dưới 1% sử dụng. Điều này loại trừ khả năng thiếu hụt sức mạnh tính toán.
3. Phân tích bộ nhớ vật lý
Thực thi lệnh free -m để kiểm tra dung lượng RAM:
free -m
| Tổng | Dùng | Trống | +/- Buffers/Cache |
|---|---|---|---|
| 15950 | 15338 | 612 | 1307 |
Hiện tượng bộ nhớ gần như cạn kiệt xảy ra do MongoDB không tự động giải phóng cache. Chúng tôi thực hiện thủ công lệnh làm sạch bộ đệm:
sync; echo 3 > /proc/sys/vm/drop_caches
Mặc dù bộ nhớ được trả lại (~5GB), nhưng sự chậm trễ khi đọc vẫn tiếp tục diễn ra.
4. Phát hiện nghẽn cổ chai I/O Ổ cứng
Dựa trên các triệu chứng còn lại, nghi vấn chuyển sang hoạt động truy xuất đĩa. Sử dụng iostat để quan sát chỉ số tải ổ cứng:
iostat -x 1
| Thiết bị | Rq/s | Wq/s | %Util |
|---|---|---|---|
| sda | 0.00 | 0.00 | 0.00 |
| sdb | 0.00 | 5.00 | 99.10 |
Lỗi được xác định là ở khối đĩa sdb, với tỷ lệ sử dụng phần trăm đạt mức 99%, gây ra độ trễ cực cao trong việc xử lý yêu cầu truy cập dữ liệu.
5. Trạng thái thực thi của MongoDB
Kết nối vào Mongo Stat để xem xét chi tiết hoạt động:
mongostat -h localhost:27017 --rowcount 20 1
Quan sát các chỉ số cho thấy thao tác write (ghi) diễn ra liên tục, dồn ép vào node chính, trong khi read (đọc) cũng đang cố gắng vượt qua cùng một nút cổ chai. Nguyên nhân cốt lõi là ứng dụng phía client (Java) chưa phân luồng giao dịch đọc sang node phụ (Secondary).
Cơ chế giải quyết: Triển khai Replica Set
Thay vì mô hình Master-Slave cũ, chúng tôi quyết định nâng cấp lên Replica Set để đảm bảo tính sẵn sàng cao và hỗ trợ phân tán tải đọc hiệu quả hơn. Trong mô hình này:
- Primary Node: Tiếp nhận mọi thao tác ghi và phân phát bản ghi sang các node khác.
- Secondary Node: Lưu trữ bản sao dữ liệu và cung cấp dịch vụ đọc nếu được cấu hình.
- Arbiter Node: Tham gia bỏ phiếu trong quá trình bầu chọn Primary, không lưu trữ dữ liệu.
Hướng dẫn triển khai chi tiết
Dưới đây là quy trình thiết lập cụm thử nghiệm trên môi trường Ubuntu.
Bước 1: Chuẩn bị thư mục và quyền hạn
# Tạo đường dẫn cho các node
mkdir -p /opt/mongo/{primary,data}/{secondary,data}/{arbiter,data}
mkdir -p /opt/mongo/logs
# Cấp quyền cho thư mục
chmod -R 775 /opt/mongo
Bước 2: Cấu hình file cấu hình (YAML/Conf)
Tạo file mongo-node-primary.conf:
systemLog:
destination: file
logAppend: true
path: /opt/mongo/logs/primary.log
storage:
dbPath: /opt/mongo/primary/data
directoryPerDB: true
net:
port: 27017
bindIp: 172.16.1.100
processManagement:
fork: true
replication:
replSetName: "ClusterRS"
security:
keyFile: /opt/mongo/keyfile # Nếu có authentication
Tương tự tạo file cho mongo-node-secondary.conf (đổi port thành 27018) và mongo-node-arbiter.conf (đổi port thành 27019).
Bước 3: Khởi động các service
cd /usr/local/bin
./mongod -f /opt/mongo/conf/mongo-node-primary.conf
./mongod -f /opt/mongo/conf/mongo-node-secondary.conf
./mongod -f /opt/mongo/conf/mongo-node-arbiter.conf
Bước 4: Khởi tạo cấu hình Cluster
Kết nối vào terminal điều khiển Primary để thiết lập tập hợp sao chép:
use admin
var config = {
_id: "ClusterRS",
members: [
{ _id: 0, host: "172.16.1.100:27017", priority: 2 },
{ _id: 1, host: "172.16.1.100:27018", priority: 1 },
{ _id: 2, host: "172.16.1.100:27019", arbiterOnly: true }
]
};
rs.initiate(config);
Đợi vài phút để hệ thống đồng bộ hóa. Kiểm tra trạng thái:
rs.status()
Nếu thành công, bạn sẽ thấy trạng thái node Primary báo "stateStr": "PRIMARY" và Secondary là "SECONDARY".
Bước 5: Bật chức năng đọc cho Secondary
Mặc định, các node Secondary không chấp nhận truy vấn đọc để tránh inconsistency. Để kích hoạt:
use admin
rs.slaveOk()
// Hoặc trong phiên làm việc mới
db.getMongo().setReadPref('secondary')
Bước 6: Xác minh phân tách tải
Thực hiện thao tác truy vấn trên ứng dụng và giám sát bằng mongostat trên từng cổng:
# Theo dõi trên cổng Primary (27017)
mongostat -h 172.16.1.100:27017 1
Và so sánh với:
# Theo dõi trên cổng Secondary (27018)
mongostat -h 172.16.1.100:27018 1
Trong bảng kết quả, nếu cột query tăng lên trên node Secondary trong khi node Primary chịu tải insert/update, chứng tỏ cơ chế phân tải đã hoạt động.