ThinkPHP5查询当前表引擎,以及InnoDB表引擎下count(*)查询效率低的问题


今天新开发的功能上线之后出现了查询效率极其低下的问题,查询日志后发现问题出在代码内的大量的count()查询上,最严重时一条简单的count()查询执行时间长达120多秒!

针对这个问题请教前辈后被告知原因:InnoDB引擎下的count()语句会在实时查询表中的所有数据后返回总数所以效率较低,而MyISAM引擎则是直接返回表内存储的行记录信息所以效率较高。因为我本地的数据库引擎为MyISAM而线上的阿里云数据库服务器引擎为InnoDB所以出现了这种本地环境与线上环境查询效率差距极大的问题。

并且前辈也给出了指导意见:因为InnoDB引擎的主键索引通常为聚簇索引,为了保证效率可以新建一个辅助索引用于为count()查询指定字段。经尝试后使用新的辅助索引字段来进行count()查询效率确实大大提升。


这里附上查询当前表引擎并判断是使用主键字段还是使用辅助索引字段进行count()查询的相关代码。

框架使用的是ThinkPHP5,可以根据自己的状况自行调整代码。

function xmsb_getCountField($tableName)
{
    $dataBase = config(‘database.database‘);
    
    $tableDDL = Db::query("SHOW TABLE STATUS FROM `{$dataBase}` WHERE name = ‘{$tableName}‘"); // 获取表信息
    $engine = strtolower($tableDDL[0][‘Engine‘]); // 取得表引擎信息
    
    // 取得主键字段
    $pk = DB::getTableInfo($tableName, ‘pk‘);
    if(is_array($pk)) $pk = $pk[0];
    
    // 若表引擎为InnoDB则判断是否存在非主键索引
    if($engine == ‘innodb‘)
    {
        $indexs = Db::query("SHOW INDEX FROM {$tableName}");
        $key = $pk;
        foreach($indexs as $index)
        {
            // 若存在非主键索引,则返回该索引对应的字段
            if($index[‘Key_name‘] != ‘PRIMARY‘)
            {
                $key = $index[‘Column_name‘];
                break;
            }
        }
        
        return $key;
    }
    else
    {
        // 非InnoDB引擎则直接返回主键字段
        return $pk;
    }
}

$count = Db::name(‘数据表名‘) -> count(xmsb_getCountField(‘完整数据表名‘));