2013-01-01 48 views
0

我有一個客戶端 - 服務器應用程序將用戶數據發送到雲(Amazon EC2 + RDS + S3)。同時更新MySQL MyISAM表中的計數器

  1. 每個用戶都可以具有連接到雲&發送數據同時多個器件安裝在每個設備上
  2. 客戶端應用程序是多線程的,並最終在同一時間上載多個數據片段。

我想可靠地跟蹤在這種情況下使用的磁盤使用情況,我不知道如何在這種情況下做到這一點?

我有兩個想法,到目前爲止,但我甚至不能確定它們是正確的:

選項1:觸發加入到MySQL表?即。

CREATE TRIGGER DiskUsage AFTER UPDATE OF Fully_Updated_File_Flag ON Files 
BEGIN 
    for each row 
    begin 
     UPDATE Users SET SpaceUsed = SpaceUsed + new.Size WHERE (new.Fully_Updated_File_Flag = 1) And UserID= 
    end 
END; 

如果我選擇使用觸發器,我怎麼給動態注入用戶ID?


選項2:通過PHP更新mysql表嗎?即。

<?php 

    SendFileToS3($file_name); 
    mysql_query('UPDATE Stats SET Value = Value + ' . filesize($file_name) . ' WHERE user_id=' . $user_id); 

?> 

如果兩個實例試圖更新相同的記錄會怎樣? (我使用的是Mysql 5.5.27-log/MyISAM),這是否仍然有效。


注#1雖然我還不放開我的申請,我還是需要的東西很好地擴展。即使這意味着將數據庫引擎全部改變。

注#2 DB-相關的代碼被封裝在模函數(即InsertIntoDB(),updatedb的()& DeleteFromDB())。再加上所有這些例程都依賴於帶有活動記錄類的CodeIgniter 2.1。

這是說,我總是可以讓如果我有(雖然我想避免)開關

回答

3

您應該使用MySQL觸發器代替PHP代碼,並且您必須將相關user_id存儲到diskusage表中。

我使用InnoDB引擎,因爲CONSTRAINT。您也可以使用MyISAM,但您應該刪除CONSTRAINT

REMARK

我會用InnoDB因爲交易和(更重要的位置)行鎖定

表結構(InnoDB的)

-- ---------------------------- 
-- Table structure for `users` 
-- ---------------------------- 
DROP TABLE IF EXISTS `users`; 
CREATE TABLE `users` (
    `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `Name` VARCHAR(10) NOT NULL DEFAULT '', 
    `SpaceUsed` BIGINT(20) NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=INNODB DEFAULT CHARSET=utf8; 

-- ---------------------------- 
-- Table structure for `diskusage` 
-- ---------------------------- 
DROP TABLE IF EXISTS `diskusage`; 
CREATE TABLE `diskusage` (
    `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `Filename` VARCHAR(50) NOT NULL DEFAULT '', 
    `Size` BIGINT(20) NOT NULL, 
    `user_id` INT(11) UNSIGNED DEFAULT NULL, 
    `Fully_Updated_File_Flag` TINYINT(4) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `fk_diskusage_user` (`user_id`), 
    CONSTRAINT `fk_diskusage_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE 
) ENGINE=INNODB DEFAULT CHARSET=utf8; 

表結構(MyISAM的)

-- ---------------------------- 
-- Table structure for `users` 
-- ---------------------------- 
DROP TABLE IF EXISTS `users`; 
CREATE TABLE `users` (
    `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `Name` VARCHAR(10) NOT NULL DEFAULT '', 
    `SpaceUsed` BIGINT(20) NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

-- ---------------------------- 
-- Table structure for `diskusage` 
-- ---------------------------- 
DROP TABLE IF EXISTS `diskusage`; 
CREATE TABLE `diskusage` (
    `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `Filename` VARCHAR(50) NOT NULL DEFAULT '', 
    `Size` BIGINT(20) NOT NULL, 
    `user_id` INT(11) UNSIGNED DEFAULT NULL, 
    `Fully_Updated_File_Flag` TINYINT(4) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `fk_diskusage_user` (`user_id`), 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

這就是所有,與上表diskusage一些觸發器在一起。

INSERT觸發器

-- ---------------------------- 
-- AFTER INSERT TRIGGER for `diskusage` 
-- ---------------------------- 
delimiter ;; 
CREATE TRIGGER `diskusage_after_insert` AFTER INSERT ON `diskusage` FOR EACH ROW BEGIN 
    IF NEW.Fully_Updated_File_Flag = 1 THEN 
    UPDATE users 
    SET 
     SpaceUsed = SpaceUsed + NEW.Size 
    WHERE 
     id = NEW.user_id; 
    END IF; 
END; 
;; 
delimiter ; 

UPDATE觸發器

-- ---------------------------- 
-- AFTER UPDATE TRIGGER for `diskusage` 
-- ---------------------------- 
delimiter ;; 
CREATE TRIGGER `diskusage_after_update` AFTER UPDATE ON `diskusage` FOR EACH ROW BEGIN 

    -- same to DELETE TRIGGER 

    -- decrease SpaceUsed with OLD Size for OLD user 

    IF OLD.Fully_Updated_File_Flag = 1 THEN 
    UPDATE users 
    SET 
     SpaceUsed = SpaceUsed - OLD.Size 
    WHERE 
     id = OLD.user_id; 
    END IF; 

    -- same to INSERT TRIGGER 

    -- increase SpaceUsed with NEW Size for NEW user 

    IF NEW.Fully_Updated_File_Flag = 1 THEN 
    UPDATE users 
    SET 
     SpaceUsed = SpaceUsed + NEW.Size 
    WHERE 
     id = NEW.user_id; 
    END IF; 

END; 
;; 
delimiter ; 

DELETE TRIGGER

-- ---------------------------- 
-- AFTER DELETE TRIGGER for `diskusage` 
-- ---------------------------- 
delimiter ;; 
CREATE TRIGGER `diskusage_after_delete` AFTER DELETE ON `diskusage` FOR EACH ROW BEGIN 

    IF OLD.Fully_Updated_File_Flag = 1 THEN 
    UPDATE users 
    SET 
     SpaceUsed = SpaceUsed - OLD.Size 
    WHERE 
     id = OLD.user_id; 
    END IF; 

END; 
;; 
delimiter ; 
2

操作MyISAM表are atomic。基本上,查詢在發生後自動提交。

此外,UPDATE是阻止,這意味着一次只能發生一個。這意味着UPDATE的讀寫週期不會中斷。

MySQL對於MyISAM表使用table locking

這相當快,並且可確保併發更新正常工作。

但是,許多更新可能會導致表花費大量時間被鎖定。如果表格中有許多行,則可能會出現問題。

InnoDB表支持行鎖定。這需要更多資源,但如果表格變大,可能會更合適。它可以更好地控制鎖定,並允許多個不相關的進程訪問表,而不會產生過多的鎖爭用。

+0

所以,如果我理解正確的話,無論我使用觸發器還是通過PHP運行查詢,InnoDB基本上都不會遇到擴展問題(至少就這個特定的UPDATE查詢而言)?那是對的嗎? – TheDude

3

如果你正在追蹤文件,所有權的大小&將做得非常快,正確的索引表,除非用戶有一個brazillion文件與他們相關聯。無需存儲您可以輕鬆計算的數字。

+0

有趣。比方說,我只**在我的表中存儲'輕量級元信息'(沒有斑點或任何「沉重」,只是一堆字符串,日期時間和整數),一個UserID索引,500萬用戶,平均,每個用戶有100萬個文件(這是總共5 **十億個**)。每當一個線程(屬於我的用戶的每個設備)上傳一個文件/數據以返回更新後的大小時,我需要做一個「SELECT Sum(Size)...」。所以,你說有足夠的雲資源,這不會是一個問題? (與使用InnoDB相比)? – TheDude

+1

@TheDude如果您真的擔心擴展到該級別,那麼即使是預先計算的值,您也可能希望此數字緩存並且不查詢數據庫。不過,如果你知道這會受到很大打擊,用戶和大小索引應該只允許你掃描索引。 –