如何加快 PDO 查询

Posted

技术标签:

【中文标题】如何加快 PDO 查询【英文标题】:How to speed up PDO query 【发布时间】:2017-12-12 18:38:47 【问题描述】:

我有超过 5500 万 行(产品)的产品数据库表(tabl_products),并且我使用 PDO 查询 来显示来自 tabl_products 表的每个产品,所以在这里是我的查询表结构和选择查询..

id | pr_name | pr_description | pr_qntty | pr_price

$pr_id = (int)$_GET['id'];
$select_pr = $con->prepare("SELECT pr_name, pr_description, pr_price FROM tabl_products WHERE id=:pr_id");
$select_pr->bindParam(":pr_id", $pr_id);
$select_pr->execute();
if(!($select_pr->rowCount()))

   include '404.php'; exit();

$fetch_pr = $select_pr->fetch(PDO::FETCH_ASSOC);

echo $pr_name = $fetch_pr['pr_name'].'<br/>';
echo $pr_description = $pr_description['pr_description'].'<br/>';
echo $pr_price = $fetch_all['pr_price'];

以上查询需要2到3分钟才能显示单个产品信息。

那么如何在 5 到 10 秒内显示单个产品信息?

(来自评论)

CREATE TABLE IF NOT EXISTS tabl_products (
    id int(11) NOT NULL AUTO_INCREMENT, 
    pr_name varchar(255) NOT NULL, 
    pr_description varchar(255) NOT NULL, 
    pr_qntty varchar(255) NOT NULL, 
    pr_price varchar(255) NOT NULL, 
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

【问题讨论】:

id 列是否已编入索引? id 列是 AUTO_INCREMENT 并且是 PRIMARY KEY 表格有多少行? tabl_products 表中正好有 55090297 行 一个完整的猜测,但是如果您正在运行 CPanel,我想知道您是否只是在共享服务器上,它要么被超额订阅,要么严重缺乏规范? 【参考方案1】:

我使用 mysql 5.7.20 重新创建了类似的数据库,用虚拟数据填充了大约 55000000 行,查询如下:

SELECT * FROM ***_test.tabl_products WHERE id=12345678;

在我的(体面的)PC 上很快返回结果(在 MYSQL Workbench 中显示 0.000 秒)。

我可以为您提供一些可以帮助您识别问题的想法。首先,您的代码中有一些错误。

echo $pr_name = $fetch_pr['pr_name'].'<br/>';
echo $pr_description = $pr_description['pr_description'].'<br/>';
echo $pr_price = $fetch_all['pr_price'];

$pr_description 应替换为$fetch_pr

$fetch_all 应替换为$fetch_pr

也许您的 php 文件中还有其他内容导致执行速度如此缓慢。尝试执行最小文件,或使用 MySQL Workbench(或类似工具)确保它不是 http 服务器或 php 的问题(可能查询运行快,但获取速度慢,或有其他问题?)。

特别是对于这么大的数据库,您需要确保使用索引。在 MySQL 中,您可以通过在查询前添加 EXPLAIN(https://dev.mysql.com/doc/refman/5.7/en/explain-output.html) 来做到这一点,即:

EXPLAIN SELECT * FROM ***_test.tabl_products WHERE id=12345678;

然后分析字段 possible_keyskey,其中应包括您的索引名称(PRIMARY)。如果 MySQL 没有使用索引,那么在查询中添加 USE INDEX (`PRIMARY`) (https://dev.mysql.com/doc/refman/5.7/en/index-hints.html) 语句应该会有所帮助,但您应该调查为什么默认情况下不使用它。

仅供参考,当我添加 IGNORE INDEX (`PRIMARY`) 只是为了检查没有索引的查询执行时间时,查询运行大约 30 秒而不是几分之一秒。

这也可能是服务器过载或功能不够强大,但使用索引您的查询无论如何应该非常快。

用户Your Common Sense 建议即使使用索引也可能太大而无法放入内存,因此从磁盘读取速度很慢。您可能需要检查索引的大小并将其与可用内存进行比较,并(如果需要)相应地调整 MySQL 内存设置。

【讨论】:

我认为你找错树了。 EXPLAIN 不会给你任何线索。应该是数据库服务器上的innodb stats和内存状态 EXPLAIN 将判断索引是否用于此特定查询。如果是,您可以进行进一步调查,但如果不是,您可以“鼓励”MySQL 将索引与 USE INDEX 一起使用(并调查为什么默认情况下不使用索引)。我知道我的回答不是完整的解决方案问题,但它可以消除一些可能的原因 是的,与另一个不同,它本身就是一个很好的答案,并且可能对其他人有所帮助。但是在这种特殊情况下,很难相信没有使用主键。很可能没有足够的 RAM 用于索引。 我同意这样的查询看起来不太可能。如果您不介意,我会将您的建议包含在我的回答中。【参考方案2】:

由于您的id 是唯一的(主键),您可以添加LIMIT 1。因为,否则,即使您的产品被找到一次,PDO 也会继续查找具有该特定 ID 的其他产品。

所以你的查询变成了:

SELECT pr_name, pr_description, pr_price FROM tabl_products WHERE id=:pr_id LIMIT 1

试着告诉我们需要多长时间:)

PS:Doctrine ORM 使用的例子:

第 195 行,参数 $load 在位置 6,http://www.doctrine-project.org/api/orm/2.5/source-class-Doctrine.ORM.EntityRepository.html#183-196 使用BasicEntityPersister http://www.doctrine-project.org/api/orm/2.5/source-class-Doctrine.ORM.Persisters.Entity.BasicEntityPersister.html#705-725的方法->load()

【讨论】:

您好,为什么您认为这没有意义?你能发展吗?您是否尝试过在 5500 万个对象表上使用和不使用 LIMIT 1?谢谢 这是没有意义的,因为 MySQL 的查询计划器已经在内部执行此操作。知道UNIQUEPRIMARY 键的含义是足够聪明的。见***.com/questions/3848390/…

以上是关于如何加快 PDO 查询的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 PDO 跨两个数据库运行连接查询

如何避免此 PDO 异常:在其他无缓冲查询处于活动状态时无法执行查询

如何在 PHP PDO 中使用异步 Mysql 查询

PDO 变更查询

如何正确使用 PDO 对象进行参数化 SELECT 查询

如何在 PHP 中为动态查询构建参数化 PDO 语句?