2012-08-22 97 views
4

TL; DR - MySQL不允许您锁定表并同时使用事务。有没有办法解决?MySQL:如何锁定表并启动事务?

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

有三个问题,我不得不面对:

  • 其他客户端将尝试读取缓存中的数据,而我更新它
  • 多个客户端可以决定缓存的数据太旧,并尝试同时
  • PHP的情况下做的工作可以在任何时候意外终止更新,数据不应该被破坏

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

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

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

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

回答

4

如果是我,我会使用MySQL中的advisory locking function来实现更新缓存的互斥锁和读取隔离的事务。例如

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; 
} 

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

+0

谢谢,这似乎工作得很好! – Malvineous

0

第二个问题可以根本不涉及数据库来解决。为缓存更新过程提供一个锁定文件,以便其他客户端知道有人已经在其上。这可能无法捕捉到每一个角落的情况,但是如果两个客户端同时更新缓存,这是否成功?毕竟,他们正在对缓存进行更新的事务仍然是一致的。

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

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

+0

不幸的是锁定文件在这里不起作用,因为我们有多个网站服务器运行本地站点文件副本。所以一个Web服务器将看不到另一个创建的锁文件。我将最后一次更新时间存储在一个表中,但在缓存刷新完成之前无法更新 - 否则,如果时间更新并且进程被终止(每点3),数据将不会被刷新,但最后更新时间将错误地说是。将整个表锁定为整个更新将断开点1,阻止其他客户端在刷新期间读取旧数据。 – Malvineous

5

这意味着我需要在两个锁表,并开始交易

这是怎么了你可以这样做:

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

欲了解更多信息,请参阅mysql doc

+0

这看起来确实会起作用,但只适用于InnoDB表(如果这是一个问题)。 – Malvineous