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

时间:2012-08-22 10:08:35

标签: mysql transactions locking data-consistency

TL; DR - MySQL不允许您锁定表并同时使用事务。有没有办法解决这个问题?

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

我必须处理三个问题:

  • 其他客户端会在我更新时尝试读取缓存数据
  • 多个客户端可能会判断缓存数据太旧,并尝试同时更新缓存数据
  • 执行工作的PHP实例可能会在任何时候意外终止,并且数据不应该被破坏

我可以通过使用事务解决第一个和最后一个问题,因此客户端将能够读取旧数据,直到提交事务,他们将立即看到新数据。任何问题都只会导致事务被回滚。

我可以通过锁定表来解决第二个问题,这样只有一个进程有机会执行更新。当任何其他进程获得锁定时,他们将意识到他们已被击败,无需更新任何内容。

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

有没有办法解决这个问题,还是有另外一种方法来实现我的目标?

4 个答案:

答案 0 :(得分:5)

  

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

这是你可以做到的:

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

有关详细信息,请参阅mysql doc

答案 1 :(得分:4)

如果是我,我会使用MySQL中的advisory locking function来实现用于更新缓存的互斥锁,以及用于读取隔离的事务。 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; 
}

(顺便说一句:如果你用持久连接尝试这个可能会发生不好的事情)

答案 2 :(得分:2)

我建议通过完全消除争用来解决问题。

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

当您需要更新缓存的数据时:

  • 只需使用当前时间戳将新的缓存数据添加到表中
  • 删除比24小时更早的缓存数据。

何时需要提供缓存的数据

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

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

答案 3 :(得分:0)

第二个问题可以在不涉及数据库的情况下解决。拥有缓存更新过程的锁定文件,以便其他客户端知道某人已经在其上。这可能无法捕获每个角落的情况,但如果两个客户端同时更新缓存,那么这是一件大事吗?毕竟,他们正在对缓存中的事务进行更新仍然是一致的。

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

即,实现自己的锁定机制以防止多个客户端更新缓存。交易将负责其余的工作。