MySQL:如何锁定表并启动事务?

MySQL:如何锁定表并启动事务?,mysql,transactions,locking,data-consistency,Mysql,Transactions,Locking,Data Consistency,TL;DR-MySQL不允许您在锁定表的同时使用事务。这有什么办法吗 我有一个MySQL表,用于缓存(速度较慢)外部系统中的一些数据。数据用于显示网页(用PHP编写)。每隔一段时间,当缓存的数据被认为太旧时,其中一个web连接应触发缓存数据的更新 我必须处理三个问题: 在我更新缓存数据时,其他客户端将尝试读取缓存数据 多个客户端可能会认为缓存数据太旧,并尝试同时更新它 执行此工作的PHP实例可能随时意外终止,并且数据不应损坏 我可以通过使用事务解决第一个和最后一个问题,这样客户机将能够读取旧

TL;DR-MySQL不允许您在锁定表的同时使用事务。这有什么办法吗

我有一个MySQL表,用于缓存(速度较慢)外部系统中的一些数据。数据用于显示网页(用PHP编写)。每隔一段时间,当缓存的数据被认为太旧时,其中一个web连接应触发缓存数据的更新

我必须处理三个问题:

  • 在我更新缓存数据时,其他客户端将尝试读取缓存数据
  • 多个客户端可能会认为缓存数据太旧,并尝试同时更新它
  • 执行此工作的PHP实例可能随时意外终止,并且数据不应损坏
我可以通过使用事务解决第一个和最后一个问题,这样客户机将能够读取旧数据,直到提交事务,此时他们将立即看到新数据。任何问题都会导致事务回滚

我可以通过锁定表来解决第二个问题,这样只有一个进程有机会执行更新。当任何其他进程获得锁时,他们将意识到自己已经被打败了,不需要更新任何东西

这意味着我需要锁定表并启动事务。根据MySQL手册。启动事务会释放锁,锁定表会提交任何活动事务


有没有办法解决这个问题,或者有没有其他完全可以实现我的目标的方法?

第二个问题可以在根本不涉及数据库的情况下解决。为缓存更新过程准备一个锁文件,以便其他客户端知道有人已经在上面了。这可能不会抓住每一个角落,但如果两个客户端同时更新缓存,这有什么大不了的吗?毕竟,他们所做的更新在事务缓存中仍然是一致的

您甚至可以通过将上次缓存更新时间存储在表中来实现锁定。当客户端需要更新缓存时,使其锁定该表,检查上次更新时间,然后更新该字段


即,实现您自己的锁定机制,以防止多个客户端更新缓存。事务将处理其余部分。

如果是我,我将使用MySQL内部的实现一个用于更新缓存的互斥锁,以及一个用于读取隔离的事务。e、 g

begin_transaction(); // although reading a single row doesnt really require this
$cached=runquery("SELECT * FROM cache WHERE key=$id");
end_transaction();

if (is_expired($cached)) {
   $cached=refresh_data($cached, $id);
}
...

function refresh_data($cached, $id)
{
 $lockname=some_deterministic_transform($id);
 if (1==runquery("SELECT GET_LOCK('$lockname',0)") {
    $cached=fetch_source_data($id);
    begin_transaction();
    write_data($cached, $id);
    end_transaction();
    runquery("SELECT RELEASE_LOCK('$lockname')");
 }
 return $cached; 
}
(顺便说一句:如果您尝试使用持久连接,可能会发生不好的事情)

这意味着我需要锁定表并启动事务

这就是你可以做到的:

SET autocommit=0;
LOCK TABLES t1 WRITE, t2 READ, ...;
... do something with tables t1 and t2 here ...
COMMIT;
UNLOCK TABLES;

有关更多信息,请参见

我建议通过完全删除争用来解决此问题

将时间戳列添加到缓存的数据中

需要更新缓存数据时:

  • 只需使用当前时间戳向表中添加新的缓存数据
  • 删除早于(比如)24小时的缓存数据
当您需要为缓存的数据提供服务时

  • 按时间戳(DESC)排序并返回最新的缓存数据

在任何给定的时间,您的客户端都将检索从未被任何其他进程删除的记录。此外,您不关心客户端是否获得属于不同写入(即具有不同时间戳)的缓存数据。

不幸的是,锁定文件在这里不起作用,因为我们有多个web服务器运行站点文件的本地副本。因此,一个web服务器不会看到另一个服务器创建的锁文件。我将上一次更新时间存储在一个表中,但在缓存刷新完成之前不能更新该时间-否则,如果时间被更新,然后进程被终止(根据第3点),数据将不会被刷新,但上一次更新时间将错误地说是。在整个更新过程中锁定该表将中断点1,在刷新期间阻止其他客户端读取旧数据。这看起来确实可行,但仅适用于InnoDB表(如果这是一个问题的话)