Gần đây, chúng tôi đã gặp phải một trường hợp hỗ trợ khách hàng thú vị liên quan đến thiết lập sao chép MariaDB. Chúng tôi đã dành rất nhiều thời gian để nghiên cứu vấn đề này và nghĩ rằng rất đáng để chia sẻ điều này với bạn trong bài đăng trên blog này.
Mô tả Môi trường của Khách hàng
Sự cố như sau:một máy chủ MariaDB cũ (trước 10.x) đang được sử dụng và một nỗ lực đã được thực hiện để di chuyển dữ liệu từ nó sang thiết lập sao chép MariaDB gần đây hơn. Điều này dẫn đến các vấn đề với việc sử dụng Mariabackup để xây dựng lại nô lệ trong cụm sao chép mới. Với mục đích của các bài kiểm tra, chúng tôi đã tạo lại hành vi này trong môi trường sau:
Dữ liệu đã được di chuyển từ 5.5 sang 10.4 bằng mysqldump:
mysqldump --single-transaction --master-data=2 --events --routines sbtest > /root/dump.sql
Điều này cho phép chúng tôi thu thập tọa độ nhật ký nhị phân chính và kết xuất nhất quán. Do đó, chúng tôi có thể cung cấp nút chính MariaDB 10.4 và thiết lập bản sao giữa nút chính 5.5 cũ và nút 10.4 mới. Lưu lượng vẫn chạy trên nút 5.5. 10.4 chính đang tạo GTID vì nó phải sao chép dữ liệu sang 10.4 phụ. Trước khi tìm hiểu chi tiết, hãy xem nhanh cách hoạt động của GTID trong MariaDB.
MariaDB và GTID
Đối với người mới bắt đầu, MariaDB sử dụng định dạng GTID khác với Oracle MySQL. Nó bao gồm ba số được phân tách bằng dấu gạch ngang:
0 - 1 - 345
Đầu tiên là miền sao chép, cho phép sao chép đa nguồn được xử lý đúng cách. Điều này không liên quan đến trường hợp của chúng tôi vì tất cả các nút đều nằm trong cùng một miền sao chép. Số thứ hai là ID máy chủ của nút đã tạo GTID. Thứ ba là số thứ tự - nó tăng đơn điệu với mọi sự kiện được lưu trữ trong nhật ký nhị phân.
MariaDB sử dụng một số biến để lưu trữ thông tin về GTID được thực thi trên một nút nhất định. Điều thú vị nhất đối với chúng tôi là:
Gtid_binlog_pos - theo tài liệu, biến này là GTID của nhóm sự kiện cuối cùng được ghi vào nhật ký nhị phân.
Gtid_slave_pos - theo tài liệu, biến hệ thống này chứa GTID của giao dịch cuối cùng được các luồng phụ của máy chủ áp dụng cho cơ sở dữ liệu.
Gtid_current_pos - theo tài liệu, biến hệ thống này chứa GTID của giao dịch cuối cùng được áp dụng cho cơ sở dữ liệu. Nếu server_id của GTID tương ứng trong gtid_binlog_pos bằng server_id của chính máy chủ và số thứ tự cao hơn GTID tương ứng trong gtid_slave_pos, thì GTID từ gtid_binlog_pos sẽ được sử dụng. Nếu không, GTID từ gtid_slave_pos sẽ được sử dụng cho miền đó.
Vì vậy, để làm rõ ràng, gtid_binlog_pos lưu trữ GTID của sự kiện được thực thi cục bộ cuối cùng. Gtid_slave_pos lưu trữ GTID của sự kiện được thực thi bởi chuỗi nô lệ và gtid_current_pos hiển thị giá trị từ gtid_binlog_pos, nếu nó có số thứ tự cao nhất và nó có server-id hoặc gtid_slave_pos nếu nó có chuỗi cao nhất. Hãy ghi nhớ điều này trong tâm trí của bạn.
Tổng quan về Vấn đề
Trạng thái ban đầu của các biến có liên quan là trên 10.4 master:
MariaDB [(none)]> show global variables like '%gtid%';
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| gtid_binlog_pos | 0-1001-1 |
| gtid_binlog_state | 0-1001-1 |
| gtid_cleanup_batch_size | 64 |
| gtid_current_pos | 0-1001-1 |
| gtid_domain_id | 0 |
| gtid_ignore_duplicates | ON |
| gtid_pos_auto_engines | |
| gtid_slave_pos | 0-1001-1 |
| gtid_strict_mode | ON |
| wsrep_gtid_domain_id | 0 |
| wsrep_gtid_mode | OFF |
+-------------------------+----------+
11 rows in set (0.001 sec)
Xin lưu ý rằng gtid_slave_pos, về mặt lý thuyết, không có ý nghĩa gì - nó đến từ cùng một nút nhưng thông qua chuỗi nô lệ. Điều này có thể xảy ra nếu bạn thực hiện chuyển đổi chính trước đó. Chúng tôi đã làm điều đó - với hai nút 10,4, chúng tôi đã chuyển các máy chủ từ máy chủ có ID máy chủ là 1001 sang máy chủ có ID máy chủ là 1002 và sau đó quay lại 1001.
Sau đó, chúng tôi đã định cấu hình bản sao từ 5.5 đến 10.4 và đây là cách mọi thứ trông như thế nào:
MariaDB [(none)]> show global variables like '%gtid%';
+-------------------------+-------------------------+
| Variable_name | Value |
+-------------------------+-------------------------+
| gtid_binlog_pos | 0-55-117029 |
| gtid_binlog_state | 0-1001-1537,0-55-117029 |
| gtid_cleanup_batch_size | 64 |
| gtid_current_pos | 0-1001-1 |
| gtid_domain_id | 0 |
| gtid_ignore_duplicates | ON |
| gtid_pos_auto_engines | |
| gtid_slave_pos | 0-1001-1 |
| gtid_strict_mode | ON |
| wsrep_gtid_domain_id | 0 |
| wsrep_gtid_mode | OFF |
+-------------------------+-------------------------+
11 rows in set (0.000 sec)
Như bạn có thể thấy, các sự kiện được sao chép từ MariaDB 5.5, tất cả chúng đều được tính trong biến gtid_binlog_pos:tất cả các sự kiện có ID máy chủ là 55. Điều này dẫn đến một vấn đề nghiêm trọng. Như bạn có thể nhớ, gtid_binlog_pos phải chứa các sự kiện được thực thi cục bộ trên máy chủ. Tại đây, nó chứa các sự kiện được sao chép từ một máy chủ khác với ID máy chủ khác.
Điều này khiến mọi thứ trở nên rắc rối khi bạn muốn xây dựng lại nô lệ 10.4, đây là lý do. Mariabackup, giống như Xtrabackup, hoạt động theo cách đơn giản. Nó sao chép các tệp từ máy chủ MariaDB trong khi quét nhật ký làm lại và lưu trữ bất kỳ giao dịch nào đến. Khi các tệp đã được sao chép, Mariabackup sẽ đóng băng cơ sở dữ liệu bằng cách sử dụng các BẢNG CHỨA FLUSH CÓ KHÓA ĐỌC hoặc khóa sao lưu, tùy thuộc vào phiên bản MariaDB và tính khả dụng của các khóa sao lưu. Sau đó, nó đọc GTID được thực thi mới nhất và lưu trữ nó cùng với bản sao lưu. Sau đó, khóa được phát hành và quá trình sao lưu hoàn tất. GTID được lưu trữ trong bản sao lưu phải được sử dụng làm GTID được thực thi mới nhất trên một nút. Trong trường hợp xây dựng lại nô lệ, nó sẽ được đặt dưới dạng gtid_slave_pos và sau đó được sử dụng để bắt đầu sao chép GTID. GTID này được lấy từ gtid_current_pos, điều này có ý nghĩa hoàn hảo - xét cho cùng thì nó là “GTID của giao dịch cuối cùng được áp dụng cho cơ sở dữ liệu”. Người đọc cấp tính đã có thể thấy vấn đề. Hãy hiển thị đầu ra của các biến khi 10.4 sao chép từ biến chính 5.5:
MariaDB [(none)]> show global variables like '%gtid%';
+-------------------------+-------------------------+
| Variable_name | Value |
+-------------------------+-------------------------+
| gtid_binlog_pos | 0-55-117029 |
| gtid_binlog_state | 0-1001-1537,0-55-117029 |
| gtid_cleanup_batch_size | 64 |
| gtid_current_pos | 0-1001-1 |
| gtid_domain_id | 0 |
| gtid_ignore_duplicates | ON |
| gtid_pos_auto_engines | |
| gtid_slave_pos | 0-1001-1 |
| gtid_strict_mode | ON |
| wsrep_gtid_domain_id | 0 |
| wsrep_gtid_mode | OFF |
+-------------------------+-------------------------+
11 rows in set (0.000 sec)
Gtid_current_pos được đặt thành 0-1001-1. Đây chắc chắn không phải là thời điểm chính xác, nó được lấy từ gtid_slave_pos trong khi chúng tôi có một loạt các giao dịch đến từ 5.5 sau đó. Vấn đề là các giao dịch đó được lưu trữ dưới dạng gtid_binlog_pos. Mặt khác, gtid_current_pos được tính theo cách nó yêu cầu ID máy chủ cục bộ cho GTID trong gitd_binlog_pos trước khi chúng có thể được sử dụng làm gtid_current_pos. Trong trường hợp của chúng tôi, chúng có ID máy chủ của nút 5.5 vì vậy chúng sẽ không được coi là sự kiện được thực thi trên chính 10.4 một cách chính xác. Sau khi khôi phục sao lưu, nếu bạn đặt nô lệ theo trạng thái GTID được lưu trữ trong bản sao lưu, nó sẽ áp dụng lại tất cả các sự kiện từ 5.5. Điều này, rõ ràng, sẽ phá vỡ sự sao chép.
Giải pháp
Giải pháp cho vấn đề này là thực hiện một số bước bổ sung:
- Dừng sao chép từ 5.5 đến 10.4. Chạy STOP SLAVE trên bản chính 10.4
- Thực hiện bất kỳ giao dịch nào vào ngày 10.4 - TẠO SCHEMA NẾU KHÔNG TỒN TẠI sửa lỗi - điều này sẽ thay đổi tình huống GTID như sau:
MariaDB [(none)]> show global variables like '%gtid%';
+-------------------------+---------------------------+
| Variable_name | Value |
+-------------------------+---------------------------+
| gtid_binlog_pos | 0-1001-117122 |
| gtid_binlog_state | 0-55-117121,0-1001-117122 |
| gtid_cleanup_batch_size | 64 |
| gtid_current_pos | 0-1001-117122 |
| gtid_domain_id | 0 |
| gtid_ignore_duplicates | ON |
| gtid_pos_auto_engines | |
| gtid_slave_pos | 0-1001-1 |
| gtid_strict_mode | ON |
| wsrep_gtid_domain_id | 0 |
| wsrep_gtid_mode | OFF |
+-------------------------+---------------------------+
11 rows in set (0.001 sec)
GITD mới nhất được thực thi cục bộ, vì vậy nó được lưu trữ dưới dạng gtid_binlog_pos. Vì nó có ID máy chủ cục bộ nên nó được chọn là gtid_current_pos. Bây giờ, bạn có thể tạo một bản sao lưu và sử dụng nó để xây dựng lại nô lệ khỏi 10.4 master. Sau khi hoàn tất, hãy bắt đầu lại chuỗi nô lệ.
MariaDB biết rằng loại lỗi này tồn tại, một trong những báo cáo lỗi liên quan mà chúng tôi tìm thấy là:https://jira.mariadb.org/browse/MDEV-10279 Rất tiếc, cho đến nay vẫn chưa có bản sửa lỗi . Những gì chúng tôi nhận thấy là vấn đề này ảnh hưởng đến MariaDB lên đến 5.5. Các sự kiện không phải GTID đến từ MariaDB 10.0 được tính toán chính xác trên 10.4 do đến từ chuỗi nô lệ và gtid_slave_pos được cập nhật đúng cách. MariaDB 5.5 là một phiên bản khá cũ (mặc dù nó vẫn được hỗ trợ) nên bạn vẫn có thể thấy các thiết lập đang chạy trên nó và cố gắng chuyển từ phiên bản MariaDB 5.5 sang phiên bản mới hơn, hỗ trợ GTID. Điều tồi tệ hơn, theo báo cáo lỗi mà chúng tôi tìm thấy, điều này cũng ảnh hưởng đến việc sao chép đến từ các máy chủ không phải MariaDB (một trong những nhận xét đề cập đến vấn đề hiển thị trên Máy chủ Percona 5.6) vào MariaDB.
Dù sao, chúng tôi hy vọng bạn thấy bài đăng trên blog này hữu ích và hy vọng bạn sẽ không gặp phải vấn đề mà chúng tôi vừa mô tả.