MySQL SQL Mode: ‘Tấm khiên’ chặn đứng dữ liệu rác và lỗi logic ngầm

MySQL tutorial - IT technology blog
MySQL tutorial - IT technology blog

Khi MySQL quá “dễ dãi” và thảm họa dữ liệu rác

Bạn đã bao giờ “đứng hình” khi thấy MySQL tự ý cắt cụt dữ liệu chưa? Hãy tưởng tượng bạn chèn một chuỗi 100 ký tự vào cột VARCHAR(50). Thay vì báo lỗi, MySQL chỉ đưa ra một Warning nhẹ nhàng và lẳng lặng vứt bỏ 50 ký tự còn lại của bạn. Hoặc tệ hơn, khi bạn chèn ngày 2023-02-31, database vẫn thản nhiên nhận giá trị 0000-00-00.

Tôi từng xử lý một sự cố tại dự án thương mại điện tử khiến báo cáo doanh thu lệch hơn 50 triệu đồng mỗi ngày. Sau hai ngày rà soát, thủ phạm không nằm ở code backend mà ở chế độ mặc định của database. Một lỗi logic khiến giá trị tiền tệ bị tràn số (overflow). Thay vì báo lỗi để dừng giao dịch, MySQL tự động đưa nó về giá trị tối đa của kiểu dữ liệu. Kết quả là ứng dụng vẫn báo thành công trong khi dữ liệu đã sai lệch hoàn toàn.

Vấn đề này nằm ở SQL Mode. Nếu không kiểm soát tốt cấu hình này, bạn đang đặt hệ thống lên một quả bom hẹn giờ.

SQL Mode: “Bản hiến pháp” của Database

SQL Mode định nghĩa cách MySQL thực thi các câu lệnh và kiểm tra dữ liệu đầu vào. Tùy vào cấu hình, database có thể cực kỳ nghiêm khắc (Strict Mode) hoặc rất lỏng lẻo (Legacy Mode).

Sự lỏng lẻo này thường nhằm duy trì tính tương thích ngược cho các ứng dụng từ thập kỷ trước. Tuy nhiên, với các hệ thống hiện đại, sự dễ dãi này chính là kẻ thù của tính toàn vẹn dữ liệu. Khi SQL Mode quá thoáng, MySQL sẽ tự ý “sửa sai” bằng cách:

  • Tự động cắt chuỗi nếu vượt quá độ dài quy định.
  • Ép các giá trị không hợp lệ về giá trị mặc định (như chuỗi rỗng hoặc 0).
  • Cho phép phép chia cho 0 trả về NULL thay vì chặn đứng hành vi sai trái.

4 SQL Mode bắt buộc phải biết

Đừng để MySQL tự quyết định vận mệnh dữ liệu của bạn. Dưới đây là những chế độ tôi luôn bật cho mọi dự án từ môi trường staging đến production.

1. STRICT_TRANS_TABLES

Đây là nòng cốt của Strict Mode. Nếu dữ liệu không chuẩn, MySQL phải bắn Error và dừng câu lệnh ngay lập tức. Điều này tuân thủ nguyên tắc “Fail Fast”: thà ứng dụng báo lỗi còn hơn lưu dữ liệu sai vào hệ thống.

2. NO_ZERO_IN_DATE & NO_ZERO_DATE

Hai mode này ngăn chặn việc lưu ngày tháng vô lý như 0000-00-00. Việc xử lý các giá trị ngày “rác” này trong Java hay PHP thường gây ra Exception vì các thư viện chuẩn không thể định dạng được chúng.

3. ERROR_FOR_DIVISION_BY_ZERO

Khi thực hiện phép tính 100 / 0, bạn chắc chắn muốn hệ thống báo lỗi logic. Nếu không có mode này, MySQL âm thầm trả về NULL, khiến các công thức tính toán tài chính phía sau sai dây chuyền.

4. ONLY_FULL_GROUP_BY

Mode này buộc các cột trong mệnh đề SELECT phải xuất hiện trong GROUP BY (trừ các hàm aggregate). Nó giúp truy vấn trở nên minh bạch, tránh việc MySQL lấy ra dữ liệu ngẫu nhiên không thể kiểm soát.

Cách kiểm tra và cấu hình SQL Mode

Để xem database của bạn đang chạy theo luật nào, hãy thực thi lệnh sau:

-- Kiểm tra cấu hình hiện tại
SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;

Nếu kết quả trả về trống hoặc thiếu STRICT_TRANS_TABLES, hệ thống của bạn đang ở trạng thái nguy hiểm.

Cấu hình tạm thời (Session)

Dùng lệnh này nếu bạn muốn thử nghiệm nhanh mà không ảnh hưởng đến toàn bộ server:

SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';

Cấu hình vĩnh viễn (Khuyên dùng)

Hãy sửa file cấu hình MySQL (my.cnf trên Linux hoặc my.ini trên Windows). Tìm mục [mysqld] và thêm dòng cấu hình chuẩn sau:

[mysqld]
sql_mode = "STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY"

Sau đó, khởi động lại service để áp dụng thay đổi:

sudo systemctl restart mysql

Kinh nghiệm thực chiến: Đừng hạ chuẩn để chiều lòng code lỗi

Nhiều lập trình viên khi gặp lỗi Field 'xyz' doesn't have a default value thường tắt Strict Mode để code chạy được ngay. Đây là sai lầm chết người. Bạn đang xử lý phần ngọn nhưng để lại hậu quả khôn lường cho dữ liệu sau này.

3 quy tắc vàng tôi luôn áp dụng:

  1. Sửa code, không hạ chuẩn: Nếu thiếu giá trị mặc định, hãy cập nhật Schema hoặc sửa câu lệnh INSERT.
  2. Đồng bộ môi trường: Đảm bảo SQL Mode ở máy cá nhân (Docker, XAMPP) giống hệt Production. Điều này tránh tình trạng “chạy tốt ở máy em nhưng chết ở server”.
  3. Sử dụng phím tắt TRADITIONAL: Bạn có thể dùng SET sql_mode = 'TRADITIONAL'; để kích hoạt toàn bộ các ràng buộc nghiêm ngặt nhất, biến MySQL thành một database chuẩn mực như PostgreSQL.

Làm chủ SQL Mode giúp bạn phát hiện lỗi logic ngay từ giai đoạn phát triển. Hãy thiết lập một bộ quy tắc nghiêm khắc ngay hôm nay để bảo vệ sự toàn vẹn cho dữ liệu của bạn.

Share: