MySQL:如何锁定表并启动事务?
TL;DR-MySQL不允许您在锁定表的同时使用事务。这有什么办法吗 我有一个MySQL表,用于缓存(速度较慢)外部系统中的一些数据。数据用于显示网页(用PHP编写)。每隔一段时间,当缓存的数据被认为太旧时,其中一个web连接应触发缓存数据的更新 我必须处理三个问题:MySQL:如何锁定表并启动事务?,mysql,transactions,locking,data-consistency,Mysql,Transactions,Locking,Data Consistency,TL;DR-MySQL不允许您在锁定表的同时使用事务。这有什么办法吗 我有一个MySQL表,用于缓存(速度较慢)外部系统中的一些数据。数据用于显示网页(用PHP编写)。每隔一段时间,当缓存的数据被认为太旧时,其中一个web连接应触发缓存数据的更新 我必须处理三个问题: 在我更新缓存数据时,其他客户端将尝试读取缓存数据 多个客户端可能会认为缓存数据太旧,并尝试同时更新它 执行此工作的PHP实例可能随时意外终止,并且数据不应损坏 我可以通过使用事务解决第一个和最后一个问题,这样客户机将能够读取旧
- 在我更新缓存数据时,其他客户端将尝试读取缓存数据
- 多个客户端可能会认为缓存数据太旧,并尝试同时更新它
- 执行此工作的PHP实例可能随时意外终止,并且数据不应损坏
有没有办法解决这个问题,或者有没有其他完全可以实现我的目标的方法?第二个问题可以在根本不涉及数据库的情况下解决。为缓存更新过程准备一个锁文件,以便其他客户端知道有人已经在上面了。这可能不会抓住每一个角落,但如果两个客户端同时更新缓存,这有什么大不了的吗?毕竟,他们所做的更新在事务缓存中仍然是一致的 您甚至可以通过将上次缓存更新时间存储在表中来实现锁定。当客户端需要更新缓存时,使其锁定该表,检查上次更新时间,然后更新该字段
即,实现您自己的锁定机制,以防止多个客户端更新缓存。事务将处理其余部分。如果是我,我将使用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表(如果这是一个问题的话)