Cách dùng tính năng backup và replication của Mnesia
Erlang
12
White

Ngoc Dao viết ngày 21/03/2016

Backup dĩ nhiên là tính năng mọi cơ sở dữ liệu đều phải có. Để cải thiện độ tin cậy hoặc tăng tốc độ truy cập của cơ sở dữ liệu người ta thường cần dùng tính năng replication, ví dụ PostgreSQL hơn MySQL nhiều mặt, nhưng chỉ vì lúc trước PostgreSQL chưa có sẵn tính năng replication mà nhiều người phải bấm bụng dùng MySQL.

Erlang có sẵn Mnesia là cơ sở dữ liệu phân tán, hỗ trợ sẵn multi-master thông qua chiêu thức Xác nhận hai pha. Bài viết này giới thiệu cách dùng tính năng này, để hiểu bạn cần đọc qua ít nhất một quyển sách Erlang.

Các loại replication

2 loại thường gặp là:

  • Master/slave: Chỉ có 1 master phụ trách việc ghi, còn việc đọc thì do nhiều slave phụ trách. Dữ liệu sau khi ghi ở master thì được replicate ở slave.
  • Multi-master: Mọi máy đều phụ trách cả đọc và ghi. Khi dữ liệu được cập nhật ở một máy nào đó, nó được replicate trên các máy còn lại.

Multi-master cao cấp hơn master/slave, nhưng master/slave thường gặp hơn vì 2 lí do:

  • Dữ liệu thường được đọc hơn là ghi, nhất là đối với web. Ví dụ theo nhiều thống kê thì khi 100 người ghé trang forum nào đó, thường chỉ có 1 người đăng bài, 99 người còn lại chỉ đọc bài của người khác.
  • Đơn giản chỉ vì chương trình cơ sở dữ liệu kém, chưa hỗ trợ multi-master.

Các loại bảng

Mnesia hơi khác và hơi giống nhiều loại cơ sở dữ liệu thông dụng. Ví dụ khác PostgreSQL ở chỗ mỗi node của Erlang chỉ có 1 cơ sở dữ liệu Mnesia, trong khi trên mỗi server PostgreSQL có thể có nhiều cơ sở dữ liệu. Ví dụ giống MySQL ở chỗ Mnesia hỗ trợ 3 loại bảng:

  • ram_copies: tất cả dữ liệu đều nằm trong RAM
  • disc_copies: dữ liệu nằm trên đĩa, nhưng được copy vào RAM để đọc cho nhanh
  • disc_only_copies thì dữ liệu chỉ nằm trên đĩa nên tốc độ rất chậm

Sau khi tạo bảng, tuỳ nhu cầu ta có thể đổi từ loại này sang loại khác. Khi các node bắt đầu được nối với nhau, Mnesia sẽ kiểm tra schema của các bảng trên các node có tương thích với nhau không, nếu không nó sẽ báo lỗi, nếu có nó sẽ merge dữ liệu của các bảng với nhau.

Thí nghiệm

Ta sẽ chạy 2 node, tạo dữ liệu ở node n1 rồi replicate sang node n2.

Từ console, chạy lệnh để tạo node n1:

erl -sname n1 -setcookie secret

Trước khi start cơ sở dữ liệu, cần khởi tạo (giống lệnh initdb của PostgreSQL). Chú ý phải cơ sở dữ liệu phải stop khi ta khởi tạo.

(n1@dhcp-10-30-255-66)> mnesia:create_schema([node()]).
ok

Phần @dhcp-10-30-255-66 là do Erlang tự động nhận biết. Lúc này thư mục Mnesia.n1@dhcp-10-30-255-66 chứa các bảng loại disc_copies vừa được tạo ra. Ta start rồi xem thông tin:

(n1@dhcp-10-30-255-66)> mnesia:start().
ok

(n1@ngoc-2)> mnesia:info().
running db nodes = ['n1@ngoc-2']
stopped db nodes = []
master node tables = []
remote = []
ram_copies = []
disc_copies = [schema]
disc_only_copies = []
[{'n1@ngoc-2',disc_copies}] = [schema]

Như vậy bảng schema dùng để chứa thông tin về các bảng khác vừa được tự động tạo ra. Ta thử tạo bảng rồi ghi và đọc dữ liệu:

(n1@dhcp-10-30-255-66)> mnesia:create_table(foo, [{disc_copies, [node()]}]).
{atomic,ok}

(n1@dhcp-10-30-255-66)> mnesia:dirty_write({foo, hello, world}).
ok

(n1@dhcp-10-30-255-66)> mnesia:dirty_read(foo, hello).
[{foo,hello,world}]

Tạo thử backup:

(n1@dhcp-10-30-255-66)> mnesia:backup("090909.backup").
ok

Trên đĩa sẽ xuất hiện tập tin 090909.backup, tất cả các bảng đã được backup vào đây.

Thử restore:

(n1@dhcp-10-30-255-66)> mnesia:restore("090909.backup", []).
{atomic, [foo]}

Chú ý trước khi restore thì các bảng phải tồn tại. Tham khảo thêm mailing list.

Bây giờ mới đến phần thú vị nhất.

Mở thêm console nữa, chạy lệnh để tạo node n2:

erl -sname n2 -setcookie secret

Ta không cần khởi tạo và thật ra không thể khởi tạo cơ sở dữ liệu loại disc_copies ở node này, Mnesia sẽ báo lỗi khi các node nối với nhau (đọc phần More Schema Management trang 45 của tài liệu ở phần Tham Khảo để biết lí do). Thay vì thế, ta tạo cơ sở dữ liệu ở node n2 bằng cách replicate từ node n1 sang.

Start Mnesia, rồi nối các node với nhau:

(n2@dhcp-10-30-255-66)> mnesia:start().
ok

(n2@dhcp-10-30-255-66)> mnesia:change_config(extra_db_nodes, ['n1@dhcp-10-30-255-66']).
{ok,['n1@dhcp-10-30-255-66']}

Chú ý phải chỉ định tên node n1 đầy đủ cả phần @. Ta xem thông tin:

(n2@dhcp-10-30-255-66)> mnesia:info().
running db nodes = ['n1@dhcp-10-30-255-66','n2@dhcp-10-30-255-66']
stopped db nodes = []
master node tables = []
remote = [foo]
ram_copies = [schema]
disc_copies = []
disc_only_copies = []
[{'n1@dhcp-10-30-255-66',disc_copies}] = [foo]
[{'n1@dhcp-10-30-255-66',disc_copies},{'n2@dhcp-10-30-255-66',ram_copies}] = [schema]

(n2@dhcp-10-30-255-66)> mnesia:system_info(running_db_nodes).
['n1@dhcp-10-30-255-66','n2@dhcp-10-30-255-66']

(n2@dhcp-10-30-255-66)> mnesia:table_info(schema, active_replicas).
['n1@dhcp-10-30-255-66','n2@dhcp-10-30-255-66']

(n2@dhcp-10-30-255-66)> mnesia:table_info(foo, active_replicas).
['n1@dhcp-10-30-255-66']

Như vậy node n2 hiện chỉ có bảng schema loại ram_copies và nó là bảng đặc biệt nên luôn được Mnesia tự động replicate (replica là thuật ngữ dùng để chỉ cái được replicate) từ node n1 sang!

Bảng foo hiện chỉ nằm ở node n1, từ node n2 ta thử đọc bảng này xem có lỗi gì không:

(n2@dhcp-10-30-255-66)> mnesia:dirty_read(foo, hello).
[{foo,hello,world}]

Wow! Đây chính là sức mạnh của Erlang.

Bây giờ ta replicate bảng foo.

(n2@dhcp-10-30-255-66)> mnesia:add_table_copy(foo, node (), disc_copies). 
{aborted,{combine_error,foo,"has no disc", 'n2@dhcp-10-30-255-66'}}

Oài, không được rồi. Ta thử replicate lại bằng loại ram_copies:

(n2@dhcp-10-30-255-66)> mnesia:add_table_copy(foo, node (), ram_copies). 
{atomic,ok}

(n2@dhcp-10-30-255-66)> mnesia:info().
running db nodes = ['n1@dhcp-10-30-255-66','n2@dhcp-10-30-255-66']
stopped db nodes = []
master node tables = []
remote = []
ram_copies = [foo,schema]
disc_copies = []
disc_only_copies = []
[{'n1@dhcp-10-30-255-66',disc_copies},{'n2@dhcp-10-30-255-66',ram_copies}] = [schema,foo]

Chỉ định ram_copies thì replicate được rồi, nhưng dữ liệu nằm trong RAM nên nếu node n1 bị hỏng ổ cứng thì rất phiền. Ta thử đổi kiểu của foo từ ram_copies sang disc_copies:

(n2@dhcp-10-30-255-66)> mnesia:change_table_copy_type(foo, node(), disc_copies).   
{aborted,{has_no_disc,'n2@dhcp-10-30-255-66'}}

Vẫn không được.

Mấu chốt là cần đổi kiểu của schema từ ram_copies sang disc_copies trước để thư mục Mnesia.n2@dhcp-10-30-255-66 được tạo ra:

(n2@dhcp-10-30-255-66)> mnesia:change_table_copy_type(schema, node(), disc_copies).
{atomic,ok}

(n2@dhcp-10-30-255-66)> mnesia:info().
running db nodes = ['n1@dhcp-10-30-255-66','n2@dhcp-10-30-255-66']
stopped db nodes = []
master node tables = []
remote = []
ram_copies = [foo]
disc_copies = [schema]
disc_only_copies = []
[{'n1@dhcp-10-30-255-66',disc_copies},{'n2@dhcp-10-30-255-66',disc_copies}] = [schema]
[{'n1@dhcp-10-30-255-66',disc_copies},{'n2@dhcp-10-30-255-66',ram_copies}] = [foo]

Sau đó mới làm ăn được:

(n2@dhcp-10-30-255-66)> mnesia:change_table_copy_type(foo, node(), disc_copies).   
{atomic,ok}

(n2@dhcp-10-30-255-66)> mnesia:info().
running db nodes = ['n1@dhcp-10-30-255-66','n2@dhcp-10-30-255-66']
stopped db nodes = []
master node tables = []
remote = []
ram_copies = []
disc_copies = [foo,schema]
disc_only_copies = []
[{'n1@dhcp-10-30-255-66',disc_copies},{'n2@dhcp-10-30-255-66',disc_copies}] = [schema,foo]

(n2@dhcp-10-30-255-66)> mnesia:table_info(foo, active_replicas).
['n2@dhcp-10-30-255-66','n1@dhcp-10-30-255-66']

Đến đây toàn bộ các bảng đã được replicate. Ta có thể nối thêm nhiều node nữa cũng được, tất cả đều bình đẳng và đều là master.

Lần sau sau khi bật node, chỉ cần mnesia:start() là tự động các node được nối với nhau và bảng của các node được đồng bộ hóa với nhau mà không cần mnesia:change_config(extra_db_nodes, Nodes)! Bí mật của điều kì diệu này là metadata về các node và các bảng đã được lưu ở bảng schema.

Tham Khảo

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Ngoc Dao

102 bài viết.
252 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
56 6
Làm thế nào để nâng cấp trang web mà không làm gián đoạn dịch vụ? Đây là câu hỏi phỏng vấn các công ty lớn thường hỏi khi bạn xin vào vị trí làm lậ...
Ngoc Dao viết 2 năm trước
56 6
White
32 0
Bài viết này giải thích sự khác khác nhau giữa hai ngành khoa học máy tính (computer science) và kĩ thuật phần mềm (software engineering), hi vọng ...
Ngoc Dao viết gần 2 năm trước
32 0
White
28 1
Nếu là team leader, giám đốc công ty hay tướng chỉ huy quân đội, vấn đề cơ bản bạn gặp phải là “hướng mọi người đi theo con đường bạn chỉ ra”. Thử...
Ngoc Dao viết gần 2 năm trước
28 1
Bài viết liên quan
White
3 0
Chú thích: Bài này đăng lần đầu năm 2009 để chia sẻ kinh nghiệm, sau khi tác giả viết xong thư viện closed source để bán. Hiện tại năm 2016 đã có t...
Ngoc Dao viết gần 2 năm trước
3 0
White
2 0
Trong xử lí song song, các đơn vị thực hiện nhiệm vụ xử lí thường gặp là: thread, process, core, CPU, node. Thường mỗi node ứng với một server vật ...
Ngoc Dao viết gần 2 năm trước
2 0
White
3 0
Khi xây dựng hệ thống server phân tán chạy song song trên nhiều node, ta thường gặp bài toán có pattern sau: Có 3 node, mỗi node chạy một process ...
Ngoc Dao viết gần 2 năm trước
3 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
102 bài viết.
252 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!