繁体   English   中英

同时更新mysql MyISAM表中的计数器

[英]Concurrently update counter in a mysql MyISAM table

我有一个客户端服务器应用程序,可将用户数据发送到云(Amazon EC2 + RDS + S3)。

  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。

这就是说, 如果需要的话,我总是可以进行切换(尽管我想避免这种情况)

如果要跟踪文件,大小和所有权,请SELECT SUM(Size) FROM Files WHERE UserID = ? 除非用户拥有与其相关联的不计其数的文件,否则它将在索引正确的表上快速增长。 无需存储数字即可轻松计算出该数字。

您应该使用MySQL触发器而不是PHP代码,并且必须将相关的user_id存储到diskusage表中。

由于使用CONSTRAINT我使用InnoDB引擎。 您也可以使用MyISAM ,但应删除CONSTRAINT

备注

由于事务和(这里更重要的是) 行锁定,我将使用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 ,还有表diskusage上的一些触发器。

插入触发

-- ----------------------------
--  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 ;

更新触发

-- ----------------------------
--  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 ;

删除触发器

-- ----------------------------
--  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 ;

MyISAM表的操作是原子的 基本上,查询是在查询发生后自动提交的。

另外, UPDATE处于阻塞状态,这意味着一次只能发生一次。

这意味着UPDATE的读写周期不会被中断。

MySQL对MyISAM表使用表锁定

这相当快,可以确保并发更新正常工作。

但是,大量更新可能导致表花费大量时间被锁定。 如果表中有很多行,则可能会出现问题。

InnoDB表支持行锁定。 这会占用更多资源,但如果表很大,可能会更合适。 它使您可以更好地控制锁定,并允许多个不相关的进程访问表,而不会产生过多的锁定争用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM