我正在尝试使用mysqli来插入数据并且存在一些奇怪的行为。例如,当我第一次使用$mysqli->autocommit(FALSE);
并花费几分钟来运行我的PHP并等待提供的查询时,它会将数据库保留到$mysqli->commit();
,因此我无法执行任何其他数据库操作。当我检查phpmyadmin中的状态时,它显示即将到来的SQL查询状态为Waiting for table metalock
,如何解决?感谢
/* Insert log Query */
function putLog($query){
global $mysqli,$ip,$browser,$dateLog,$isQuerySuccess;
$isQuerySuccess = $mysqli->query("INSERT INTO DPS_Log_$dateLog (PageID,FunctionID,ActionID,UserID,UserIP,UserInfo,LogType,Remark,LogTime) VALUES (15,20,25,25,'$ip','$browser',1,'$query',NOW())") ? true : false;
}
/* Start DB connection */
$mysqli = new mysqli(DATABASEIP, DBUSER, DBPWD, DATABASE,PORT);
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$mysqli->autocommit(FALSE);
$isQuerySuccess = true;
putLog ("Fail to delete: $folderPath.$item");
$isQuerySuccess ? $mysqli->commit() : $mysqli->rollback();
$mysqli->close();
更新: 我终于发现问题是由另一个查询引起的。简而言之,上面的编码是插入日志,而下面的查询是检查用户登录时是否存在日志表。问题是,当我打开一个事务并尝试记录操作(操作需要> 30秒)结果时,我无法执行下面的查询(等待表metalock)所以整个系统一直保持到操作完成,如何要解决这个问题?感谢
$sql = "
CREATE TABLE IF NOT EXISTS `$logTableName` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`PageID` int(2),
`FunctionID` int(2),
`ActionID` int(3) NOT NULL,
`UserID` int(5) NOT NULL,
`UserIP` varchar(15) COLLATE utf8_unicode_ci NOT NULL,
`UserInfo` text COLLATE utf8_unicode_ci NOT NULL,
`LogType` int(1) NOT NULL DEFAULT '1',
`Remark` text COLLATE utf8_unicode_ci NOT NULL,
`LogTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`ID`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;
";
$this -> databaseHelper -> common_query($sql);
答案 0 :(得分:5)
正确的顺序是:
$mysqli->autocommit(FALSE);
$mysqli->query("INSERT INTO ...");
$mysqli->commit();
但是对于一个小插件来说这样做很奇怪。在您的特定情况下,仅执行此操作就足够了:
$mysqli->query("INSERT INTO ...);
没有自动提交禁用和没有提交。
答案 1 :(得分:2)
CREATE TABLE your_table
需要对您的表进行独占锁定才能进行处理,但并发INSERT INTO your_table
已经在此表上放置了一个独占锁。 CREATE TABLE
被搁置直到并发事务结束,这是预期的行为。
由于您计划在表格存在(IF NOT EXISTS
)时不做任何事情,您可以在发出罪魁祸首CREATE TABLE
之前检查该表是否存在:
SELECT COUNT(*) FROM information_schema
WHERE table_schema = 'your database here'
AND table_name = 'your table name here';
首先发出此(非阻止)查询,然后仅在上述内容返回CREATE TABLE IF EXISTS
时才发出后续0
。
虽然不太可能,并发事务可能会在上述两个语句的执行过程中创建表并锁定它。这是因为information_schema
表是MEMORY
表,这个引擎不支持事务(也没有任何形式的锁定)。因此,请不要删除IF EXISTS
子句。
您可以通过创建自己的(InnoDB)跟踪表来规避此限制。
答案 2 :(得分:1)
Please increase your php execution time by php.ini setting
step 1 open php.ini
step 2 search max_execution_time
step 3 set max_execution_time = 3000
step 4 save it
step 5 Restart apache
step 6 run the program again
答案 3 :(得分:1)
基本上DDL操作与事务不兼容。 DDL是数据定义语言的简写,是指CREATE,ALTER和DROP语句。
尽量避免动态创建表。并且如果需要,手动创建所有表或使用一次性脚本。
如果绝对必要,请在打开事务之前尝试创建表。
答案 4 :(得分:1)
首先,不应该只为一个查询打开事务。交易需要付费。只有在真正需要的时候才支付费用。
其次,动态创建表不是一个好主意。它显示了糟糕的架构设计的叹息。
假设您共享的两个查询必须在一对中执行,最好的方法是打开一个事务。在这种情况下,在第一个查询之前打开事务,并在任何方式失败时关闭它,这样就不需要整个操作。 您还可以使用Mysql Savepoint回滚部分而不会弄乱整个事务。
这是伪代码。
open a transaction.
everything_is_okay=true;
try{
do a query
do another
if(everything_is_not_all_right_till_now())
throw a meaningfull exception;
do some more query;
savepoint A;
and again some other query;
if(some_partial_problem_happend())
rollback to savepoint A;
do hell lot of query;
}
catch(meaningfull exception){
rollback;
}
if(every_thing_is_still_okay())
commit;
顺便说一下,即使创建了一个交易,也只有这两个查询不应该采取> 30秒。您是否有可能在寻找被破坏的鸡蛋的错误桶? :p 请考虑这个事实。