我们的一个查询是扫描全桌,它正在四处走动 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)
答案 0 :(得分:2)
select正在扫描所有行,因为条件" unm.newsletter_id = 1和unm.active = 1":这些字段未编入索引,因此引擎必须扫描所有行。
也包括索引中的那些字段' user_id'解决问题。
我建议改变:
KEY idx_747
(user_id
)
为了
KEY idx_747
(user_id
,newsletter_id
,active
)
答案 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
,所以我会先尝试第一组索引。