Replica set trong MongoDB

Giới thiệu

Replica set là một hệ replication trong MongoDB. Tập data sẽ được nhân bản trên nhiều server thay vì tập trung trên một single server. Nhờ vậy, replica set cung cấp tính năng high availability và dự phòng. Nó cũng scale read request cho mongodb. Mô hình của replica set trong mongodb gần giống replication trong mysql.

Tổng quan

alt text

Một replica set chỉ có duy nhất một primary. Primary member sẽ nhận các write request. Primary ghi các thay đổi của nó vào oplog – một file có vai trò như binlog trên mysqld. Các secondary sẽ apply từ primary nên có chung data set với primary. Read request có thể scale trên primary và tất cả các secondary. Một replica set có thể có tối đa là 50 member. Nếu lớn hơn 50 member thì bạn phải dùng giải pháp khác. Mongodb có đề nghị giải pháp master-slave replication cho môi trường lớn hơn 50 member nhưng tôi chưa tìm hiểu về giải pháp này.

Giữa các member trong replica set luôn duy trì kết nối heartbeat nên khi một member nào đó down các member còn lại sẽ nhận ra luôn và tự động tiến hành failover. Đây là điểm khác biệt so với mysql. Mysql replication thiếu cơ chế để tự động failover ( Trong các phiên bản mysql từ 5.6 hỗ trợ GTID thì mysql bắt đầu có thể failover tự động nhưng để thực hiện được, bạn cần sử dụng một node giám sát như mysqlfailover còn trong mongodb thì các member tự giám sát và tự failover )

alt text

Cơ chế thực hiện failover của replica set là dựa trên voting. Một secondary sẽ được bầu lên làm primary của cả replica set. Để voting thành công thì số member trong một replica set phải là số lẻ nếu không sẽ xảy ra trường hợp hai ứng viên đều nhận được số phiếu bầu bằng nhau rốt cục chẳng ai làm primary cả hoặc có thể dẫn đến tình huống có hai member đều tự nhận là primary nếu network partition xảy ra.

Tuy vậy, để giảm chi phí đầu tư, bạn có thể chỉ cần hai member trong một replica set. Thành viên thứ ba sẽ là một arbiter. Arbiter là một member đặc biệt. Nó không apply các oplog từ primary nên nó không lưu data gì cả. Nhiệm vụ của arbiter là giám sát hệ replica set qua các đường liên kết heartbeat và bầu chọn một secondary lên primary khi failover. Arbiter hoạt động không có gì nặng nề, bạn không cần server riêng cho arbiter nhưng arbiter không nên được deploy trên server dùng làm primary hay secondary trong replica set. Arbiter thì sẽ mãi là arbiter không như primary có thể down rồi trở thành secondary khi join lại vào replica set hay secondary có thể trở thành primary khi failover xảy ra.

alt text

Replication từ primary về secondary là bất đồng bộ (asynchronous) do đó writeset có trên primary sẽ không thể ngay lập tức có trên secondary. Hệ quả là data set trên secondary sẽ không phản ánh trạng thái mới nhất của data set trên primary. Thực ra đây là nhược điểm đều có trong mọi hệ thống mà dựa trên replication.

(Mysql thì rõ ràng cũng có nhược điểm này. Tham số second_behind_master trên slave status có thể cho biết mức độ sai biệt giữa slave và master trong hệ mysql replication. Ngay cả với galera thì dù cho tuyên bố là replication đồng bộ (synchronous) nhưng việc apply các writeset được replicate về lại asynchronous nên rốt cục vẫn có khả năng data set giữa các node trong hệ galera không được đồng bộ. Để khắc phục, galera có cung cấp cấu hình wsrep_causal_reads để ép read request phải đợi cho đến khi tất cả các replication data trong slave queue được apply hết)

Read preference

Mặc định, driver mongodb sẽ luôn thực hiện read request đến master. Read request chỉ từ primary gọi là strict consistency với ý nghĩa application sẽ luôn lấy data state mới nhất. Nếu application không yêu cầu luôn trả về phiên bản mới nhất của data thì bạn có thể điều chỉnh read preference của mongodb để scale out read. Read từ secondary gọi là eventual consistency với ý nghĩa rồi cuối cùng thì thế nào data state trên secondary cũng đồng nhất với primary. Read preference có 5 mode tất cả:
http://docs.mongodb.org/manual/core/read-preference/#read-preference-modes

  • primary là mặc định, mọi read request sẽ chỉ đi đến primary
  • primaryPreferred: mọi read request sẽ đi đến primary nhưng nếu primary down nó sẽ đi đến secondary
  • secondary: mọi read request chỉ đi đến các secondary.
  • secondaryPreferred: mọi read request sẽ đi đến secondary nhưng nếu tất cả các secondary down nó sẽ đi đến primary.
  • nearest: read request sẽ đến member có network latency thấp nhất không phân biết member đó là primary hay secondary.

Tham số read preference bạn sẽ khai báo từ application. Chi tiết tham khảo ở đây:
https://docs.mongodb.org/manual/reference/connection-string/

Write concern

Write concern sẽ yêu cầu mongodb xác nhận một write request có thành công hay không. Với replica set, mặc định write concern sẽ chỉ yêu cầu mongodb xác nhận write request trên primary member. Ở chế độ này, application chỉ có thể biết được write request có thành công hay không trên primary. Nó không thể biết được liệu write request đó đã được replicate thành công đến các secondary member hay chưa.

alt text

Mongodb cho phép bạn thay đổi hành vi mặc định của write concern. Application có thể lựa chọn thay đổi write concern ngay trong write request. Hành vi write concern thay đổi chỉ áp dụng cho write request này:

db.products.insert(
   { item: "envelopes", qty : 100, type: "Clasp" },
   { writeConcern: { w: 2, wtimeout: 5000 } }
)

Write request insert một item vào collection products sẽ được trả về sau khi write request thực hiện trên primary và ít nhất một secondary (w: 2) hoặc nếu không có kết quả trả về thì kết thúc trong vòng 5s (wtimeout: 5000). Giá trị wtimeout để tránh cho write request bị block lại quá lâu nếu như không có đủ số members mà write concern cần.

Bạn cũng có thể thay đổi hành vi mặc định của write concern trên phạm vi toàn bộ các write request thay vì chỉ từng request như trên:

cfg = rs.conf()
cfg.settings = {}
cfg.settings.getLastErrorDefaults = { w: "majority", wtimeout: 5000 }
rs.reconfig(cfg)

Cấu hình trên sẽ yêu cầu mongodb trả về kết quả sau khi write request được hoàn thành trên majority member hoặc trả về sau 5s. Majority members là phần member chiếm đa số trong một replica set. Ví dụ một replica set có 5 node thì majority member sẽ bao gồm 1 primary và ít nhất 2 secondary ( Tổng số sẽ là 3 trên 5 ). Majority trong write concern đề cập sẽ luôn có primary vì write request luôn đập vào primary member trước tiên.

Từ vị trí application, nếu bạn không áp dụng write concern đủ thì có thể dẫn đến write set bị mất sau khi failover: Giả sử application dùng write concern mặc định nên kết quả trả về ngay khi primary xử lý xong write set nhưng có thể vì lý do nào đó, write set đó chưa được replicate sang secondary. Application không biết điều này. Không may, tại thời điểm đó, primary down, failover được thực hiện tự động, một secondary lên làm primary nhưng secondary này sẽ không có write set đó. Đó là cách dữ liệu bị mất khi dùng write concern không đầy đủ.

Các loại secondary member

Priority 0 replica set member

Là một secondary member không bao giờ có thể trở thành primary được. Nó cũng không thể trigger một event election nhưng vẫn có thể tham gia vào quá trình voting. Vai trò của priority 0 member giống như một standby member được dùng để thay thế các secondary member mà bị unavailable. Một trường hợp khác mà priority 0 member được dùng là khi các member có thông số hardware mạnh yếu khác nhau. Những member khỏe mạnh thì có thể được đôn lên làm primary khi failover. Những member yếu hơn chút thì có thể giữ ở vị trí secondary member.

Hidden replica set member

Là một priority 0 member nên nó không thể trở thành primary nhưng vẫn tham gia vào quá trình voting. Điểm khác biệt là nó hoàn toàn ẩn đi với client. Client sẽ không gửi read request đến hidden member do đó ngoài replication, trên hidden member không còn traffic nào khác. Bạn có thể sử dụng hidden member trong vài trò backup hoặc reporting server. Tuy vậy, sử dụng nó với vai trò backup thì có rủi ro vì nó luôn là một bản sao của primary. Giả sử, một developer lỡ tay thực hiện remove document khỏi collection trên primary mà quên không truyền điều kiện vậy là ôi thôi đi tong cả collection. Tệ hơn, một system admin vô ý gõ nhầm drop collection trên primary. Cả hai tình huống đó, backup trên hidden member không thể cứu vãn được. Đó thực sự là thảm họa.

Delay replica set member

Là một priority 0, hidden member, tham gia vào quá trình voting khi failover. Đây là loại member sẽ giải quyết hai tình huống mà hidden member bó tay ở trên. Delay member cũng replicate data từ oplog của primary như các secondary khác nhưng có khác một chút là data set của delay member luôn cũ hơn của primary một khoảng thời gian nhất định. Khoảng thời gian này gọi là slaveDelay có thể cấu hình được. Như vậy, nếu một sự cố vô ý mất dữ liệu do thao tác sai lúc 9h sáng thì chúng ta vẫn còn data set nguyên vẹn của thời điểm 8h sáng ( nếu slaveDelay là 3600s ). Tuy lượng data backup bị hụt đi so với data hiện tại khoảng gần 1 tiếng nhưng dù sao khi sự cố xảy ra bạn cũng không mất hết. Delay member có ưu việt hơn hidden member không ? Tôi nghĩ là không. Mỗi loại member giải quyết một bài toán khác nhau. Nếu tình huống là primary bị hỏng ổ cứng, kernel panic… thì hidden member tỏ ra ưu việt hơn delay member khi vẫn giữ được gần như toàn bộ data set.

Bài viết chỉ điểm qua vài ý chính trong tài liệu mongo và sau đó được diễn giải theo ý hiểu của tôi. Tài liệu mongo thực sự rất chi tiết và thuận lợi cho người mới tiếp cận. Khi nào rảnh, tôi sẽ viết tiếp vài phần hay hay đọc từ tập tài liệu đó.

Nguồn tham khảo
http://docs.mongodb.org/manual/

(nguồn https://kipalog.com/posts/Replica-set-trong-MongoDB)

https://kipalog.com/posts/Gioi-thieu-MongoDB

Leave a Reply

%d bloggers like this: