TL; DR - MySQL不允许您锁定表并同时使用事务。有没有办法解决这个问题?
我有一个MySQL表,用于缓存(慢)外部系统中的一些数据。数据用于显示网页(用PHP编写。)每隔一段时间,当缓存的数据被认为太旧时,其中一个网络连接应该触发缓存数据的更新。
我必须处理三个问题:
我可以通过使用事务解决第一个和最后一个问题,因此客户端将能够读取旧数据,直到提交事务,他们将立即看到新数据。任何问题都只会导致事务被回滚。
我可以通过锁定表来解决第二个问题,这样只有一个进程有机会执行更新。当任何其他进程获得锁定时,他们将意识到他们已被击败,无需更新任何内容。
这意味着我需要同时锁定表和启动事务。根据MySQL手册,this is not possible。启动事务会释放锁定,锁定表会提交任何活动事务。
有没有办法解决这个问题,还是有另外一种方法来实现我的目标?
答案 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)
我建议通过完全消除争用来解决问题。
将时间戳列添加到缓存的数据。
当您需要更新缓存的数据时:
何时需要提供缓存的数据
在任何给定时间,您的客户将检索从未被任何其他进程删除的记录。而且,您不必担心客户端是否获得了属于不同写入(即具有不同时间戳)的缓存数据
答案 3 :(得分:0)
第二个问题可以在不涉及数据库的情况下解决。拥有缓存更新过程的锁定文件,以便其他客户端知道某人已经在其上。这可能无法捕获每个角落的情况,但如果两个客户端同时更新缓存,那么这是一件大事吗?毕竟,他们正在对缓存中的事务进行更新仍然是一致的。
您甚至可以通过将最后一次缓存更新时间存储在表中来实现锁定。当客户端想要更新缓存时,使其锁定该表,检查上次更新时间,然后更新该字段。
即,实现自己的锁定机制以防止多个客户端更新缓存。交易将负责其余的工作。