加速MySQL Query + PHP

时间:2016-01-08 16:48:06

标签: php mysql performance

我想加快这段代码。这是需要时间的查询。如果我将从100返回的行数更改为10,则需要几乎相同的时间(约2秒)。 GET基于用户排序/搜索输入。如何提高速度呢?这个项目表有大约2374744行,而bot表大约有20行。

$bot = " && user_items_new.bot_id != '0'";
if ($_GET['bot'] != 0) {
    $bot = " && user_items_new.bot_id='".$_GET['bot']."'";
}

$name = '';
if (strlen($_GET['name']) > 0) {
    $name = " && user_items_new.name LIKE '%".$_GET['name']."%'";
}

$min = '';
if (strlen($_GET['min']) > 0) {
    $min = " && steam_price >= '".$_GET['min']."'";
}

$max = '';
if (strlen($_GET['max']) > 0) {
    $max = " && steam_price <= '".$_GET['max']."'";
}

$order = '';
if ($_GET['order'] == 'price_desc') {
    $order = "ORDER BY steam_price DESC, user_items_new.name ASC";
} elseif ($_GET['order'] == 'price_asc') {
    $order = "ORDER BY steam_price ASC, user_items_new.name ASC";
} elseif ($_GET['order'] == 'name_desc') {
    $order = "ORDER BY user_items_new.name DESC";
} else {
    $order = "ORDER BY user_items_new.name ASC";
}

$limit = $_GET['start'];
$limit .= ', 100';

$i = 0;
$sql = mysql_query("SELECT user_item_id, user_items_new.bot_id AS item_bot_id, sticker, `key`, `case`, exterior, stattrak, image, user_items_new.name AS item_name, steam_price, color, bots_new.bot_id, bots_new.name AS bot_name, withdraw_enabled FROM user_items_new LEFT JOIN bots_new ON user_items_new.bot_id=bots_new.bot_id WHERE steam_price > '0.1' && deposit_start='0' && deposited='0' && user_id='0' && withdraw_enabled='1' ".$bot." ".$name." ".$min." ".$max." ".$order." LIMIT ".$limit)or die(mysql_error());
while ($item = mysql_fetch_assoc($sql)) {
    //...
}

项目表看起来像这样(从phpMyAdmin转储):

CREATE TABLE IF NOT EXISTS `user_items_new` (
`user_item_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `bot_id` int(11) NOT NULL,
  `item_original_id` varchar(22) NOT NULL,
  `item_real_id` varchar(22) NOT NULL,
  `class_id` varchar(22) NOT NULL,
  `weapon_id` int(11) NOT NULL,
  `name` text NOT NULL,
  `image` text NOT NULL,
  `case` int(11) NOT NULL,
  `key` int(11) NOT NULL,
  `sticker` int(11) NOT NULL,
  `capsule` int(11) NOT NULL,
  `holo` int(11) NOT NULL,
  `name_tag` int(11) NOT NULL,
  `access_pass` int(11) NOT NULL,
  `stattrak` int(11) NOT NULL,
  `color` varchar(32) NOT NULL,
  `exterior` text NOT NULL,
  `steam_price` double NOT NULL,
  `deposited` int(11) NOT NULL,
  `deposit_start` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=5219079 DEFAULT CHARSET=utf8;

ALTER TABLE `user_items_new`
 ADD PRIMARY KEY (`user_item_id`), ADD KEY `user_id` (`user_id`), ADD KEY `bot_id` (`bot_id`);

ALTER TABLE `user_items_new`
MODIFY `user_item_id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=5219079;

然后是机器人表:

CREATE TABLE IF NOT EXISTS `bots_new` (
`bot_id` int(11) NOT NULL,
  `name` varchar(64) NOT NULL,
  `username` varchar(64) NOT NULL,
  `password` varchar(64) NOT NULL,
  `deposit_enabled` int(11) NOT NULL,
  `withdraw_enabled` int(11) NOT NULL,
  `ident` varchar(32) NOT NULL
) ENGINE=MyISAM AUTO_INCREMENT=19 DEFAULT CHARSET=utf8;

ALTER TABLE `bots_new`
 ADD PRIMARY KEY (`bot_id`);

修改(添加精美印刷的SELECT)

SELECT  user_item_id, user_items_new.bot_id AS item_bot_id, sticker,
        key, case, exterior, stattrak, image, user_items_new.name AS item_name,
        steam_price, color, bots_new.bot_id, bots_new.name AS bot_name,
        withdraw_enabled
    FROM  user_items_new
    LEFT JOIN  bots_new ON user_items_new.bot_id=bots_new.bot_id
    WHERE  user_items_new.bot_id != '0' && deposit_start='0' && deposited='0' && user_id='0' && withdraw_enabled='1'
    ORDER BY  user_items_new.name ASC
    LIMIT  , 100

3 个答案:

答案 0 :(得分:1)

如何加快速度......

首先,在具有相等比较谓词的列上添加复合索引,例如

... ON user_items_new (user_id,deposited,deposit_start)

如果谓词过滤掉大量行,这将是有益的。例如,如果少于10%的行满足条件user_id = 0

顺便说一句,谓词withdraw_enabled='1'将否定&#34;外在性&#34; LEFT JOIN的。{如果省略关键字LEFT,则查询的结果将是等效的。

另一个问题是ORDER BY将导致&#34;使用filesort&#34;对行进行排序的操作。在应用LIMIT子句之前,需要对整个集进行排序。因此,我们不希望LIMIT 10LIMIT 1000更快,除了客户转移额外990行的额外时间。 (关于整个集合的排序并不完全正确;在某些情况下,MySQL可以在识别出第一个&#34;限制行数后中止排序操作。但是MySQL仍然需要通过整套以获得第一行。)

ORDER BY子句中的列添加到索引后,可能会在具有等式谓词的列之后添加。这些需要紧跟在等式谓词中引用的列之后出现。可能还需要在ORDER BY子句中指定相同的列。

假设当前查询包括:

... 
  WHERE ...
     && deposit_start='0' && u.deposited='0' && u.user_id='0' ...
...
  ORDER BY steam_price ASC, user_items_new.name ASC 

该指数可能合适:

... ON user_items_new (user_id,deposited,deposit_start,steam_price,name)

EXPLAIN的输出将显示该索引是否用于查询。除了前三列的相等比较之外,MySQL可以对索引使用范围扫描操作来满足steam_price >谓词。

还有InnoDB缓冲池的问题;分配多少内存来保存内存中的索引和数据页,以避免存储i / o。

要避免查找基础表中的数据页,可以考虑为查询创建覆盖索引。覆盖索引包括从表引用的列的所有,因此可以从索引完全满足查询。 EXPLAIN输出将显示&#34;使用索引&#34;如果查询使用覆盖索引,则在Extra列中。 (但是索引中的列数和总行大小是有限制的。当表行很大时,这将最有利于查询的性能,并且索引中列的大小是该列的一小部分。总表格行。

答案 1 :(得分:0)

使用该大小的表,可以用来优化查询的最简单的技巧之一是在where子句中使用的字段上添加索引。这允许解析器为您最常使用的查询预分类。

例如,你应该看到显着的收益:

ALTER TABLE user_items_new ADD INDEX (steam_price);

数据和数据类型在确定实际收益方面有很长的路要走。在所有字段上添加索引将导致查询效率倒退。所以更多不一定更好。

答案 2 :(得分:0)

您的查询速度很慢,因为您对user_items_new表的查询需要检查120万行。虽然您有user_item_iduser_idbot_id的索引,但这些索引到目前为止只能过滤您的搜索结果。

您需要在某些数据列上添加索引。您想要添加哪些索引(以及它们中的任何一个是否是复合索引)将取决于表的实际内容,并且在没有更多信息的情况下很难推荐。

您需要根据哪些列添加索引,其中不同的值会减少必须显着查看的数据;例如,withdraw_enabled上的索引不太可能获得太多,除非很少行具有withdraw_enabled == 1.如果您的行中只有很少的行具有steam_price&gt; = 0.1,则steam_price的索引将是有益的。 / p>