2015-07-11 33 views
0

我在建立一些mysql的遊戲邏輯並遇到了一個難題。玩家擁有可以通過購買升級的技術。然後經過一段時間後,玩家的等級會提高。通過這種方式,關卡實時更新,而不是當玩家通過觸發器進行操作時。 mysql如何根據升級過程是否完成並實時更新來更新播放器的級別?根據在mysql中的時間創建自動更新行

我的第一個想法: 每秒或每分鐘運行一次mysql事件來檢查過期的技術並適當升級它們。在開始構建這個想法之後,我很快發現我的開銷在增加。除非我使用一些健壯的硬件,否則我不認爲我可以在一臺服務器上運行4或5或這些進程並有效運行我的遊戲服務器。

我的新想法: 我在玩意見,並提出了一個有趣的工作,對我的問題進行了排序,使其對資源更友好。

首先我有我的players_technologies表:

CREATE TABLE `players_technologies` (
    `playertech_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `player_id` int(10) unsigned NOT NULL, 
    `tech_id` int(10) unsigned NOT NULL, 
    `next_update` datetime DEFAULT NULL, 
    `level` int(11) NOT NULL, 
    PRIMARY KEY (`playertech_id`), 
    KEY `fk_players_technologies_1_idx` (`player_id`), 
    CONSTRAINT `fk_players_technologies_1` FOREIGN KEY (`player_id`) REFERENCES `players` (`player_id`) ON DELETE NO ACTION ON UPDATE NO ACTION 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

這是玩家的技術水平的存儲位置。 tech_id僅僅是對名稱等技術細節的引用,不應該對此設置產生太大影響。

然後,我創建了一個VIEW用於從數據庫中的其他位置訪問的技術。

CREATE VIEW `players_technologies_real` AS 
    SELECT `players_technologies`.`playertech_id` AS `playertech_id`, 
     `players_technologies`.`player_id` AS `player_id`, 
     `players_technologies`.`tech_id` AS `tech_id`, 
     `players_technologies`.`next_update` AS `next_update`, 
     `players_technologies`.`level` AS `level`, 
     if(isnull(`players_technologies`.`next_update`), 
     `players_technologies`.`level`, 
     if((`players_technologies`.`next_update` < now()), 
      (`players_technologies`.`level` + 1), 
      `players_technologies`.`level`)) AS `real_level` 
    FROM `players_technologies`; 

此檢查的截止日期,next_update,看看那個時候已經過去。如果有,它將顯示real_level增加1.這是數據庫的其餘部分應用於邏輯並保持實時的字段。

然後,我可以在player_technologies的一行更新之前添加TRIGGER,如果過期日期過期,它將拒絕更新next_update。如果它已經過去,它會增加水平並繼續執行UPDATE。這TRIGGER看起來是這樣的:

DELIMITER $$ 
CREATE DEFINER=`root`@`localhost` TRIGGER `players_technologies_BU` BEFORE UPDATE ON `players_technologies` 
FOR EACH ROW 
BEGIN 
    DECLARE msg VARCHAR(255); 
    IF(NEW.`next_update` <> OLD.`next_update`) THEN 
     IF(OLD.`next_update` > NOW()) THEN 
      SET msg = CONCAT('Player Technology Error: Player Tech #',NEW.`playertech_id`,' is not finished with it current research.'); 
      SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg; 
     ELSE 
      SET NEW.`level` = OLD.`level` + 1; 
     END IF; 
    END IF; 
END$$ 

任何人都可以看到,這是不正確的設置?這有什麼不對嗎?

+0

它會工作,但是在mysql中視圖往往很慢(與優化器有關,除非你破壞mysql)(至少在版本5.7之前),所以要麼升級到mariadb似乎沒有這個問題,或者不使用視圖。 –

+0

@NorbertvanNobelen - 將視圖邏輯封裝到函數中並使用該函數確定級別會更好嗎?即players_tech_real('next_update','level')AS'real_level'。這會跳過對視圖的需求。 – wesleywmd

+0

你可以這樣做。然後,您可以通過在函數中更改查詢來應用任何標準,並且您仍然只有一個位置來維護您的代碼。優化器對該部分沒有問題。 –

回答

0

在Norbert的評論之後,我選擇放棄VIEW,因爲它不是最優的。我決定在mysql中編寫一個用戶定義的函數來完成我想要做的事情。首先這裏是我測試過的FUNCTION

DELIMITER $$ 
CREATE FUNCTION `get_playertech_real`(next_update DATETIME, my_level INT) RETURNS int(11) 
BEGIN 
    DECLARE real_level INT; 
    IF isnull(next_update) THEN 
     SET real_level = my_level; 
    ELSE 
     IF next_update < now() THEN 
      SET real_level = my_level + 1; 
     ELSE 
      SET real_level = my_level; 
     END IF; 
    END IF; 
    RETURN real_level; 
END$$ 
DELIMITER ; 

此功能可能會稍微清潔一些,但可以正常工作。然後,你可以通過調用FUNCTION像這樣進入了「真正的」科技等級:

SELECT `playertech_id` AS `playertech_id`, 
    `player_id` AS `player_id`, 
    `tech_id` AS `tech_id`, 
    `next_update` AS `next_update`, 
    `level` AS `level`, 
    get_playertech_real(`next_update`,`level`) AS `real_level` 
FROM `players_technologies` 

這是測試和工程。但是這還不是測試非常大的數據集。

相關問題