Bạn có thể gỡ bỏ cơ sở dữ liệu MySQL theo nhiều cách. Một số cách rõ ràng là tắt máy chủ, rút cáp nguồn hoặc cố gắng giết quá trình mysqld với SIGKILL để mô phỏng hành vi tắt MySQL không sạch. Nhưng cũng có những cách ít tinh tế hơn để cố tình làm hỏng máy chủ MySQL của bạn và sau đó xem loại phản ứng dây chuyền mà nó gây ra. Tại sao bạn muốn làm điều này? Thất bại và phục hồi có thể có nhiều trường hợp khó khăn, và hiểu chúng có thể giúp giảm yếu tố bất ngờ khi mọi thứ xảy ra trong quá trình sản xuất. Lý tưởng nhất là bạn muốn mô phỏng các lỗi trong môi trường được kiểm soát, sau đó thiết kế và kiểm tra các thủ tục chuyển đổi dự phòng cơ sở dữ liệu.
Có một số lĩnh vực trong MySQL mà chúng ta có thể giải quyết, tùy thuộc vào cách bạn muốn nó không thành công hoặc gặp sự cố. Bạn có thể làm hỏng không gian bảng, làm tràn bộ đệm và bộ đệm MySQL, giới hạn tài nguyên để làm đói máy chủ và cũng gây rối với các quyền. Trong bài đăng trên blog này, chúng tôi sẽ chỉ cho bạn một số ví dụ về cách làm hỏng máy chủ MySQL trong môi trường Linux. Một số trong số chúng sẽ phù hợp với ví dụ:Các phiên bản Amazon RDS, trong đó bạn sẽ không có quyền truy cập vào máy chủ cơ bản.
Giết, Giết, Giết, Chết, Chết, Chết
Cách dễ nhất để làm hỏng máy chủ MySQL là chỉ cần giết tiến trình hoặc máy chủ lưu trữ và không cho MySQL cơ hội thực hiện tắt máy một cách duyên dáng. Để mô phỏng sự cố mysqld, chỉ cần gửi tín hiệu 4, 6, 7, 8 hoặc 11 tới quy trình:
$ kill -11 $(pidof mysqld)
Khi xem nhật ký lỗi MySQL, bạn có thể thấy các dòng sau:
11:06:09 UTC - mysqld got signal 11 ;
This could be because you hit a bug. It is also possible that this binary
or one of the libraries it was linked against is corrupt, improperly built,
or misconfigured. This error can also be caused by malfunctioning hardware.
Attempting to collect some information that could help diagnose the problem.
As this is a crash and something is definitely wrong, the information
collection process might fail.
..
Attempting backtrace. You can use the following information to find out
where mysqld died. If you see no messages after this, something went
terribly wrong...
Bạn cũng có thể sử dụng kill -9 (SIGKILL) để kết thúc quá trình ngay lập tức. Có thể tìm thấy thêm chi tiết về tín hiệu Linux tại đây. Ngoài ra, bạn có thể sử dụng một cách nhẹ nhàng hơn về mặt phần cứng như rút cáp nguồn, nhấn nút khởi động lại cứng hoặc sử dụng thiết bị hàng rào để STONITH.
Kích hoạt OOM
MySQL phổ biến trong các dịch vụ đám mây như Amazon RDS và Google Cloud SQL không có cách nào dễ dàng để xử lý chúng. Thứ nhất vì bạn sẽ không nhận được bất kỳ quyền truy cập cấp hệ điều hành nào vào phiên bản cơ sở dữ liệu và thứ hai là vì nhà cung cấp sử dụng máy chủ MySQL được vá độc quyền. Có một cách là làm tràn một số bộ đệm và để trình quản lý hết bộ nhớ (OOM) khởi động quy trình MySQL.
Bạn có thể tăng kích thước bộ đệm sắp xếp lên một cái gì đó lớn hơn những gì RAM có thể xử lý và thực hiện một số truy vấn sắp xếp mysql đối với máy chủ MySQL. Hãy tạo một bảng 10 triệu hàng bằng cách sử dụng sysbench trên phiên bản Amazon RDS của chúng tôi, vì vậy chúng tôi có thể tạo một loại lớn:
$ sysbench \
--db-driver=mysql \
--oltp-table-size=10000000 \
--oltp-tables-count=1 \
--threads=1 \
--mysql-host=dbtest.cdw9q2wnb00s.ap-tokyo-1.rds.amazonaws.com \
--mysql-port=3306 \
--mysql-user=rdsroot \
--mysql-password=password \
/usr/share/sysbench/tests/include/oltp_legacy/parallel_prepare.lua \
run
Thay đổi sort_buffer_size lên 5G (phiên bản thử nghiệm của chúng tôi là db.t2.micro - 1GB, 1vCPU) bằng cách đi tới Bảng điều khiển Amazon RDS -> Nhóm thông số -> Tạo Nhóm thông số -> chỉ định tên nhóm -> Chỉnh sửa thông số -> chọn "sort_buffer_size" và chỉ định giá trị là 5368709120.
Áp dụng các thay đổi nhóm tham số bằng cách đi tới Phiên bản -> Hành động phiên bản -> Sửa đổi -> Tùy chọn cơ sở dữ liệu -> Nhóm tham số cơ sở dữ liệu -> và chọn nhóm tham số mới tạo của chúng tôi. Sau đó, khởi động lại phiên bản RDS để áp dụng các thay đổi.
Sau khi thiết lập, hãy xác minh giá trị mới của sort_buffer_size :
MySQL [(none)]> select @@sort_buffer_size;
+--------------------+
| @@sort_buffer_size |
+--------------------+
| 5368709120 |
+--------------------+
Sau đó, kích hoạt 48 truy vấn đơn giản yêu cầu phân loại từ máy khách:
$ for i in {1..48}; do (mysql -urdsroot -ppassword -h dbtest.cdw9q2wnb00s.ap-tokyo-1.rds.amazonaws.com -e 'SELECT * FROM sbtest.sbtest1 ORDER BY c DESC >/dev/null &); done
Nếu bạn chạy phần trên trên một máy chủ tiêu chuẩn, bạn sẽ nhận thấy máy chủ MySQL sẽ bị kết thúc và bạn có thể thấy các dòng sau xuất hiện trong nhật ký hệ điều hành hoặc dmesg:
[164199.868060] Out of memory: Kill process 47060 (mysqld) score 847 or sacrifice child
[164199.868109] Killed process 47060 (mysqld) total-vm:265264964kB, anon-rss:3257400kB, file-rss:0kB
Với systemd, MySQL hoặc MariaDB sẽ tự động được khởi động lại, Amazon RDS cũng vậy. Bạn có thể thấy thời gian hoạt động cho phiên bản RDS của chúng tôi sẽ được đặt lại về 0 (trong trạng thái mysqladmin) và giá trị 'Thời gian khôi phục gần đây nhất' (trong Bảng điều khiển RDS) sẽ được cập nhật vào thời điểm nó gặp sự cố.
Làm hỏng dữ liệu
InnoDB có không gian bảng hệ thống riêng để lưu trữ từ điển dữ liệu, bộ đệm và phân đoạn khôi phục bên trong một tệp có tên ibdata1. Nó cũng lưu trữ vùng bảng được chia sẻ nếu bạn không cấu hình innodb_file_per_table (được bật theo mặc định trong MySQL 5.6.6+). Chúng tôi chỉ có thể xóa tệp này, gửi hoạt động ghi và xóa các bảng để làm sập mysqld:
# empty ibdata1
$ cat /dev/null > /var/lib/mysql/ibdata1
# send a write
$ mysql -uroot -p -e 'CREATE TABLE sbtest.test (id INT)'
# flush tables
$ mysql -uroot -p -e 'FLUSH TABLES WITH READ LOCK; UNLOCK TABLES'
Sau khi bạn gửi thư, trong nhật ký lỗi, bạn sẽ thấy:
2017-11-15T06:01:59.345316Z 0 [ERROR] InnoDB: Tried to read 16384 bytes at offset 98304, but was only able to read 0
2017-11-15T06:01:59.345332Z 0 [ERROR] InnoDB: File (unknown): 'read' returned OS error 0. Cannot continue operation
2017-11-15T06:01:59.345343Z 0 [ERROR] InnoDB: Cannot continue operation.
Lúc này, mysql sẽ bị treo vì nó không thể thực hiện bất kỳ thao tác nào, và sau khi tuôn ra, bạn sẽ nhận được dòng "mysqld got signal 11" và mysqld sẽ tắt. Để dọn dẹp, bạn phải xóa ibdata1 bị hỏng, cũng như ib_logfile * vì không thể sử dụng tệp nhật ký làm lại với không gian bảng hệ thống mới sẽ được tạo bởi mysqld vào lần khởi động lại tiếp theo. Dự kiến sẽ mất dữ liệu.
Đối với bảng MyISAM, chúng ta có thể lộn xộn với .MYD (tệp dữ liệu MyISAM) và .MYI (chỉ mục MyISAM) trong bộ dữ liệu MySQL. Ví dụ:lệnh sau thay thế bất kỳ lần xuất hiện nào của chuỗi "F" bằng "9" bên trong tệp:
$ replace F 9 -- /var/lib/mysql/sbtest/sbtest1.MYD
Sau đó, gửi một số ghi (ví dụ:sử dụng sysbench) đến bảng đích và thực hiện xả:
mysql> FLUSH TABLE sbtest.sbtest1;
Thông tin sau sẽ xuất hiện trong nhật ký lỗi MySQL:
2017-11-15T06:56:15.021564Z 448 [ERROR] /usr/sbin/mysqld: Incorrect key file for table './sbtest/sbtest1.MYI'; try to repair it
2017-11-15T06:56:15.021572Z 448 [ERROR] Got an error from thread_id=448, /export/home/pb2/build/sb_0-24964902-1505318733.42/rpm/BUILD/mysql-5.7.20/mysql-5.7.20/storage/myisam/mi_update.c:227
Bảng MyISAM sẽ được đánh dấu là bị lỗi và cần chạy câu lệnh REPAIR TABLE để có thể truy cập lại bảng.
Giới hạn tài nguyên
Chúng tôi cũng có thể áp dụng giới hạn tài nguyên hệ điều hành cho quy trình mysqld của chúng tôi, ví dụ như số lượng bộ mô tả tệp đang mở. Sử dụng biến open_file_limit (mặc định là 5000) cho phép mysqld dự trữ các bộ mô tả tệp bằng lệnh setrlimit (). Bạn có thể đặt biến này tương đối nhỏ (chỉ đủ để mysqld khởi động) và sau đó gửi nhiều truy vấn đến máy chủ MySQL cho đến khi nó đạt đến giới hạn.
Nếu mysqld đang chạy trong máy chủ systemd, chúng tôi có thể đặt nó trong tệp đơn vị systemd nằm tại /usr/lib/systemd/system/mysqld.service và thay đổi giá trị sau thành giá trị thấp hơn (mặc định của systemd là 6000):
# Sets open_files_limit
LimitNOFILE = 30
Áp dụng các thay đổi cho systemd và khởi động lại máy chủ MySQL:
$ systemctl daemon-reload
$ systemctl restart mysqld
Sau đó, bắt đầu gửi các kết nối / truy vấn mới tính trong các cơ sở dữ liệu và bảng khác nhau để mysqld phải mở nhiều tệp. Bạn sẽ nhận thấy lỗi sau:
2017-11-16T04:43:26.179295Z 4 [ERROR] InnoDB: Operating system error number 24 in a file operation.
2017-11-16T04:43:26.179342Z 4 [ERROR] InnoDB: Error number 24 means 'Too many open files'
2017-11-16T04:43:26.179354Z 4 [Note] InnoDB: Some operating system error numbers are described at http://dev.mysql.com/doc/refman/5.7/en/operating-system-error-codes.html
2017-11-16T04:43:26.179363Z 4 [ERROR] InnoDB: File ./sbtest/sbtest9.ibd: 'open' returned OS error 124. Cannot continue operation
2017-11-16T04:43:26.179371Z 4 [ERROR] InnoDB: Cannot continue operation.
2017-11-16T04:43:26.372605Z 0 [Note] InnoDB: FTS optimize thread exiting.
2017-11-16T04:45:06.816056Z 4 [Warning] InnoDB: 3 threads created by InnoDB had not exited at shutdown!
Tại thời điểm này, khi đạt đến giới hạn, MySQL sẽ bị đóng băng và nó sẽ không thể thực hiện bất kỳ hoạt động nào. Khi cố gắng kết nối, bạn sẽ thấy những điều sau đây:
$ mysql -uroot -p
ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 104
Rối loạn quyền
Quá trình mysqld chạy bởi người dùng "mysql", có nghĩa là tất cả các tệp và thư mục mà nó cần truy cập đều thuộc sở hữu của người dùng / nhóm mysql. Bằng cách xáo trộn quyền và quyền sở hữu, chúng ta có thể làm cho máy chủ MySQL trở nên vô dụng:
$ chown root:root /var/lib/mysql
$ chmod 600 /var/lib/mysql
Tạo một số tải cho máy chủ, sau đó kết nối với máy chủ MySQL và chuyển tất cả các bảng vào đĩa:
mysql> FLUSH TABLES WITH READ LOCK; UNLOCK TABLES;
Tại thời điểm này, mysqld vẫn đang chạy nhưng nó vô dụng. Bạn có thể truy cập nó thông qua ứng dụng khách mysql nhưng bạn không thể thực hiện bất kỳ thao tác nào:
mysql> SHOW DATABASES;
ERROR 1018 (HY000): Can't read dir of '.' (errno: 13 - Permission denied)
Để dọn dẹp đống lộn xộn, hãy đặt các quyền chính xác:
$ chown mysql:mysql /var/lib/mysql
$ chmod 750 /var/lib/mysql
$ systemctl restart mysqld
Khóa nó xuống
BẢNG FLUSH VỚI KHÓA ĐỌC (FTWRL) có thể phá hủy trong một số điều kiện. Chẳng hạn như trong một cụm Galera nơi tất cả các nút đều có thể xử lý ghi, bạn có thể sử dụng câu lệnh này để khóa cụm từ bên trong một trong các nút. Câu lệnh này chỉ đơn giản là tạm dừng các truy vấn khác được xử lý bởi mysqld trong quá trình xả cho đến khi khóa được phát hành, điều này rất tiện dụng cho các quy trình sao lưu (bảng MyISAM) và ảnh chụp nhanh hệ thống tệp.
Mặc dù hành động này sẽ không làm hỏng hoặc làm sập máy chủ cơ sở dữ liệu của bạn trong quá trình khóa, nhưng hậu quả có thể rất lớn nếu phiên giữ khóa không giải phóng nó. Để thử điều này, chỉ cần:
mysql> FLUSH TABLES WITH READ LOCK;
mysql> exit
Sau đó, gửi một loạt các truy vấn mới đến mysqld cho đến khi nó đạt đến max_connections giá trị. Rõ ràng, bạn không thể quay lại phiên tương tự như phiên trước khi bạn đã ra ngoài. Vì vậy, khóa sẽ chạy vô hạn và cách duy nhất để giải phóng khóa là hủy truy vấn bởi người dùng SUPER đặc quyền khác (sử dụng phiên khác). Hoặc tự hủy quá trình mysqld hoặc thực hiện khởi động lại cứng.
Tuyên bố từ chối trách nhiệm
Blog này được viết để cung cấp các lựa chọn thay thế cho sysadmins và DBA để mô phỏng các tình huống thất bại với MySQL. Đừng thử những điều này trên máy chủ sản xuất của bạn :-)