Vấn đềBạn gặp bế tắc khi cố gắng sao chép một bảng hoặc lưu kết quả truy vấn bằng phím tắt CREATE TABLE ... SELECT (CTAS). Lệnh này là lựa chọn yêu thích của các lập trình viên cần sao lưu nhanh, nhưng nó lại thất bại trong các môi trường có tính sẵn sàng cao hiện đại. Nếu thực thể MySQL của bạn sử dụng Global Transaction Identifiers (GTID)—tiêu chuẩn cho AWS RDS, Aurora hoặc bất kỳ thiết lập master-slave mạnh mẽ nào—bạn sẽ thấy lỗi sau:
ERROR 1785 (HY000): Statement violates GTID consistency: CREATE TABLE ... SELECT.
MySQL đang bảo vệ bạn. Nó thực thi các quy tắc nghiêm ngặt để đảm bảo mọi thay đổi đều có thể được theo dõi trên toàn bộ cluster mà không gây lệch dữ liệu giữa các máy chủ.
Tại sao điều này xảy raĐể kiểm tra xem máy chủ của bạn có đang ở chế độ nghiêm ngặt này không, hãy chạy lệnh sau:
SHOW VARIABLES LIKE 'enforce_gtid_consistency';
Nếu kết quả là ON, MySQL sẽ trở nên khắt khe về cách ghi nhật ký các giao dịch. Câu lệnh CREATE TABLE ... SELECT gây rắc rối vì nó cố gắng thực hiện hai việc khác nhau cùng lúc: định nghĩa một schema mới (DDL) và đổ dữ liệu vào đó (DML).
Trong các phiên bản trước MySQL 8.0.21, công cụ không thể ghi lại hai hành động riêng biệt này dưới dạng một sự kiện GTID nguyên tử (atomic) duy nhất. Nếu máy chủ master gặp sự cố giữa chừng hoặc máy chủ replica bị trễ, cơ sở dữ liệu không thể đảm bảo rằng bảng mới sẽ trông giống hệt nhau trên mọi node trong cluster. Để ngăn chặn sự không nhất quán này, MySQL chỉ đơn giản là chặn lệnh đó.
Giải pháp: Cấu trúc lại thành hai bướcCách khắc phục rất đơn giản. Bạn cần ngừng việc cố gắng thực hiện mọi thứ trên một dòng lệnh. Bằng cách chia nhỏ thao tác, bạn cung cấp cho công cụ GTID các bước rõ ràng, có thể ghi nhật ký và không làm gián đoạn quá trình sao lưu.
1. Định nghĩa cấu trúc bảngĐầu tiên, hãy tạo một khung bảng trống. Nếu bạn muốn một bản sao chính xác của bảng hiện có (bao gồm cả các index), hãy sử dụng mệnh đề LIKE. Nếu bạn chỉ cần các cột cụ thể, hãy định nghĩa chúng một cách thủ công.
-- Tùy chọn A: Sao chép chính xác cấu trúc (khuyên dùng)
CREATE TABLE orders_backup LIKE orders;
-- Tùy chọn B: Tạo cấu trúc tùy chỉnh cho dữ liệu cụ thể
CREATE TABLE monthly_summary (
id INT PRIMARY KEY,
total_sales DECIMAL(10,2),
report_date DATE
);
2. Di chuyển dữ liệuBây giờ khi bảng đã tồn tại, hãy di chuyển dữ liệu bằng câu lệnh INSERT INTO ... SELECT tiêu chuẩn. Đây là một thao tác dữ liệu thuần túy mà GTID xử lý mà không có bất kỳ phàn nàn nào.
INSERT INTO monthly_summary (id, total_sales, report_date)
SELECT id, amount, '2023-12-31'
FROM orders
WHERE status = 'completed' AND created_at >= '2023-12-01';
Đối với các tập dữ liệu lớn—ví dụ trên 1 triệu dòng—hãy cân nhắc thêm LIMIT hoặc xử lý dữ liệu theo đợt (batch) để tránh khóa bảng nguồn quá lâu.
Xác minh kết quảSau khi chạy các lệnh đã chia nhỏ, hãy dành 30 giây để đảm bảo mọi thứ đã được đồng bộ hóa:
- **Xác nhận số lượng dòng:** Kiểm tra xem các con số có khớp với mong đợi của bạn không.
```
SELECT COUNT(*) FROM monthly_summary;
- **Kiểm tra tình trạng sao lưu:** Nếu bạn đang làm việc trên máy chủ master thực tế, hãy đảm bảo các replica vẫn hoạt động ổn định.
```
SHOW REPLICA STATUS\G;
Tìm các dòng `Replica_IO_Running: Yes` và `Replica_SQL_Running: Yes`. Nếu chúng hiển thị `No`, bạn có thể đang gặp một vấn đề sao lưu lớn hơn cần giải quyết.
Các phương pháp hay nhất
- **Bỏ CTAS trong các script:** Tránh sử dụng `CREATE TABLE ... SELECT` trong bất kỳ script di chuyển tự động hoặc mã ứng dụng nào. Đó là một "quả bom hẹn giờ" sẽ phát nổ ngay khi bạn chuyển sang môi trường hỗ trợ GTID.
- **Bảng tạm thời (Temporary Tables):** Hãy cẩn thận với `CREATE TEMPORARY TABLE`. Dưới sự nhất quán của GTID, thông thường bạn không thể tạo bảng tạm thời bên trong một giao dịch (giữa `BEGIN` và `COMMIT`).
- **Nâng cấp nếu có thể:** MySQL 8.0.21 đã giới thiệu "Atomic DDL", cuối cùng cho phép CTAS dưới GTID cho các bảng InnoDB. Nếu bạn đang bị kẹt ở phiên bản 5.7 hoặc các bản phát hành 8.0 đời đầu, việc nâng cấp có thể loại bỏ hoàn toàn rắc rối này.

