[英]Concurrently update counter in a mysql MyISAM table
I have a client-server application that send user data to the cloud (Amazon EC2 + RDS + S3). 我有一个客户端服务器应用程序,可将用户数据发送到云(Amazon EC2 + RDS + S3)。
I'd like to reliably track disk usage used in this context and I wonder how to do this in this context? 我想可靠地跟踪在这种情况下使用的磁盘使用情况,我想知道如何在这种情况下执行此操作?
I have two ideas so far, but I'm not even sure they are correct: 到目前为止,我有两个想法,但我什至不确定它们是正确的:
Option 1: Add a trigger to mysql table? 选项1:向mysql表添加触发器? ie. 即。
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;
If I opt to use triggers, how am I supposed to dynamically inject the user id? 如果我选择使用触发器,应该如何动态注入用户ID?
Option 2: Update mysql table via PHP? 选项2:通过PHP更新mysql表? ie. 即。
<?php
SendFileToS3($file_name);
mysql_query('UPDATE Stats SET Value = Value + ' . filesize($file_name) . ' WHERE user_id=' . $user_id);
?>
What if two instances are trying to update the same record? 如果两个实例试图更新同一记录怎么办? (I'm using Mysql 5.5.27-log / MyISAM), would this still work. (我正在使用Mysql 5.5.27-log / MyISAM),这是否仍然有效。
Note #1 Although I didn't yet release my application, I still need something that scales well. 注意#1尽管我尚未发布应用程序,但仍需要可扩展的功能。 Even if it means changing db engine all together. 即使这意味着一起更改数据库引擎。
Note #2 DB-related code is encapsulated in modular functions (ie. InsertIntoDB(), UpdateDB() & DeleteFromDB()). 注意#2与DB相关的代码封装在模块化函数(即InsertIntoDB(),UpdateDB()和DeleteFromDB())中。 Plus all of these routines relies on CodeIgniter 2.1 with active record class. 另外,所有这些例程都依赖于具有活动记录类的CodeIgniter 2.1。
This is to say that I could always make the switch if I have to (although I'd like to avoid that) 这就是说, 如果需要的话,我总是可以进行切换(尽管我想避免这种情况)
If you're tracking files, size & ownership, doing a SELECT SUM(Size) FROM Files WHERE UserID = ?
如果要跟踪文件,大小和所有权,请SELECT SUM(Size) FROM Files WHERE UserID = ?
would be blazingly fast on a properly indexed table unless users have a brazillion files associated with them. 除非用户拥有与其相关联的不计其数的文件,否则它将在索引正确的表上快速增长。 No need to store a number you can calculate that easily. 无需存储数字即可轻松计算出该数字。
You should use MySQL Triggers instead of PHP code and you have to store the related user_id
into diskusage
table. 您应该使用MySQL触发器而不是PHP代码,并且必须将相关的user_id
存储到diskusage
表中。
I use
InnoDB
engine because of theCONSTRAINT
. 由于使用CONSTRAINT
我使用InnoDB
引擎。 You can also useMyISAM
, but you should remove theCONSTRAINT
. 您也可以使用MyISAM
,但应删除CONSTRAINT
。
REMARK 备注
I would use InnoDB
because of Transactions and (more important here) Row-Locking . 由于事务和(这里更重要的是) 行锁定,我将使用InnoDB
。
Table Structure (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;
Table Structure (MyISAM) 表结构(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;
Thats all, together with some triggers on table diskusage
. diskusage
,还有表diskusage
上的一些触发器。
INSERT TRIGGER 插入触发
-- ----------------------------
-- 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 TRIGGER 更新触发
-- ----------------------------
-- 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 ;
Operations for MyISAM tables are atomic . MyISAM表的操作是原子的 。 Basically, the queries are automatically committed after they happen. 基本上,查询是在查询发生后自动提交的。
In addition, UPDATE
s are blocking, meaning that only one can occur at a time. 另外, UPDATE
处于阻塞状态,这意味着一次只能发生一次。
This means that the read-write cycle of the UPDATE
will not be interrupted. 这意味着UPDATE
的读写周期不会被中断。
MySQL uses table locking for MyISAM tables. MySQL对MyISAM表使用表锁定 。
This is fairly quick and will ensure that concurrent updates work correctly. 这相当快,可以确保并发更新正常工作。
However, lots of updates may result in the table spending a lot of time being locked. 但是,大量更新可能导致表花费大量时间被锁定。 If you have many rows in your table, this may become problematic. 如果表中有很多行,则可能会出现问题。
InnoDB tables support row-locking. InnoDB表支持行锁定。 This takes more resources, but may be much more appropriate if your table gets large. 这会占用更多资源,但如果表很大,可能会更合适。 It gives you finer control over the locking and would allow multiple, unrelated, processes to access the table, without excessive lock contention. 它使您可以更好地控制锁定,并允许多个不相关的进程访问表,而不会产生过多的锁定争用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.