我有一个每天自动更新一次的mysql表。执行此更新的脚本与下面的PHP代码类似。它基本上做了什么,它使一个具有相同结构的临时表,首先插入所有数据,然后截断实际表并从临时表中插入数据。最后临时表被销毁了。使用此方法是因为它显着降低了实际表的停机时间。
很长一段时间以来一切正常。直到最近,随着Debian的升级(从Debian 8到Debian 9,这也意味着从MySQL切换到MariaDB)。现在我经常在日志文件中找到此错误消息:
[Wed Sep 27 06:03:04.903652 2017] [:error] [pid 27393] [client 127.0.0.1:36794] PHP Fatal error: Uncaught PDOException: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'database.table_temp' doesn't exist in /someFile.php:50\nStack trace:\n#0 /someFile.php(50): PDOStatement->execute()\n#1 {main}\n thrown in /someFile.php on line 50
此错误最终会导致某些数据丢失。如果我理解正确,它说临时表在插入完成之前就被销毁了,对吧?但是如果发生这种情况,考虑到这不是真正的TEMPORARY TABLE
,我只使用一个真实的表并将其命名为temp
。 MariaDB是否使INSERT延迟,以便php脚本可以在INSERT完成之前执行DROP?
这是cron.d条目,因此该脚本实际上每天只执行一次:
1 6 * * * root /usr/bin/wget -q http://www.example.com/someFile.php -O /dev/null
同样值得注意的是,脚本在一两个小时内完成执行,因此前一天的执行时间很长,并且不会干扰。
这基本上就是代码:
$dataArray = []; // much data
$db->query("DROP TABLE IF EXISTS `table_temp`;");
$db->query("CREATE TABLE `table_temp` LIKE `table`;");
$tres = $db->prepare("
INSERT INTO `table_temp`
(`field1`, `field2`)
VALUES
(:field1, :field2)
");
foreach($dataArray as $data){
$db->beginTransaction();
$res->execute();
foreach($data as $item){
$tres->bindParam('field1', $item['field1'], PDO::PARAM_INT);
$tres->bindParam('field2', $item['field2']);
$tres->execute(); // line 50
}
$db->commit();
}
$db->query("RENAME TABLE `table` TO `table_old`, `table_temp` TO `table`");
$db->query("DROP TABLE `table_old`");
答案 0 :(得分:0)
通过锁定延迟的命令被添加到"队列"按照他们被称为的顺序。
mySql命令DROP
和CREATE
都会在受影响的表上应用TABLE_LOCK
。这意味着在CREATE
释放DELETE
之前,您的TABLE_LOCK
不会执行。
可能发生的事情是,要运行的命令的顺序可能如下所示:(因为准备和排队CREATE
命令需要的时间比前几个INSERTS
)
...它只是继续发送命令的快乐方式。 那个是您问题的根源。
无法使用交易来解决问题。 CREATE and DROP will commit a transaction
$db
对象。使用一个会根据查询的成功执行返回结果,例如PDO::exec。然后你应该能够将你的INSERT
语句包装在一个条件块中(PHP必须等待响应才能检查条件)。
$result = $db->exec('CREATE ...');
if (false !== $result && $result >= 0) {
foreach($dataArray as $data){
// INSERT
}
}
答案 1 :(得分:0)
打嗝的可能性为零:
CREATE TABLE new LIKE real;
...load the data into `new`...
RENAME TABLE real TO old, new TO real; -- Atomic and 'instantaneous'
DROP TABLE old;
不需要交易等等。
一个潜在的问题是“负载”。可能是从一些移动目标中获取数据。
另一项优化
使用'批次' INSERT
- 这是一个包含多行的INSERT
语句。它的速度是1行INSERTs
的等效数的10倍。我建议每批100-1000行。