php - 在 mysql MyISAM 表中同时更新计数器

标签 php mysql database codeigniter concurrency

我有一个将用户数据发送到云端的客户端-服务器应用程序 (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;

如果我选择使用触发器,我应该如何动态注入(inject)用户 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 与数据库相关的代码封装在模块化函数中(即 InsertIntoDB()、UpdateDB() 和 DeleteFromDB())。此外,所有这些例程都依赖于带有事件记录类的 CodeIgniter 2.1。

这就是说,如果我必须这样做,我总是可以进行切换(尽管我想避免这种情况)

最佳答案

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

I use InnoDB engine because of the CONSTRAINT. You can also use MyISAM, but you should remove the CONSTRAINT.

备注

我会使用 InnoDB 因为 Transactions 和(这里更重要)Row-Locking

表结构 (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 上的一些触发器。

插入触发器

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

关于php - 在 mysql MyISAM 表中同时更新计数器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14111487/

相关文章:

javascript - 动态添加行上的数组单选按钮

mysql - 在 bash 脚本中执行多个 MySQL 查询

php - 在 PHPDoc 中记录数组选项的最佳方式?

php - 如何使用 PHP PDO 解析 MySQL 数据库中的对象数据?

php - MySQL 在 PHP 中按错误排序

sql - 过程数据库代码与多个数据库调用

C# - 使项目兼容 MySQL 和 SQL Server

mysql - 从 AngularJS 中的数据库读取

javascript - 将文本区域数据发送到数据库时出现问题

php - 在 "anonymous"对象中使用 $this