表索引很好,但查询需要很长时间才能执行?

时间:2014-06-12 10:31:47

标签: mysql sql database-performance

  

我们的一个查询是扫描全桌,它正在四处走动   15分钟,它对我们的应用程序性能有影响。桌子   有适当的索引,但它是扫描完整的表。如何   重写还是有最好的方法索引?

查询:

 select count(u.user_id) 
   from iflora_user_newsletter_map unm, 
        users u , 
        addresses a 
  where unm.user_id=u.user_id 
    and unm.newsletter_id=1 
    and unm.active=1  
    and u.user_id=a.user_id 
    and a.type='billing';\

解释计划:

+----+-------------+-------+------+------------------+----------+---------+----------------------+---------+--------------------------+
| id | select_type | table | type | possible_keys    | key      | key_len | ref                  | rows    | Extra                    |
+----+-------------+-------+------+------------------+----------+---------+----------------------+---------+--------------------------+
|  1 | SIMPLE      | unm   | ref  | idx_2243,idx_747 | idx_2243 | 4       | const                | 2960628 | Using where              |
|  1 | SIMPLE      | u     | ref  | idx_747          | idx_747  | 5       | shopcart.unm.user_id |       1 | Using where; Using index |
|  1 | SIMPLE      | a     | ref  | idx_747          | idx_747  | 4       | shopcart.unm.user_id |       1 | Using where              |
+----+-------------+-------+------+------------------+----------+---------+----------------------+---------+--------------------------+
3 rows in set (0.05 sec)

表格结构:

mysql> show create table iflora_user_newsletter_map\G
*************************** 1. row ***************************
       Table: iflora_user_newsletter_map
Create Table: CREATE TABLE `iflora_user_newsletter_map` (
  `row_mod` datetime DEFAULT NULL,
  `row_create` datetime DEFAULT NULL,
  `user_id` int(11) NOT NULL,
  `newsletter_id` int(11) NOT NULL,
  `active` int(11) DEFAULT NULL,
  `date_subscribed` datetime DEFAULT NULL,
  `date_unsubscribed` datetime DEFAULT NULL,
  `email_preference` int(11) DEFAULT NULL,
  `mail_preference` int(11) DEFAULT NULL,
  `sms_preference` int(11) DEFAULT NULL,
  `phone_preference` int(11) DEFAULT NULL,
  UNIQUE KEY `idx_2243` (`newsletter_id`,`user_id`),
  KEY `idx_1571` (`date_subscribed`),
  KEY `idx_1798` (`date_unsubscribed`),
  KEY `idx_747` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin
1 row in set (0.00 sec)

mysql> show create table users\G
*************************** 1. row ***************************
       Table: users
Create Table: CREATE TABLE `users` (
  `row_mod` datetime DEFAULT NULL,
  `row_create` datetime DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  `name` varchar(50) COLLATE latin1_bin DEFAULT NULL,
  `password` varchar(255) COLLATE latin1_bin DEFAULT NULL,
  `salt` varchar(12) COLLATE latin1_bin DEFAULT NULL,
  `last_visit` int(11) DEFAULT NULL,
  `member_since` int(11) DEFAULT NULL,
  `email` varchar(100) COLLATE latin1_bin DEFAULT NULL,
  `active` int(11) DEFAULT NULL,
  `transient` int(11) DEFAULT NULL,
  `fname` varchar(50) COLLATE latin1_bin DEFAULT NULL,
  `lname` varchar(50) COLLATE latin1_bin DEFAULT NULL,
  `default_address_id` decimal(12,0) DEFAULT NULL,
  `opt_in_email` char(1) COLLATE latin1_bin DEFAULT NULL,
  `opt_in_email2` char(1) COLLATE latin1_bin DEFAULT NULL,
  `opt_in_email3` char(1) COLLATE latin1_bin DEFAULT NULL,
  `email_promotions` char(1) COLLATE latin1_bin DEFAULT NULL,
  `textonly_email` char(1) COLLATE latin1_bin DEFAULT NULL,
  `website_id` int(11) DEFAULT NULL,
  `express_checkout` int(11) DEFAULT NULL,
  `webstore_id` int(11) DEFAULT NULL,
  UNIQUE KEY `idx_747` (`user_id`),
  KEY `idx_417` (`name`),
  KEY `idx_delete_transient` (`transient`,`last_visit`),
  KEY `users_n1` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin
1 row in set (0.00 sec)

mysql> show create table addresses\G
*************************** 1. row ***************************
       Table: addresses
Create Table: CREATE TABLE `addresses` (
  `row_mod` datetime DEFAULT NULL,
  `row_create` datetime DEFAULT NULL,
  `address_id` decimal(12,0) NOT NULL,
  `user_id` int(11) NOT NULL,
  `name` varchar(30) COLLATE latin1_bin DEFAULT NULL,
  `type` varchar(20) COLLATE latin1_bin DEFAULT NULL,
  `fname` varchar(50) COLLATE latin1_bin DEFAULT NULL,
  `lname` varchar(50) COLLATE latin1_bin DEFAULT NULL,
  `company` varchar(100) COLLATE latin1_bin DEFAULT NULL,
  `email` varchar(100) COLLATE latin1_bin DEFAULT NULL,
  `street_address` varchar(100) COLLATE latin1_bin DEFAULT NULL,
  `address2` varchar(100) COLLATE latin1_bin DEFAULT NULL,
  `city` varchar(30) COLLATE latin1_bin DEFAULT NULL,
  `state` varchar(40) COLLATE latin1_bin DEFAULT NULL,
  `zip` varchar(10) COLLATE latin1_bin DEFAULT NULL,
  `country` varchar(20) COLLATE latin1_bin DEFAULT NULL,
  `day_phone` varchar(20) COLLATE latin1_bin DEFAULT NULL,
  `day_phone_ext` varchar(10) COLLATE latin1_bin DEFAULT NULL,
  `evening_phone` varchar(20) COLLATE latin1_bin DEFAULT NULL,
  `mobile_phone` varchar(20) COLLATE latin1_bin DEFAULT NULL,
  `fax` varchar(15) COLLATE latin1_bin DEFAULT NULL,
  `location_name` varchar(40) COLLATE latin1_bin DEFAULT NULL,
  `personal_notes` varchar(100) COLLATE latin1_bin DEFAULT NULL,
  `complete_flag` varchar(1) COLLATE latin1_bin DEFAULT 'N',
  `credit_card_number` varchar(20) COLLATE latin1_bin DEFAULT NULL,
  `credit_card_type` varchar(10) COLLATE latin1_bin DEFAULT NULL,
  `credit_card_exp_month` int(11) DEFAULT NULL,
  `credit_card_exp_year` int(11) DEFAULT NULL,
  `birthday` datetime DEFAULT NULL,
  `title` varchar(10) COLLATE latin1_bin DEFAULT NULL,
  `street_address2` varchar(100) COLLATE latin1_bin DEFAULT NULL,
  `district` varchar(30) COLLATE latin1_bin DEFAULT NULL,
  `verified` int(11) DEFAULT NULL,
  `email_type` varchar(1) COLLATE latin1_bin DEFAULT NULL,
  `location_type` varchar(20) COLLATE latin1_bin DEFAULT NULL,
  `birth_day` varchar(4) COLLATE latin1_bin DEFAULT NULL,
  `birth_month` varchar(4) COLLATE latin1_bin DEFAULT NULL,
  `birth_year` varchar(4) COLLATE latin1_bin DEFAULT NULL,
  `skip_queries` int(11) DEFAULT NULL,
  `has_qas_results` int(11) DEFAULT NULL,
  `qas_queried` int(11) DEFAULT NULL,
  `debit_card_issue_number` varchar(6) COLLATE latin1_bin DEFAULT NULL,
  `credit_card_start_year` varchar(4) COLLATE latin1_bin DEFAULT NULL,
  `credit_card_start_month` varchar(4) COLLATE latin1_bin DEFAULT NULL,
  `in_the_name_of` varchar(30) COLLATE latin1_bin DEFAULT NULL,
  `parameters` varchar(1024) COLLATE latin1_bin DEFAULT NULL,
  UNIQUE KEY `idx_1042` (`address_id`),
  KEY `idx_747` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin
1 row in set (0.00 sec)

@morilo,

这里是添加索引后的解释 在iflora_user_newsletter_map上创建索引idx_748(user_id,newsletter_id,active);

解释计划

mysql> explain select count(u.user_id) from iflora_user_newsletter_map unm, users u , addresses a where unm.user_id=u.user_id and unm.newsletter_id=1  and unm.active=1  and u.user_id=a.user_id and a.type='billing';
+----+-------------+-------+--------+-----------------------------------+----------+---------+--------------------------+------+-------------+
| id | select_type | table | type   | possible_keys                     | key      | key_len | ref                      | rows | Extra       |
+----+-------------+-------+--------+-----------------------------------+----------+---------+--------------------------+------+-------------+
|  1 | SIMPLE      | u     | index  | idx_747                           | idx_747  | 5       | NULL                     | 2575 | Using index |
|  1 | SIMPLE      | a     | ref    | idx_747,idx_ads                   | idx_747  | 4       | shopcart.u.user_id       |    1 | Using where |
|  1 | SIMPLE      | unm   | eq_ref | idx_2243,idx_news,idx_747,idx_748 | idx_2243 | 8       | const,shopcart.u.user_id |    1 | Using where |
+----+-------------+-------+--------+-----------------------------------+----------+---------+--------------------------+------+-------------+
3 rows in set (0.00 sec)

Explain Plan use force index

mysql> explain select count(u.user_id) from iflora_user_newsletter_map unm, users u , addresses a where unm.user_id=u.user_id and unm.newsletter_id=1  and unm.active=1  and u.user_id=a.user_id and a.type='billing';
+----+-------------+-------+--------+-----------------------------------+----------+---------+--------------------------+------+-------------+
| id | select_type | table | type   | possible_keys                     | key      | key_len | ref                      | rows | Extra       |
+----+-------------+-------+--------+-----------------------------------+----------+---------+--------------------------+------+-------------+
|  1 | SIMPLE      | u     | index  | idx_747                           | idx_747  | 5       | NULL                     | 2575 | Using index |
|  1 | SIMPLE      | a     | ref    | idx_747,idx_ads                   | idx_747  | 4       | shopcart.u.user_id       |    1 | Using where |
|  1 | SIMPLE      | unm   | eq_ref | idx_2243,idx_news,idx_747,idx_748 | idx_2243 | 8       | const,shopcart.u.user_id |    1 | Using where |
+----+-------------+-------+--------+-----------------------------------+----------+---------+--------------------------+------+-------------+
3 rows in set (0.00 sec)

2 个答案:

答案 0 :(得分:2)

select正在扫描所有行,因为条件" unm.newsletter_id = 1和unm.active = 1":这些字段未编入索引,因此引擎必须扫描所有行。 也包括索引中的那些字段' user_id'解决问题。 我建议改变:
    KEY idx_747user_id
为了
    KEY idx_747user_idnewsletter_idactive

答案 1 :(得分:2)

这是使用正确的连接语法编写的查询:

select count(u.user_id)
from iflora_user_newsletter_map unm join
     users u 
     on unm.user_id = u.user_id join
     addresses a
     on u.user_id = a.user_id 
where unm.newsletter_id = 1  and unm.active = 1 and a.type = 'billing';

此查询可能会受益于以下索引:

addresses(user_id, type)
iflora_user_newsletter_map(newsletter_id, active, user_id)

或:

addresses(type, user_id)
iflora_user_newsletter_map(active, user_id, newsletter_id)

我没有在表中的索引中看到where子句中的列。其中哪一个更好取决于每个表中有多少数据用于索引。较大的表似乎是unm,所以我会先尝试第一组索引。