【问题标题】:Alternative to block a table阻止表的替代方法
【发布时间】:2011-08-11 11:17:05
【问题描述】:

我有一个名为“消息”(INNODB) 的表,用户可以在其中插入自己的帖子。 我想设置一个限制。在我的 php 脚本中,当表达到 10 条记录时,您不能添加更多。程序的逻辑大致如下。

第 1 步。我运行一个查询来计算表中的行数。

第 2 步。恢复该值,我决定是否插入新帖子。

我的困难在于正确管理两个用户同时做同样事情的可能性。如果用户 A 在第 1 步,而用户 B 刚刚输入第十个帖子,则用户 A 将包含第十一个。

如何避免?

【问题讨论】:

  • 你能发布一些你的php代码吗?

标签: php mysql


【解决方案1】:

您可以创建CHAR(1) NOT NULL 字段并用UNIQUE INDEX 覆盖它。这将防止插入超过 10 行。

其他可行的解决方案是创建 BEFORE INSERT 触发器,该触发器检查行数并在超过 10 行时引发错误 (look here for sample)(但在这种情况下,您可能会因条件竞争而失败)。

【讨论】:

  • @Martin:它是一个 char 字段,长度为1。所以它将包含0..9 值(假设你永远不会尝试在那里写非数字);-)
  • 我指的是您评论中的括号部分。检查约束可以强制执行(在 MySQL 中)吗?
  • @Martin:嗯,不。处理可能的值完全是程序员的工作。我们假设开发人员将使用0..9 值。如您所知,mysql 中没有(?)“真正的”检查约束(例如,oracle 有),所以我想不出任何“正确”的解决方案。
  • BEFORE INSERT 触发器(与锁定一起使用)是这里唯一真正的解决方案 - 索引方法仅适用于值 10,虽然它回答了这个特定问题,但它为您提供了一个解决方案无法更改 - 如果他们将要求更改为 15 条记录,那么您将不得不使用触发方法无论如何 - 它更易于维护。
【解决方案2】:

为了允许您更改表格的阈值,您可以使用触发器。因为 MySQL 触发器没有“阻止INSERT”选项,所以您需要将表中的值设置为NOT NULL。然后触发器可以将该列的插入值设置为NULL,如果条件检查失败,这将阻止INSERT

这样的触发器:

CREATE TRIGGER block_insert 
BEFORE INSERT ON table_name 
FOR EACH ROW 
BEGIN   
  DECLARE count INT;
  SELECT COUNT(*) 
  FROM table_name INTO count;
  IF count >= 10 
  THEN
    SET NEW.non_nullable_value = NULL;   
  END IF; 
END;

如果您插入第 11 行将失败,如下所示:

ERROR 1048 (23000): Column 'non_nullable_value' cannot be null

您可能希望将不可为空的列的名称设置为代表其用途的名称。您可以通过让触发器从配置表中提取限制值来改善这一点。

更新

为了避免必须使用不可为空的列,您也可以创建一个错误过程,并从您的触发器中创建CALL - 类似于example in the "Emulating Check Constraints" section of this page - 它们引用 Oracle 数据库,其中实现了检查约束你想要什么,但 MySQL 不支持它们。

示例中的“错误过程”将重复行的INSERT 执行到错误表中,导致唯一键错误并停止父事务。

更新 2

正如下面评论中所指出的,多个同时交易可能会绕过检查 - 您必须使用 LOCK TABLES <name> WRITE 以确保它们不能。

【讨论】:

  • 触发解决方案很方便,但受竞态条件影响。
  • 竞争条件可以通过正确使用锁定来解决。遗憾的是 MySQL 缺少检查约束,但这意味着使用触发器是唯一真正的解决方法。
【解决方案3】:

2/ 你也可以锁定 MySQL 表。

  • 执行:锁定表 my_table
  • 然后按照您的业务规则行事。
  • 执行:解锁表

这也确保了每个动作都是按顺序执行的。 (但你必须处理性能开销)

希望这可能有用。

自以下 cmets 更新:在这种情况下事务不起作用

1/你使用的是InnoDB,你也可以使用数据库事务

  • 开启交易
  • 那就做你的业务规则吧。
  • 提交或回滚您的事务

这将确保每个动作一个接一个地执行。

【讨论】:

  • 用于解决并发问题。 @trickwallett 说:我的困难在于正确管理两个用户同时做同样事情的可能性。如果用户 A 在步骤 1 中,而用户 B 刚刚完成输入第十个帖子,则用户 A 将包括第十一个。如何避免?
  • 我认为@zerkms 的意思是事务元素本身并没有真正帮助,它是LOCK TABLES 阻止并发(错误)INSERT 成功。即使这两个操作都发生在它们自己的事务中,它们都将能够毫无错误地提交,留下超过 10 行。 不过,您对LOCK TABLES 的建议很满意
  • 感谢解释,在与 InnoDB 的事务期间,“读取”也被锁定。 (实际上我正在使用 LOCK TABLES 技巧来模拟 pk 的序列)
猜你喜欢
  • 2013-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-09
  • 2018-07-20
  • 2014-04-07
  • 2013-08-19
相关资源
最近更新 更多