php+mysql在数据库里数据大的话查询很慢
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了php+mysql在数据库里数据大的话查询很慢相关的知识,希望对你有一定的参考价值。
参考技术A 1.建立索引,尽可能把索引建立到你你经常比较的字段上,如selecta,b,c,d
from
a
where
索引字段=值,这个索引字段最好是数值型数据
2.慢有更多情况,
情况1:远程查询,其实可能查询不慢,由于数据量大,传输过程慢
情况2:WHERE
后面的比较数据太多,比如
like
类的语句
情况3:需要哪个字段只取那个字段就行了,比如select
*
from
a与select
b,c,d
from
a速度是有差距的
3.数据库定期维护,压缩,把不常用的数据备份后放入备份库里,查询时查备份库等
问题补充:
.第一条:建立索引,怎么建立,我也听说过,但不知道怎么使用
答:每种数据建立索引的方法有差异,比如SQL
SERVER
2000中可对多个字段进行索引,比如SQL
SERVER2000中有命令
CREATE
INDEX
为给定表或视图创建索引。
只有表或视图的所有者才能为表创建索引。表或视图的所有者可以随时创建索引,无论表中是否有数据。可以通过指定限定的数据库名称,为另一个数据库中的表或视图创建索引。
语法
CREATE
[
UNIQUE
]
[
CLUSTERED
|
NONCLUSTERED
]
INDEX
index_name
ON
table
|
view
(
column
[
ASC
|
DESC
]
[
,...n
]
)
[
WITH
<
index_option
>
[
,...n]
]
[
ON
filegroup
]
<
index_option
>
::=
PAD_INDEX
|
FILLFACTOR
=
fillfactor
|
IGNORE_DUP_KEY
|
DROP_EXISTING
|
STATISTICS_NORECOMPUTE
|
SORT_IN_TEMPDB
第三条:数据库定期维护,压缩:怎么个压缩法?及时备份数据到备份库查询备份库,那查询时不是还慢吗?
答:这个有压缩工具,基本上每种数据库都有自己的压缩数据库的工具
为啥在 PHP 和 MySQL 中使用 PDO 的某些类型的准备查询很慢?
【中文标题】为啥在 PHP 和 MySQL 中使用 PDO 的某些类型的准备查询很慢?【英文标题】:Why are certain types of prepared queries using PDO in PHP with MySQL slow?为什么在 PHP 和 MySQL 中使用 PDO 的某些类型的准备查询很慢? 【发布时间】:2011-05-20 00:57:50 【问题描述】:当使用带有prepare()/execute() 的PDO 使用具有超过10000 个键的SELECT * FROM table WHERE Id IN ( .. )
查询时,性能比使用带有预准备语句的mysqli 或不使用预准备语句的PDO 执行相同查询要降低约10 倍。
更多奇怪的细节:
没有WHERE Id IN( ..)
子句的更典型的 SELECT 语句即使在 100K+ 行时也能正常执行。 SELECT * FROM table WHERE Id
例如很快。
prepare()/execute() 完成后性能下降 - 它完全在 PDOStatement::fetch()
或 PDOStatement::fetchAll()
中。在所有情况下,MySQL 查询执行时间都很短——这不是 MySQL 优化的情况。
将 10K 查询拆分为具有 1K 键的 10 个查询是高效的。
使用 mysql、带有预准备语句的 mysqli 或不带预准备语句的 PDO 是高效的。
在下面的示例中,带准备的 PDO 大约需要 6 秒,而其他的大约需要 0.5 秒。
你拥有的键越多,它就会以非线性方式变得更糟。尝试 10 万个密钥。
示例代码:
// $imageIds is an array with 10K keys
$keyCount = count($imageIds);
$keys = implode(', ', array_fill(0, $keyCount, '?'));
$query = "SELECT * FROM images WHERE ImageID IN ($keys)";
$stmt = $dbh->prepare($query);
$stmt->execute($imageIds);
// until now, it's been fast. fetch() is the slow part
while ($row = $stmt->fetch())
$rows[] = $row;
【问题讨论】:
如果这是可重现的,那么您可能需要分析 PHP 以了解出现减速的原因。 从您的描述来看,这听起来像是一个后处理错误。我怀疑减速是由于处理绑定参数。尝试->debugDumpParams()
并查找is_param=
值。如果是1
,那么 PDO 将遍历列表以查找要更新的绑定变量。也许使用 ->bindValue()
而不是 ->execute(ARRAY)
手动预置会有所帮助。但我怀疑 PDO 总是会遍历绑定的参数列表。不确定 is_param= 是否对此起决定性作用。 (而且懒得理解pdo_stmt.c)
请注意,在 mysqli 中使用$stmt->bind_param(str_repeat('s', count($imageIds)), ...$imageIds);
将参数绑定为字符串不比将它们绑定为整数慢。两种 mysqli 方法都需要比未准备好的语句多 50% 的时间。但是 PDO 准备好的语句就像 50 倍慢(具有 10K 参数)。所以不能只是 PDO 总是将参数绑定为字符串。即使$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
也不会改变任何东西。 PDO 发生了一些非常奇怪的事情。
你有没有弄清楚是什么原因造成的?我遇到了完全相同的问题。
相关错误报告:bugs.php.net/bug.php?id=53458 - @mario 很好地解决了这个问题。获取时间与以下两者成正比:返回的行数和绑定参数的数量。对于这种查询,应该是线性的问题变成 O(n²)。这意味着:参数增加 100 倍 => 慢 10000 倍。
【参考方案1】:
示例代码存在一些重大错误。所以更准确地说。
// $imageIds is an array with 10K keys
$keyCount = count($imageIds);
$keys = implode(', ', array_fill(0, $keyCount, '?'));
$query = "SELECT * FROM images WHERE ImageID IN ($keys)";
到目前为止,上面的代码将提供类似的东西......
SELECT * FROM images WHERE ImageID IN (?, ?, ?, ?, ?, ?,...?, ?, ?, ?)
没有用于绑定的循环...应该有一个小循环,您可以在其中绑定所有传递给 MySQL 的参数。您从 prepare
转到 execute
。 当正确时,binding 主要是您想要的。
$stmt = $dbh->prepare($query);
$stmt->execute($imageIds);
// until now, it's been fast. fetch() is the slow part
while ($row = $stmt->fetch())
$rows[] = $row;
现在我对这部分问题有一个简单的逻辑问题......
当使用
SELECT * FROM table WHERE Id IN ( .. )
查询时 超过 10000 个键 使用 PDO 和 prepare()/execute(),性能 与使用 mysqli 执行相同查询相比,降级约 10 倍 不使用预处理语句的预处理语句或 PDO。
如果重写相同的查询,这样您就不需要传递 10000 个键作为参数,不是更好吗?
PDO
和MySQLi
在时间上没有大的区别。糟糕的书面查询会。如果没有得到很好的优化,非常复杂的存储过程有时可能会变得很慢。
检查另一个查询是否可以获取所需的结果。例如
创建一个名为test
的小表
create table `test` (
`id` int(10) not null,
`desc` varchar(255)
);
insert into `test` (`id`,`desc`) values (1,'a'),(10,'a1'),(11,'a2'),(12,'a3'),(13,'a4'),(14,'a5'),(15,'a6'),(2,'ab'),(20,'ab1'),(21,'ab2'),(22,'ab3'),(23,'ab4'),(24,'ab5'),(25,'ab6');
运行那些简单的查询
select * from `test` where `id` rlike '^1$';
select * from `test` where `id` rlike '^1+';
select * from `test` where `id`=1;
select * from `test` where `id` rlike '^1.$';
select * from `test` where `id` rlike '.2$';
select * from `test` where `id` rlike '^2$';
select * from `test` where `id` rlike '.(2|3)'; // Slower
select * from `test` where `id` IN (12,13,22,23); // Faster
select * from `test` where `id` IN ('12,13,22,23'); // Wrong result
select * from `test` where `id` IN ('12','13','22','23'); // Slower
在此示例中,最后 4 个查询具有相同的结果。我认为大多数情况下,如果您在SQLFiddle 上查看它,您会得到与给定标签相对应的查询时间。
【讨论】:
我不明白你的回答。$keys = implode(', ', array_fill(0, $keyCount, '?'));
工作得很好here。 $stmt->execute($imageIds);
通常也可以正常工作。为什么要为可以通过单个命令完成的事情编写循环?查询SELECT * FROM table WHERE Id IN ( .. )
也很好,我在处理关系时经常使用它。为什么要重写一些非常简单的东西?
@PaulSpiegel 我已经编辑了我的答案,请同时检查 SQL Fiddle。你会看到某个查询得到了错误的结果,也就是说,当它在 MySQL 中声明为 int
时,你将参数作为字符串传递,你会得到一个结果,但大多数时候要慢得多。关键是如果查询很慢并不意味着驱动程序很慢。如果查询是错误的,那么肯定不是司机的错:)
@PaulSpiegel 我已经编辑了我的答案,但请回答我。您已经在变量中应用了括号
,如果将它们取出,它在rextester.com
中仍然得到相同的结果,这是否意味着不需要它们?关键是传递的是一系列字符串而不是整数,或者查询没有很好地编写或优化。
在字符串插值中是可选的。但有时你无法避免它。对我来说,始终使用它们是一个好习惯。
@PaulSpiegel 此外,这也是一个peculiar
问题,因为它只有一个小代码示例,其中缺少绑定参数循环的所有问题中最相关的部分。我的观点是,我真的认为 OP 应该完全改变查询,这就是为什么我做了一个小的 SQLFiddle
来支持我的观点。我没有处理过表有数百万行并且IN
子句内部有 10K 键的查询,但我真的会尝试重写我的查询以避免必须在 10k 键中搜索。也许正则表达式更合适。或许……【参考方案2】:
没有任何使用 PDO 的经验,所以对此无能为力,但这种方法的性能非常好,虽然在某些地方有点难看;)
PHP
<?php
$nums = array(); $max = 10000;
for($i=0;$i<$max*10;$i++) $nums[] = $i;
$conn = new mysqli("127.0.0.1", "vldb_dbo", "pass", "vldb_db", 3306);
$sql = sprintf("call list_products_by_id('%s',0)", implode(",",array_rand($nums, $max)));
$startTime = microtime(true);
$result = $conn->query($sql);
echo sprintf("Fetched %d rows in %s secs<br/>",
$conn->affected_rows, number_format(microtime(true) - $startTime, 6, ".", ""));
$result->close();
$conn->close();
?>
结果
select count(*) from product;
count(*)
========
1000000
Fetched 1000 rows in 0.014767 secs
Fetched 1000 rows in 0.014629 secs
Fetched 2000 rows in 0.027938 secs
Fetched 2000 rows in 0.027929 secs
Fetched 5000 rows in 0.068841 secs
Fetched 5000 rows in 0.067844 secs
Fetched 7000 rows in 0.095199 secs
Fetched 7000 rows in 0.095184 secs
Fetched 10000 rows in 0.138205 secs
Fetched 10000 rows in 0.134356 secs
MySQL
drop procedure if exists list_products_by_id;
delimiter #
create procedure list_products_by_id
(
in p_prod_id_csv text,
in p_show_explain tinyint unsigned
)
proc_main:begin
declare v_id varchar(10);
declare v_done tinyint unsigned default 0;
declare v_idx int unsigned default 1;
create temporary table tmp(prod_id int unsigned not null)engine=memory;
-- split the string into tokens and put into a temp table...
if p_prod_id_csv is not null then
while not v_done do
set v_id = trim(substring(p_prod_id_csv, v_idx,
if(locate(',', p_prod_id_csv, v_idx) > 0,
locate(',', p_prod_id_csv, v_idx) - v_idx, length(p_prod_id_csv))));
if length(v_id) > 0 then
set v_idx = v_idx + length(v_id) + 1;
insert ignore into tmp values(v_id);
else
set v_done = 1;
end if;
end while;
end if;
if p_show_explain then
select count(*) as count_of_tmp from tmp;
explain
select p.* from product p
inner join tmp on tmp.prod_id = p.prod_id order by p.prod_id;
end if;
select p.* from product p
inner join tmp on tmp.prod_id = p.prod_id order by p.prod_id;
drop temporary table if exists tmp;
end proc_main #
delimiter ;
【讨论】:
嘿 f00,谢谢!唉,我已经有了 MySQL、MySQLi、PDO 没有准备好的语句等的工作函数——我想知道为什么这个特殊的用例如此混乱。出于多种原因,我更喜欢 PDO 和准备好的语句。 不用担心 :) 只是出于兴趣你得到什么样的运行时?? @donmacaskill 您的来源并未表明在 PREPARE 和 EXECUTE 之后使用 CLOSE 来释放资源。请参阅 Jon Black 的源代码结尾。【参考方案3】:确保您告诉 PDO 该值是整数而不是字符串;如果 PDO 把它作为一个字符串,那么 MySQL 将不得不对值进行类型转换以进行比较。根据它的处理方式,它可能会导致 MySQL 避免使用索引,从而导致严重的减速。
我不完全确定这里的行为,但几年前我在 Postgres 上遇到过这个问题......
【讨论】:
+1 因为这可能是导致速度缓慢的原因。铸造将整齐地使用索引。之前在 mysql + PDO 准备语句中遇到过完全相同的问题。 上次看代码的时候,PDO MySQL驱动总是以字符串的形式传递参数。大多数数据类型参数都被忽略(PDO::PARAM_LOB
和 PDO::PARAM_NULL
除外)。其他品牌的 RDBMS 的其他一些 PDO 驱动程序也需要它们。
请注意,mysqli
可以很好地将参数绑定为字符串。而且带有字符串列表的未准备语句也很快。以上是关于php+mysql在数据库里数据大的话查询很慢的主要内容,如果未能解决你的问题,请参考以下文章