MySQL Event Scheduler: Tự động hóa Database để không còn phải ‘trực chiến’ đêm khuya

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

Vấn đề: Khi Database phình to và những tác vụ thủ công trở thành gánh nặng

Hồi mình quản lý một hệ thống e-commerce, bảng user_activities từng là nỗi ám ảnh thực sự. Ban đầu, mọi thứ chạy rất mượt với latency chỉ khoảng 50ms. Nhưng sau 6 tháng, khi database chạm mốc 50GB với hơn 80 triệu dòng log, các câu query đơn giản bắt đầu nhảy vọt lên 3-5 giây.

Giải pháp lúc đó khá “thủ công mỹ nghệ”: cứ sáng thứ Hai, mình lại phải dậy sớm để DELETE bớt dữ liệu cũ. Có những tuần ngủ quên, y như rằng hệ thống báo lỗi Disk Full hoặc index phình to đến mức treo cả database. Chỉ cần một lần gõ nhầm lệnh WHERE trong trạng thái ngái ngủ, hậu quả sẽ cực kỳ thảm khốc.

Tại sao các cách làm truyền thống thường gây phiền toái?

Khi cần tự động hóa, đa số anh em thường cân nhắc hai phương án quen thuộc:

  1. Dùng Crontab của Linux: Bạn viết một script Python hoặc PHP rồi gọi nó qua Cron. Cách này tạo ra một dependency bên ngoài. Nếu server chạy script lỗi kết nối hoặc sai version môi trường, task sẽ chết âm thầm mà database không hề hay biết.
  2. Xử lý trong Application Logic: Chèn code kiểm tra dữ liệu mỗi khi có user truy cập. Đây là sai lầm tai hại vì nó trực tiếp làm tăng Response Time của người dùng chỉ để xử lý việc nội bộ của hệ thống.

Câu hỏi đặt ra là: Tại sao phải đi đường vòng khi bản thân MySQL đã có sẵn một bộ máy lập lịch cực mạnh?

MySQL Event Scheduler – Bộ máy “Cron job” nội tại

MySQL Event Scheduler hoạt động như một trình lập lịch nằm ngay trong database engine. Nó cho phép thực thi SQL hoặc Stored Procedure theo chu kỳ (hàng giờ, hàng ngày) hoặc chạy một lần duy nhất vào thời điểm định sẵn.

1. Kích hoạt “vũ khí” bị ẩn

Mặc định, tính năng này có thể bị tắt ở một số phiên bản để tiết kiệm tài nguyên. Bạn hãy kiểm tra trạng thái bằng lệnh:

SHOW VARIABLES LIKE 'event_scheduler';

Nếu kết quả là OFF, hãy bật nó lên ngay lập tức:

SET GLOBAL event_scheduler = ON;

Mẹo nhỏ: Để thiết lập này không bị mất khi restart server, bạn cần thêm event_scheduler=ON vào file my.cnf hoặc my.ini.

2. Thiết lập Event đầu tiên

Thay vì dậy sớm lúc 2 giờ sáng để dọn log, mình dùng Event để nó tự chạy vào khung giờ thấp điểm nhất.

CREATE EVENT clean_old_user_logs
ON SCHEDULE EVERY 1 DAY
STARTS (TIMESTAMP(CURRENT_DATE) + INTERVAL 2 HOUR)
DO
  DELETE FROM user_activities 
  WHERE created_at < NOW() - INTERVAL 30 DAY;

Cấu trúc rất trực quan: ON SCHEDULE định nghĩa tần suất, còn DO chứa nội dung công việc cần thực hiện.

3. Quản lý danh sách tác vụ

Khi hệ thống lớn dần, bạn sẽ có hàng chục Event chạy ngầm. Việc kiểm soát chúng giúp tránh tình trạng các task “đá nhau” gây nghẽn tài nguyên.

-- Liệt kê tất cả task đang chạy
SHOW EVENTS;

-- Tạm dừng task khi cần bảo trì hệ thống
ALTER EVENT clean_old_user_logs DISABLE;

-- Kích hoạt lại sau khi xong việc
ALTER EVENT clean_old_user_logs ENABLE;

Kinh nghiệm thực tế: Đừng để Event làm sập Database

Làm việc với database production 50GB dạy mình rằng không được chủ quan. Một câu lệnh DELETE quá lớn có thể khóa (lock) toàn bộ bảng, khiến ứng dụng bị đứng hình.

Chia để trị (Batching)

Đừng xóa 1 triệu dòng cùng lúc. Hãy chia nhỏ thành từng đợt 5.000 dòng để MySQL có thời gian “thở” và giải phóng tài nguyên cho các query khác. Kết hợp với SLEEP(1) sẽ giúp giảm tải cho CPU và tránh lag cho các server Replica.

DELIMITER //
CREATE PROCEDURE proc_safe_cleanup()
BEGIN
  REPEAT
    DELETE FROM user_activities 
    WHERE created_at < NOW() - INTERVAL 30 DAY
    LIMIT 5000;
    UNTIL ROW_COUNT() = 0
  END REPEAT;
END //
DELIMITER ;

Xây dựng hệ thống giám sát riêng

MySQL không tự gửi tin nhắn báo lỗi khi Event thất bại. Mình thường tạo một bảng event_log để ghi lại thời gian bắt đầu, kết thúc và trạng thái của mỗi task. Nếu executed_at của task dọn log không cập nhật quá 24h, mình sẽ biết ngay có vấn đề cần xử lý.

Khi nào nên và không nên dùng Event Scheduler?

Công cụ này là lựa chọn số 1 cho các tác vụ thuần túy dữ liệu như:

  • Dọn dẹp dữ liệu rác, session hết hạn hoặc log cũ.
  • Tính toán báo cáo tổng hợp (Aggregated data) vào cuối ngày.
  • Tự động đóng các đơn hàng chưa thanh toán sau 24 giờ.

Tuy nhiên, nếu task của bạn cần gọi API bên ngoài, gửi Email hay upload file lên S3, hãy dùng Queue Worker (như Redis/RabbitMQ). MySQL không được thiết kế để xử lý các tác vụ bên ngoài môi trường database.

Áp dụng Event Scheduler đã giúp mình giảm 80% công việc bảo trì thủ công. Database luôn duy trì ở mức dung lượng an toàn, hiệu năng ổn định mà mình không còn phải lo lắng mỗi dịp cuối tuần hay những lúc hệ thống tăng trưởng nóng.

Share: