2

表定义和查询说明:

项目 |

CREATE TABLE `item` ( 
`item_id` int(11) NOT NULL AUTO_INCREMENT, 
`item_type_id` int(11) NOT NULL, 
`brand_id` int(11) NOT NULL, 
`site_id` int(11) NOT NULL, 
`seller_id` int(11) NOT NULL, 
`title` varchar(175) NOT NULL, 
`desc` text NOT NULL, 
`url` varchar(767) NOT NULL, 
`price` int(11) NOT NULL, 
`photo` varchar(255) NOT NULL, 
`photo_file` varchar(255) NOT NULL, 
`photo_type` varchar(32) NOT NULL, 
`has_photo` enum('yes','no','pending') NOT NULL DEFAULT 'pending', 
`added_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 
`updated_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 
`created_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 
`normalized_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 
`location` varchar(128) NOT NULL, 
PRIMARY KEY (`item_id`), 
KEY `item_type_id` (`item_type_id`), 
KEY `brand_id` (`brand_id`), 
KEY `site_id` (`site_id`), 
KEY `seller_id` (`seller_id`), 
KEY `created_at` (`created_at`), 
KEY `added_at` (`added_at`), 
KEY `normalized_time` (`normalized_time`), 
KEY `typephototime` (`item_type_id`,`has_photo`,`normalized_time`), 
KEY `brandidphoto` (`brand_id`,`item_type_id`,`has_photo`), 
KEY `brandidphoto2` (`brand_id`,`item_type_id`,`has_photo`), 
KEY `idphoto` (`item_type_id`,`has_photo`), 
KEY `idphototime` (`item_type_id`,`has_photo`,`normalized_time`), 
KEY `idphoto2` (`item_type_id`,`has_photo`), 
KEY `typepricebrandid` (`item_type_id`,`price`,`brand_id`,`item_id`), 
KEY `sellertypephototime` (`seller_id`,`item_type_id`,`has_photo`,`normalized_time`), 
KEY `typephoto` (`item_type_id`,`has_photo`) 
) ENGINE=MyISAM AUTO_INCREMENT=508885 DEFAULT CHARSET=latin1 | 

mysql> 解释 SELECT item.* FROM itemWHERE item.item_type_id = "1" AND item.has_photo = "yes" ORDER BY normalized_time DESC LIMIT 1;

+----+-------------+-------+------+------------------------------------------------------------------------------------+---------------+---------+-------------+-------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
+----+-------------+-------+------+------------------------------------------------------------------------------------+---------------+---------+-------------+-------+-------------+ 
| 1 | SIMPLE | item | ref | item_type_id,typephototime,idphoto,idphototime,idphoto2,typepricebrandid,typephoto | typephototime | 5 | const,const | 69528 | Using where | 
+----+-------------+-------+------+------------------------------------------------------------------------------------+---------------+---------+-------------+-------+-------------+ 
1 row in set (0.02 sec) 

mysql> explain SELECT * FROM itemWHERE item_type_id = "1" AND (price BETWEEN "25" AND "275") AND brand_id = "10" ORDER BY item_id DESC LIMIT 1;

+----+-------------+-------+-------+------------------------------------------------------------------------------------------------------------------------+---------+---------+------+------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
+----+-------------+-------+-------+------------------------------------------------------------------------------------------------------------------------+---------+---------+------+------+-------------+ 
| 1 | SIMPLE | item | index | item_type_id,brand_id,typephototime,brandidphoto,brandidphoto2,idphoto,idphototime,idphoto2,typepricebrandid,typephoto | PRIMARY | 4 | NULL | 203 | Using where | 
+----+-------------+-------+-------+------------------------------------------------------------------------------------------------------------------------+---------+---------+------+------+-------------+ 
1 row in set (0.01 sec) 

mysql> 解释 SELECT item.* FROM itemWHERE item.brand_id = "10" AND item.item_type_id = "1" AND item.has_photo = "yes" ORDER BY normalized_time DESC LIMIT 1;

+----+-------------+-------+-------+------------------------------------------------------------------------------------------------------------------------+-----------------+---------+------+--------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
+----+-------------+-------+-------+------------------------------------------------------------------------------------------------------------------------+-----------------+---------+------+--------+-------------+ 
| 1 | SIMPLE | item | index | item_type_id,brand_id,typephototime,brandidphoto,brandidphoto2,idphoto,idphototime,idphoto2,typepricebrandid,typephoto | normalized_time | 8 | NULL | 502397 | Using where | 
+----+-------------+-------+-------+------------------------------------------------------------------------------------------------------------------------+-----------------+---------+------+--------+-------------+ 
1 row in set (2.15 sec) 

mysql> 解释 SELECT COUNT(*) FROM itemWHERE item.item_type_id = "1" AND item.has_photo = "yes" ;

+----+-------------+-------+------+------------------------------------------------------------------------------------+-----------+---------+-------------+-------+--------------------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
+----+-------------+-------+------+------------------------------------------------------------------------------------+-----------+---------+-------------+-------+--------------------------+ 
| 1 | SIMPLE | item | ref | item_type_id,typephototime,idphoto,idphototime,idphoto2,typepricebrandid,typephoto | typephoto | 5 | const,const | 71135 | Using where; Using index | 
+----+-------------+-------+------+------------------------------------------------------------------------------------+-----------+---------+-------------+-------+--------------------------+ 
1 row in set (0.01 sec)
4

2 回答 2

1

以下索引是多余的,因为它们匹配另一个索引的左列。您几乎可以肯定删除这些索引并节省一些空间和开销。

KEY `item_type_id` (`item_type_id`), /* redundant */
KEY `brand_id` (`brand_id`), /* redundant */
KEY `seller_id` (`seller_id`), /* redundant */
KEY `idphototime` (`item_type_id`,`has_photo`,`normalized_time`),  /* redundant */
KEY `brandidphoto2` (`brand_id`,`item_type_id`,`has_photo`), /* redundant */
KEY `idphoto` (`item_type_id`,`has_photo`), /* redundant  */
KEY `idphoto2` (`item_type_id`,`has_photo`), /* redundant */
KEY `typephoto` (`item_type_id`,`has_photo`) /* redundant */

这留下了以下索引:

KEY `site_id` (`site_id`), 
KEY `created_at` (`created_at`), 
KEY `added_at` (`added_at`), 
KEY `normalized_time` (`normalized_time`), 
KEY `brandidphoto` (`brand_id`,`item_type_id`,`has_photo`), 
KEY `typephototime` (`item_type_id`,`has_photo`,`normalized_time`),
KEY `typepricebrandid` (`item_type_id`,`price`,`brand_id`,`item_id`), 
KEY `sellertypephototime` (`seller_id`,`item_type_id`,`has_photo`,`normalized_time`), 

您还可以使用pt-duplicate-key-checker 之类的工具来查找冗余索引。

接下来考虑存储引擎:

) ENGINE=MyISAM AUTO_INCREMENT=508885 DEFAULT CHARSET=latin1;

几乎总是,InnoDB 是比 MyISAM 更好的选择。不仅是为了性能,而且是为了数据完整性和碰撞安全。自 2010 年以来,InnoDB 一直是默认存储引擎,并且它是唯一正在积极改进的存储引擎。我建议制作此表的副本,将存储引擎更改为 InnoDB,并根据您的查询比较它的性能。

接下来让我们考虑查询的索引:

SELECT item.* FROM `item` WHERE item.item_type_id = "1" AND item.has_photo = "yes" 
ORDER BY normalized_time DESC LIMIT 1; 

我会选择一个索引(item_type_id, has_photo, normalized_time),这就是它当前使用的索引,即typephototime.

进一步优化这一点的一种方法是仅获取索引中的列。当您在 EXPLAIN 计划中看到“使用索引”时,它可能会极大地提高性能。

另一个重要的因素是确保您的索引缓存在内存中:key_buffer_size如果您使用 MyISAM 或者innodb_buffer_pool_size如果您使用 InnoDB 与您希望保留在内存中的所有索引一样大,则增加。因为您不想运行需要扫描大于缓冲区的索引的查询;它会导致大量交换。

SELECT * FROM `item` WHERE item_type_id = "1" AND (price BETWEEN "25" AND "275") AND brand_id = "10" 
ORDER BY item_id DESC LIMIT 1; 

我会在 上选择一个索引(item_type_id, brand_id, price),但这个查询当前使用的是 PRIMARY 索引。您应该创建一个新索引。

SELECT item.* FROM `item` WHERE item.brand_id = "10" AND item.item_type_id = "1" AND item.has_photo = "yes" 
ORDER BY normalized_time DESC LIMIT 1; 

我会在(item_type_id, brand_id, has_photo, normalized_time). 您应该创建一个新索引。

SELECT COUNT(*) FROM `item` WHERE item.item_type_id = "1" AND item.has_photo = "yes" ; 

我会选择一个索引(item_type_id, has_photo),这就是它当前使用的索引,即typephoto. 它还获得了“使用索引”优化,因此唯一的其他改进可能是确保有足够的缓冲区来保存内存中的索引。

很难优化SELECT COUNT(*)查询,因为它们自然要扫描很多行。

优化 COUNT(*) 的另一种策略是离线计算计数,并将它们存储在汇总表或内存缓存(如 memcached)中,这样您就不必在每次有人加载页面时重新计算它们。但这意味着每次有人添加或删除item表中的一行时,您都必须更新计数,这可能会昂贵,具体取决于发生的频率。

于 2013-07-20T18:59:20.373 回答
0

我建议改变一些事情:

  • 您不需要所有这些索引。您实际上只需要经常访问的字段上的索引,例如外键字段。删除除 ID 字段之外的所有索引。
  • 除非有实际数据,否则您应该将日期存储为空值。
  • 远离枚举数据类型,使用 smallint 和代表每个值的标志。例如,0 待定,1 是,2 否。

除了减小数据库的大小外,它还使事情变得更加整洁。您的新表结构如下所示:

CREATE TABLE `item` ( 
    `item_id` int(11) NOT NULL AUTO_INCREMENT, 
    `item_type_id` int(11) NOT NULL, 
    `brand_id` int(11) NOT NULL, 
    `site_id` int(11) NOT NULL, 
    `seller_id` int(11) NOT NULL, 
    `title` varchar(175) NOT NULL, 
    `desc` text NOT NULL, 
    `url` varchar(767) NOT NULL, 
    `price` int(11) NOT NULL, 
    `photo` varchar(255) NOT NULL, 
    `photo_file` varchar(255) NOT NULL, 
    `photo_type` varchar(32) NOT NULL, 
    `has_photo` smallint NOT NULL DEFAULT 0, 
    `added_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 
    `updated_at` datetime NULL DEFAULT NULL, 
    `created_at` datetime NULL DEFAULT NULL, 
    `normalized_time` datetime NULL DEFAULT NULL, 
    `location` varchar(128) NULL, 
    PRIMARY KEY (`item_id`), 
    KEY `item_type_id` (`item_type_id`), 
    KEY `brand_id` (`brand_id`), 
    KEY `site_id` (`site_id`), 
    KEY `seller_id` (`seller_id`)
) ENGINE=MyISAM AUTO_INCREMENT=508885 DEFAULT CHARSET=latin1;

我还建议utf8_unicode_ci用作整理、utf8字符集和InnoDB引擎。

但首先,删除所有这些键,然后重试。还要删除第三个查询的别名。

SELECT * FROM item WHERE brand_id = "10" AND item_type_id = "1" AND has_photo = "yes" ORDER BY normalized_time DESC LIMIT 1;

于 2013-07-20T03:57:43.887 回答