Windows 上的 PHP / MySQLi:插入 MySQL innoDB 表将 AUTO_INCREMENT 列增加 2

Posted

技术标签:

【中文标题】Windows 上的 PHP / MySQLi:插入 MySQL innoDB 表将 AUTO_INCREMENT 列增加 2【英文标题】:PHP / MySQLi on Windows: inserts into a MySQL innoDB Table increases the AUTO_INCREMENT column by 2 【发布时间】:2016-01-25 10:34:08 【问题描述】:

当我通过 php mysqli API 在 Windows 上的 PHP 进程中使用准备好的语句进行简单插入时,定义的 AUTO_INCREMENT 列增加了 2 而不是 1:

INSERT INTO `table` (`name`) VALUES (?)

在一个 PHP 进程中执行多次插入(在单独的事务中一个接一个)时,它会增加 1。

当我通过 phpmyadmin 使用相同的 SQL 查询时,它总是增加 1。

在提到的INSERT 之前或之后没有其他INSERTUPDATE 语句。之前只有一个SHOW 和一些SELECT 语句。

我找不到这个问题的原因。这种行为的原因可能是什么?

这里是主要代码部分:

<?php

class DB

    private function __construct($host, $username, $password, $schema, $port, $socket)
    
        if(is_null(self::$DB))
        
            self::$DB = new \MySQLi((string) $host, (string) $username, (string) $password, (string) $schema, (int) $port, (string) $socket);
            self::$DB->set_charset('utf8');

        
    
    // [...]
    public function __destruct()
    
        if(!is_null(self::$DB))
            self::$DB->close();
    
    // [...]
    public static function connect($host = '', $username = '', $password = '', $schema = '', $port = 0, $socket = '')
    
        if(is_null(self::$instance))
        
            $MD = new \MySQLi_driver();
            $MD->report_mode = MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT;
            // [...]
            self::$instance = new self($host, $username, $password, $schema, $port, $socket);
        

        return self::$instance;
    
    // [...]
    public static function __callStatic($name, $args)
    
        self::connect();

        switch(true)
        
            case in_array($name, array('insert', 'select', 'update', 'delete', 'show', 'describe', 'explain')):

            $query = isset($args[0]) ? (string) $args[0] : '';
            $vals = isset($args[1]) ? $args[1] : array();
            $select = isset($args[2]) ? trim((string) $args[2]) : 'array';
            $empty = isset($args[3]) ? $args[3] : array();
            $types = isset($args[4]) ? trim((string) $args[4]) : '';

            return self::dml(in_array($name, array('show', 'describe', 'explain')) ? 'select' : $name, $query, $vals, $select, $empty, $types);

            break;
        
    //[...]
    

    // [...]
    public static function dml($type, $query, $vals = array(), $select = 'array', $empty = array(), $types = '')
    
        // [...]
        if(!empty($vals) || mb_strpos($query,'?') !== false)
        
            if(!$stmt = self::$DB->prepare($query))
                throw new DBException('Failed to prepare statement '.htmlspecialchars($query).PHP_EOL.self::$DB->error.' ('.self::$DB->sqlstate.').');

            $args = array();

            if(empty($types))
            
                foreach($vals as &$val)
                
                    $t = gettype($val);

                    if($t == 'string' || $t == 'NULL')
                        $types.= 's';
                    elseif($t == 'integer' || $t == 'boolean')
                        $types.= 'i';
                    elseif($t == 'double' || $t == 'float')
                        $types.= 'd';
                    else
                        throw new DBException('Its not possible to automatically assign a value of type '.$t.'. Please specify the corresponding types manually.');
                    $args[] = $val;
                
            
            else
            
                foreach($vals as &$val)
                    $args[] = $val;
            

            array_unshift($args, $types);

            $RC = new \ReflectionClass($stmt);

            $RM = $RC->getMethod('bind_param');

            if(!$RM->invokeArgs($stmt, $args))
                throw new DBException('Failed to bind params.'.PHP_EOL.self::$DB->error.' ('.self::$DB->sqlstate.').');

            if(!$stmt->execute())
                throw new DBException('Failed to execute Statement.'.PHP_EOL.self::$DB->error.' ('.self::$DB->sqlstate.').');

            if($type == 'select')
            
                // [...]
            
            else
            
                $return = $type == 'insert' && self::$DB->insert_id > 0 ? self::$DB->insert_id : self::$DB->affected_rows;
                $stmt->close();
                return $return;
            
        
        else
        
            // [...]
        
    

?>

与:

echo DB::insert("INSERT INTO `table` (`name`) VALUES (?)", ["test"]);

查询日志显示 2 个插入。这会导致额外的增量。但是,当我将回显放入相应的 DB::dml() 方法时,它只输出一次,因此调用一次。 mysql-query-log:

151026 12:54:49     3 Connect   XXX@localhost on XXX
 3 Query    SET NAMES utf8
        3 Query SELECT DATABASE() AS `schema`
        3 Query SHOW GRANTS FOR CURRENT_USER
        3 Prepare   INSERT INTO `table` (`name`) VALUES (?)
        3 Execute   INSERT INTO `table` (`name`) VALUES ('testinsert22')
        3 Close stmt    
        3 Quit  
        4 Connect   XXX@localhost on XXX
        4 Query SET NAMES utf8
        4 Query SELECT DATABASE() AS `schema`
        4 Query SHOW GRANTS FOR CURRENT_USER
        4 Prepare   INSERT INTO `table` (`name`) VALUES (?)
        4 Execute   INSERT INTO `table` (`name`) VALUES ('testinsert22')
        4 Close stmt    
        4 Quit

【问题讨论】:

请向我们展示完整的 PHP 脚本。 【参考方案1】:

好的。我终于明白了。 问题是:我将所有请求重定向到我的 index.php 并使用 PHP 完成其余所有操作。我用简单的输出测试了一些类的行为。其中一项测试导致了上述问题。 我检查了 apache 访问日志并注意到重复的 favicon.ico 请求。这些请求——由主要浏览器自动产生(另见How to prevent favicon.ico requests?)——也到达了index.php——看起来像是重定向到浏览器? 通常这一切都在我的应用程序框架中处理,但我在测试时禁用了它。

【讨论】:

以上是关于Windows 上的 PHP / MySQLi:插入 MySQL innoDB 表将 AUTO_INCREMENT 列增加 2的主要内容,如果未能解决你的问题,请参考以下文章

缺少 mysqli 扩展。请检查您的 PHP 配置,Windows 7 [关闭]

Windows 10/Apache24/PHP 7.4.19 未加载 mysqli

ubuntu 16.04 上的 php 7.2 上缺少请求的 PHP 扩展 ext-mysqli *

ubuntu 16.04 上的 php 7.2 上缺少请求的 PHP 扩展 ext-mysqli *

致命错误:在 Windows 上找不到类“MySQLi”

Ubuntu 18.04上的PHP启动问题