兩張表 db_count(當前計數)與 db_date(時間記錄),需定期檢查 db_date.t_date 是否過期,過期則將對應 db_count.count 歸零。
CREATE TABLE db_count (
id INT PRIMARY KEY,
count INT
);
CREATE TABLE db_date (
id INT PRIMARY KEY,
t_date DATETIME,
count INT
);
DELIMITER //
CREATE PROCEDURE update_expired_counts()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE v_id INT;
DECLARE v_t_date DATETIME;
-- 游標:查詢所有需檢查的記錄
DECLARE cur CURSOR FOR
SELECT id, t_date FROM db_date;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO v_id, v_t_date;
IF done THEN LEAVE read_loop; END IF;
-- 核心邏輯:當前時間超過記錄時間,歸零
IF NOW() > v_t_date THEN
UPDATE db_count c
JOIN db_date d ON c.id = d.id
SET c.count = 0
WHERE c.id = v_id;
END IF;
END LOOP;
CLOSE cur;
END //
DELIMITER ;
-- 啟用事件調度器(永久生效需加入 my.cnf)
SET GLOBAL event_scheduler = ON;
-- 創建每秒執行一次的事件(演示用,生產建議 1 分鐘/小時)
CREATE EVENT ev_update_counts
ON SCHEDULE EVERY 1 MINUTE
STARTS CURRENT_TIMESTAMP
DO
CALL update_expired_counts();
SHOW EVENTS; -- 查看事件
SHOW PROCESSLIST; -- 事件執行線程顯示為 "Daemon"
ALTER EVENT ev_update_counts DISABLE; -- 暫停
ALTER EVENT ev_update_counts ENABLE; -- 恢復
DROP EVENT ev_update_counts; -- 刪除
CREATE PROCEDURE update_expired_counts_v2()
BEGIN
UPDATE db_count c
JOIN db_date d ON c.id = d.id
SET c.count = 0
WHERE d.t_date < NOW();
END
單條 SQL 完成,無游標開銷,原子性更強,推薦生產使用。
[mysqld]
event_scheduler = ON
EVENT 權限(GRANT EVENT ON *.* TO 'user'@'%';)time_zone 正確,影響 NOW() 判斷log_error 記錄事件執行錯誤| 方案 | 優點 | 缺點 | 適用 |
|---|---|---|---|
| MySQL Event | 無外部依賴、事務一致、秒級精度 | 單實例、無分佈式協調 | 單庫內部狀態維護 |
| Cron + 腳本 | 靈活、可跨庫、語言不限 | 需維護連接、網絡抖動、無事務 | 跨庫/異構/複雜邏輯 |
| 應用層定時任務 | 業務邏輯集中、易測試 | 部署耦合、單點故障 | 微服務架構 |