Eilės naudojimas MySQL duombazėje 2
Anksčiau mėginau apibrėžti ko dažniausiai reikia realizuojant eilę (queue) duomenų bazėje, dabar sukurkim likusias trūkstamas dalis. Mums reikės lentelės:
CREATE TABLE `queue` ( `object_id` int(10) unsigned NOT NULL, `status` enum('W','L','E') NOT NULL DEFAULT 'W', `num_attempts` int(1) NOT NULL DEFAULT '0', `priority` int(1) unsigned NOT NULL DEFAULT '5', `locked_time` datetime DEFAULT NULL, `locked_by` varchar(100) DEFAULT NULL, PRIMARY KEY (`object_id`), KEY `status` (`status`,`priority`) ) ENGINE=InnoDB;
Trumpai apie lentelės laukus:
* `object_id` – objekto, patalpinto į eilę, ID
* `status` – objekto statusas eilėje: `W` – laukia (waiting); `L` – užrakintas/apdorojamas (locked); `E` – klaida (error)
* `num_attempts` – kiek kartų buvo užrakintas/apdorojamas objektas
* `priority` – objekto eilėje prioritetas, pagal nutylėjimą – 5, galimos reikšmės 0 (aukščiausias) – 9 (žemiausias)
* `locked_time` – kada objektas užrakintas
* `locked_by` – kas užrakino objektą
Na o dabar – ilgai laukta stored procedūra, kuris pagalba imsime objektus iš eilės pagal prioritetą.
CREATE PROCEDURE `sp_getNextInQueue`(IN lockedBy VARCHAR(100), OUT objectIdRet INT) BEGIN DECLARE lockTimeout, objectId, maxAttempts INT; SET lockTimeout = 5, objectId = NULL, maxAttempts = 3; # Pirma pakeiciame statusa objektu, kurie stovi eileje per # ilgai ir juos apdoroti bandyta daugiau nei nustatyta kartu UPDATE queue SET `status` = 'E' WHERE `status` = 'L' AND TIMESTAMPDIFF(MINUTE, locked_time, NOW()) > lockTimeout AND num_attempts >= maxAttempts; # Pakeiciame objektu statusa, kurie buvo uzrakinti per ilgai UPDATE queue SET `status` = 'W', locked_time = NULL, locked_by = NULL WHERE `status` = 'L' AND TIMESTAMPDIFF(MINUTE, locked_time, NOW()) > lockTimeout; # imame pirma objekta is laukianciuju eiles pagal prioriteta START TRANSACTION; SELECT object_id INTO objectId FROM queue WHERE `status` = 'W' AND (`num_attempts` < maxAttempts) ORDER BY priority LIMIT 1 FOR UPDATE; # jei toki objekta randame - uzrakiname IF (objectId > 0) THEN UPDATE queue SET `status` = 'L', num_attempts = COALESCE(num_attempts, 0) + 1, locked_time = NOW(), locked_by = lockedBy WHERE object_id = objectId; END IF; COMMIT; # atiduodame objekto ID SET objectIdRet = objectId; END$$ DELIMITER ;
Testuojam, naudojam? paprasta:
SET @objId = NULL; CALL sp_getNextInQueue('test', @objId); SELECT @objId;
Jei savo paimto objekto ID per nustatytą laiką neišimsite iš eilės – jis bus vėl sugražintas į eilę. Testavimams nurodžiau 5 minutes, o realiuose projektuose galima nusistatyti pagal formulę `max(t)*2`. T.y. – jei mūsų kažkokia procedūra apdoroja objektą ilgiausiai per 1 min, tai timeout’as neturėtų būti trumpesnis nei 2 minutės.
Ką patiems belieka pasidaryti – objekto iš eilės panaikinimą, kai jis sėkmingai apdorojamas ar pan. Tai galėtų būti taip pat procedūra. O jei kas turit idėjų ar noro parašyti tokią procedūrą – prašom, lauksiu.
Panašūs įrašai: