【问题标题】:Why mysqli open transcation locks the database?为什么mysql open transaction 会锁定数据库?
【发布时间】:2013-10-29 09:21:34
【问题描述】:

我正在尝试使用 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秒)结果时,我无法执行以下查询(等待表金属锁)所以整个系统一直保持到操作完成,如何修理它?谢谢

$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); 

【问题讨论】:

  • 做你的操作,然后离开。无论如何,从dev.mysql.com/doc/refman/5.7/en/sql-syntax-transactions.htmldev.mysql.com/doc/refman/5.7/en/innodb-concepts.html 开始 - 请注意,需要锁定或其他形式的控制(例如 MVCC)来维持 ACID。锁定的具体内容(以及锁定方式)取决于多种因素。
  • 你想要无锁方式吗,比如添加一个status 列并在putlog 之后更新它
  • 请通过php.ini设置增加你的php执行时间
  • 显示您的另一个查询
  • 如果您需要一些位于函数范围之外的变量 (global $mysqli,$ip,$browser,$dateLog,$isQuerySuccess;),将其作为参数传递,不要 (ab-) 使用 global这么多

标签: php mysql transactions mysqli


【解决方案1】:

事情的正确顺序是:

$mysqli->autocommit(FALSE);
$mysqli->query("INSERT INTO ...");
$mysqli->commit();

但是对于一个小的插入,这样做很奇怪。在您的特定情况下,只需这样做就足够了:

$mysqli->query("INSERT INTO ...);

没有禁用自动提交,也没有提交。

【讨论】:

    【解决方案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';
    

    首先发出此(非阻塞)查询,然后仅在上述返回0 时发出后续CREATE TABLE IF EXISTS

    有可能,虽然不太可能,并发事务设法创建表并锁定它,就在上述两个语句的执行中间。这是因为information_schema 表是MEMORY 表,该引擎不支持事务(也不支持任何形式的锁定)。因此不要删除IF EXISTS 子句。

    您可以通过创建自己的 (InnoDB) 跟踪表来规避此限制。

    【讨论】:

      【解决方案3】:
        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
      

      【讨论】:

      • 这只会降低等待时间导致超时的可能性。它不会减少等待时间或查询被搁置的可能性。
      【解决方案4】:

      基本上 DDL 操作与事务不兼容。 DDL 是 Data Definition Language 的简写,指的是 CREATE、ALTER 和 DROP 语句。

      尽可能避免动态创建表。如果需要,可以手动或使用一次性脚本创建所有表。

      如果绝对必要,请尝试在打开事务之前创建表。

      【讨论】:

      【解决方案5】:

      一开始不应该只为一个查询打开事务。交易是有成本的。仅在真正需要时才支付费用。

      其次,动态创建表不是一个好主意。它显示了糟糕的架构设计的叹息。

      假设您共享的两个查询必须成对执行,最好的方法是打开一个事务。在这种情况下,在第一个查询之前打开事务,并在出现任何失败时立即关闭它,这使得整个操作变得不必要。 您还可以使用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;
      

      顺便说一句,即使在创建事务之后,只有这 2 个查询不应该超过 30 秒。是否有可能您正在为变质鸡蛋寻找错误的桶? :p 请考虑这个事实。

      【讨论】:

        猜你喜欢
        • 2017-07-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-06
        相关资源
        最近更新 更多