在mysql中管理行过期的最佳方法

Posted

技术标签:

【中文标题】在mysql中管理行过期的最佳方法【英文标题】:Best way to manage row expiration in mysql 【发布时间】:2014-01-11 17:29:59 【问题描述】:

应用程序执行以下操作:

向具有唯一 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 作业 将字段值从 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 作业每 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);

【问题讨论】:

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

您没有提及您使用的是哪个版本的 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

然后您可以看到一种方式与另一种方式相比需要多长时间。

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

【讨论】:

如果您在创建触发器时需要帮助,请告诉我。 很好的答案,我会测试并报告。这是否意味着我什至不需要表中的时间戳列?我知道调度程序,只是想知道它是否比 cron 作业更重,但考虑到 mysql 具有 current_timestamp 和 timestampdiff 功能,是的,这似乎是要走的路。 是的,您需要时间戳列。我的代码指的是它。 另外,您认为在性能和资源使用方面如何?解决方案 1 将使用更少的资源,因为 UPDATE 不那么繁重,但读取性能会随着列数的增加而下降,在这种情况下,我们仍然必须决定是在状态列还是时间戳列上对表进行索引或分区。第二种解决方案在读取表时显然非常快,但可能会使用更多资源,因为在新表中删除和插入更重,并且有一种守护进程一直在执行 BEGIN 插入和删除 END 可能会出现问题?跨度>

以上是关于在mysql中管理行过期的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

测试 MySQL 表中是不是存在行的最佳方法

测试 MySQL 表中是不是存在行的最佳方法

如果存储在本地存储中,检查 jwt 令牌过期状态的最佳方法

选择行mysql的最佳方法

在源代码管理中存储 MySQL 数据库的最佳方式是啥?

使用 Datatables 在 Laravel 中处理大量行的最佳方法是啥?