xawiers.esu.as: 3-io lygio gyventojas

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.

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • LinkedIn
  • StumbleUpon
  • Twitter
  • Technorati

Panašūs įrašai:

  1. Eilės naudojimas MySQL duombazėje
  2. VDU laboras MySQL LD2
  3. VDU laboras MySQL LD3

You can follow any responses to this entry through the RSS 2.0 feed.