问题 在mysql中管理行到期的最佳方法


应用程序执行以下操作:

  • 将行写入具有唯一ID的表
  • 读取表并找到唯一ID并输出其他变量(其中包括时间戳)。

问题是:应用程序需要只读取未过期的行,这些行每2分钟到期一次。有几种方法可以实现这一目标:性能最佳?

考虑读取过期的行并不重要,因为它会偶尔完成。非过期行的数量总是低于几千,但可能只有几百行。

执行此操作的cron作业(每分钟运行)(或mysql事件计划)可以是基于时间戳的以下之一(或任何其他想法?):

A)添加一个BOL变量来索引表,然后读取WHERE未过期(当然基于boll变量) B)添加一个BOL变量来分区表,然后只读相关的分区(我是分区的新手,所以我不知道这可能会有什么效果) C)读取整个表并删除已过期的每一行,然后将同一行写入另一个表 D)写入时,在两个表中同时写两行,然后在一个表中删除过期的行

要么

E)根本不使用cron作业并检查每次读取的时间戳。 (但为什么我要扫描整个表?过期的行对应用程序本身基本上没用)

编辑1

让我重新解释一下这个问题:

目标是仅在行少于2分钟前写入行时检索行的所有列。

该表具有此结构,但可以完全重新定义

transactionID CHAR(8) NOT NULL, 
PRIMARY KEY(transactionID),
details VARCHAR(416),
tel INT(10),
time TIMESTAMP(),
address VARCHAR(60),
town VARCHAR(30),
flat VARCHAR (5),
supplier SMALLINT()

supplier 是一把外键

索引是 transactionID 并最终“status“,一个额外的列,数据类型TO_BE_DEFINED_but_probably_SMALLINT _?()

解决方案1:使用索引列指示行的状态(已过期,活动,已使用)

这是实现运行的 cron job 将字段值从1(活动)更改为2(已过期),查询将如下所示:

$transaction = //a POST or GET 

$query = mysqli_query($con,"SELECT * FROM table WHERE transaction = '$transaction' AND status = 1");

if(mysql_num_rows($query)== 0){
   echo "Transaction expired";
}
else{
// I will show all the fields in the selected row;
}

mysqli_close($con);

解决方案2:根据每行的时间戳使用分区

这是实现运行的 cron job 每2分钟对列进行一次分区,但查询可能更快:

$transaction = //a POST or GET 

$query = mysqli_query($con,"SELECT * FROM table WHERE transaction = '$transaction' AND PARTITION (active)");

if(mysql_num_rows($query)== 0){
   echo "Transaction expired";
}
else{
// I will show all the fields in the selected row;
}

mysqli_close($con);

然后,cron工作将与此类似

$date = date("Y-m-d H:i:s");
$time = strtotime($date);
$check = $time - (2);


$query = mysqli_query($con,"PARTITION BY RANGE (timestamp_field) 
(PARTITION active VALUES LESS THAN ($check)),
((PARTITION expired VALUES MORE THAN ($check));")

解决方案3:忘记所有这些并将时间戳大于2分钟的行复制到另一个带有cron作业的表

虽然脚本在写入方面可能很重,但是很容易实现,但是对于“过期”表的写入效率是无关紧要的,我希望“活动”查询快速。

解决方案3B:添加行时,也将其写入另一个表,以便它只是cron作业必须执行的DELETE函数。

解决方案4:忘记它的所有内容和时间戳上的WHERE

$transaction = //a POST or GET 

$date = date("Y-m-d H:i:s");
$time = strtotime($date);
$check = $time - (2);

$query = mysqli_query($con,"SELECT * FROM table WHERE transaction = '$transaction' AND timestamp > $check");

if(mysql_num_rows($query)== 0){
   echo "Transaction expired";
}
else{
// I will show all the fields in the selected row;
}

mysqli_close($con);

7862
2017-12-23 13:24


起源

你的问题陈述不是很清楚。当你说 BOL,你的意思是布尔或其他什么?您的目标是加快读取未过期的行,还是删除过期的行?你说 WHERE 搜索然后在相同的段落中删除行,然后再次关于重复行。还请提一下是什么 目前的表现 和 当前表格大小 (如果可供使用的话)。最后,如果过期的行没用;为什么你甚至想要经历这个练习,因为它们只有几千(这是无关紧要的)。 - Burhan Khalid
听起来对你说你做的事很简单非常复杂..你每2分钟就会到期记录..所以你也每隔2分钟插入/更新一次未过期的记录吧?如果不是你应该详细说明!!因为听起来你想要实现自定义 MultiVersion Concurrency Control (MCC或MVCC) - Raymond Nijland
@BurhanKhalid目标是加速读取未过期的行。通过BOL我的意思是一个变量来检查是否过期/未过期,但如果行的状态类型也可以是第三个,也可以是tinyINT。我写BOl的原因是我对基于变量类型的索引性能了解不多,而且我猜测布尔会更快。正在开发应用程序时,目前没有表格大小。根据你的最后一行:我为什么要让查询在整个表中运行,过期的行将远远超过几千行。 - smartcity
@RaymondNijland我只更新记录以改变他们的状态。它们是交易行,当它们过期时,它们仍然用于统计/计费目的。我对MCC的理解是它用于创建同一行的不同版本,但在我的情况下,行会(永久地)更改为应用程序不再使用它的状态(除了后台零星的读取访问) - smartcity
@ user3129652认为我们仍然需要输出off show create table [table_name] ...如果我正确理解你正在“删除”(读取过期)记录,当你将状态列更新为零(0)或类似的东西时那..你很可能在状态列上有一个索引,用于选择未过期的行而不使用全表扫描?如果是这样状态列的更新非常“昂贵”,因为数据和索引需要更新.. - Raymond Nijland


答案:


您没有提到您正在使用的MySQL版本。只要你有5.1,你就拥有 事件调度程序

CREATE EVENT clearExpired
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 2 MINUTES
DO
   UPDATE table SET status=0
    WHERE status <> 0
      AND TIMESTAMPDIFF(SECONDS, NOW(),table.timestamp)>120;

要么

CREATE EVENT clearExpired
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 2 MINUTES
DO
   DELETE FROM table
    WHERE TIMESTAMPDIFF(SECONDS, NOW(),table.timestamp)>120;

在这种情况下,您还可以设置一个触发器,在删除之前将所有已删除的行复制到历史记录表中。

为了衡量效率,请创建一个表格 events (列:时间戳和操作),并在表中生成大量行,然后将您的事件更改为:

...
DO
   BEGIN
      INSERT INTO events SET action="clearExpired start";
      <insert or delete>
      INSERT INTO events SET action="clearExpired end";
   END

然后你可以看到它与另一种方式需要多长时间。

但是,此事件每分钟或每两分钟只运行一次,而将有更多插入。您希望优化最常发生的操作。


12
2017-12-27 22:35



如果您需要帮助创建触发器,请告诉我。 - Marjeta
很好的答案,我会测试并报告。这是否意味着我甚至不需要表中的timestamp列?我知道调度程序,只是想知道它是否比cron作业更重,但考虑到mysql有current_timestamp和timestampdiff函数是的,它似乎要走了。 - smartcity
是的,您需要时间戳列。我的代码指的是它。 - Marjeta
您还对性能和资源使用情况有何看法?解决方案1将使用更少的资源,因为UPDATE不太重,但读取性能会因大量列而降低,在这种情况下,我们仍然需要决定是在状态列还是时间戳列上对表进行索引或分区。第二种解决方案显然是超快速读取表格,但可能会使用更多的资源,因为删除和插入新表格更重,并且有一种deamon做BEGIN插入并一直删除END可能代表一个问题? - smartcity
现在的事情对你有用吗? - Marjeta