2011-05-26 127 views
1

我们有一个中心登录,我们用来支持多个网站。为了存储我们用户的数据,我们有一个accounts表,该表存储每个用户帐户,然后存储每个站点的users表以获取特定于站点的信息。我们还有一个简单的connections表,用于存储用户之间的连接。更奇怪的MySQL行为 - 查询优化帮助

我们注意到一个连接主键user_id上的表的查询正在执行缓慢。我希望那里的一些SQL专家能够解释为什么它使用WHERE来搜索users_site1表,并建议我们如何优化它。这里是慢速查询&的解释结果:

mysql> explain select a.username,a.first_name,a.last_name,a.organization_name,a.organization,a.city,a.state,a.zip,a.country,a.profile_photo,a.facebook_id,a.twitter_id,u.reviews from accounts a join users_site1 u ON a.user_id=u.user_id where a.user_id IN (select cid2 from connections where cid1=10001006 AND type="MM" AND status="A") OR a.user_id IN (select cid1 from connections where cid2=10001006 AND type="MM" AND status="A") order by RAND() LIMIT 4; 
+----+--------------------+-------------+--------+-------------------+---------+---------+-----------------------+-------+----------------------------------------------+ 
| id | select_type  | table  | type | possible_keys  | key  | key_len | ref     | rows | Extra          | 
+----+--------------------+-------------+--------+-------------------+---------+---------+-----------------------+-------+----------------------------------------------+ 
| 1 | PRIMARY   | u   | ALL | PRIMARY   | NULL | NULL | NULL     | 79783 | Using where; Using temporary; Using filesort | 
| 1 | PRIMARY   | a   | eq_ref | PRIMARY   | PRIMARY | 4  | exampledb.u.user_id |  1 |            | 
| 3 | DEPENDENT SUBQUERY | connections | ref | PRIMARY,cid1,cid2 | cid2 | 6  | const,const   |  2 | Using where         | 
| 2 | DEPENDENT SUBQUERY | connections | ref | PRIMARY,cid1,cid2 | cid1 | 6  | const,const   |  1 | Using where         | 
+----+--------------------+-------------+--------+-------------------+---------+---------+-----------------------+-------+----------------------------------------------+ 
4 rows in set (0.00 sec) 

下面是每个表的定义:

CREATE TABLE `accounts` (
    `user_id` int(9) unsigned NOT NULL AUTO_INCREMENT, 
    `username` varchar(40) DEFAULT NULL, 
    `facebook_id` bigint(15) unsigned DEFAULT NULL, 
    `facebook_username` varchar(30) DEFAULT NULL, 
    `password` varchar(20) DEFAULT NULL, 
    `profile_photo` varchar(100) DEFAULT NULL, 
    `first_name` varchar(40) DEFAULT NULL, 
    `middle_name` varchar(40) DEFAULT NULL, 
    `last_name` varchar(40) DEFAULT NULL, 
    `suffix_name` char(3) DEFAULT NULL, 
    `organization_name` varchar(100) DEFAULT NULL, 
    `organization` tinyint(1) unsigned DEFAULT NULL, 
    `address` varchar(200) DEFAULT NULL, 
    `city` varchar(40) DEFAULT NULL, 
    `state` varchar(20) DEFAULT NULL, 
    `zip` varchar(10) DEFAULT NULL, 
    `province` varchar(40) DEFAULT NULL, 
    `country` int(3) DEFAULT NULL, 
    `latitude` decimal(11,7) DEFAULT NULL, 
    `longitude` decimal(12,7) DEFAULT NULL, 
    `phone` varchar(20) DEFAULT NULL, 
    `sex` char(1) DEFAULT NULL, 
    `birthday` date DEFAULT NULL, 
    `about_me` varchar(2000) DEFAULT NULL, 
    `activities` varchar(300) DEFAULT NULL, 
    `website` varchar(100) DEFAULT NULL, 
    `email` varchar(150) DEFAULT NULL, 
    `referrer` int(4) unsigned DEFAULT NULL, 
    `referredid` int(9) unsigned DEFAULT NULL, 
    `verify` int(6) DEFAULT NULL, 
    `status` char(1) DEFAULT 'R', 
    `created` datetime DEFAULT NULL, 
    `verified` datetime DEFAULT NULL, 
    `activated` datetime DEFAULT NULL, 
    `network` datetime DEFAULT NULL, 
    `deleted` datetime DEFAULT NULL, 
    `logins` int(6) unsigned DEFAULT '0', 
    `api_logins` int(6) unsigned DEFAULT '0', 
    `last_login` datetime DEFAULT NULL, 
    `last_update` datetime DEFAULT NULL, 
    `private` tinyint(1) unsigned DEFAULT NULL, 
    `ip` varchar(20) DEFAULT NULL, 
    PRIMARY KEY (`user_id`), 
    UNIQUE KEY `username` (`username`), 
    KEY `facebook_id` (`facebook_id`), 
    KEY `status` (`status`), 
    KEY `state` (`state`) 
); 

CREATE TABLE `users_site1` (
    `user_id` int(9) unsigned NOT NULL, 
    `facebook_id` bigint(15) unsigned DEFAULT NULL, 
    `facebook_username` varchar(30) DEFAULT NULL, 
    `facebook_publish` tinyint(1) unsigned DEFAULT NULL, 
    `facebook_checkin` tinyint(1) unsigned DEFAULT NULL, 
    `facebook_offline` varchar(300) DEFAULT NULL, 
    `twitter_id` varchar(60) DEFAULT NULL, 
    `twitter_secret` varchar(50) DEFAULT NULL, 
    `twitter_username` varchar(20) DEFAULT NULL, 
    `type` char(1) DEFAULT 'M', 
    `referrer` int(4) unsigned DEFAULT NULL, 
    `referredid` int(9) unsigned DEFAULT NULL, 
    `session` varchar(60) DEFAULT NULL, 
    `api_session` varchar(60) DEFAULT NULL, 
    `status` char(1) DEFAULT 'R', 
    `created` datetime DEFAULT NULL, 
    `verified` datetime DEFAULT NULL, 
    `activated` datetime DEFAULT NULL, 
    `deleted` datetime DEFAULT NULL, 
    `logins` int(6) unsigned DEFAULT '0', 
    `api_logins` int(6) unsigned DEFAULT '0', 
    `last_login` datetime DEFAULT NULL, 
    `last_update` datetime DEFAULT NULL, 
    `ip` varchar(20) DEFAULT NULL, 
    PRIMARY KEY (`user_id`) 
); 

CREATE TABLE `connections` (
    `cid1` int(9) unsigned NOT NULL DEFAULT '0', 
    `cid2` int(9) unsigned NOT NULL DEFAULT '0', 
    `cid3` int(9) unsigned NOT NULL DEFAULT '0', 
    `type` char(2) NOT NULL, 
    `status` char(1) NOT NULL, 
    `created` datetime DEFAULT NULL, 
    `updated` datetime DEFAULT NULL, 
    PRIMARY KEY (`cid1`,`cid2`,`type`,`cid3`), 
    KEY `cid1` (`cid1`,`type`), 
    KEY `cid2` (`cid2`,`type`) 
); 
+0

请追加[query profiler](http://dev.mysql.com/tech-resources/articles/using-new-query-profiler.html)结果,它更直观。 – silverfox 2011-05-26 06:04:30

回答

2

而不是WHERE a.userid IN(...) OR a.userid IN(...)你应该使用其他连接:

select 
a.username,a.first_name,a.last_name,a.organization_name,a.organization,a.city, 
a.state,a.zip,a.country,a.profile_photo,a.facebook_id,a.twitter_id,u.reviews 
from accounts a 
join users_site1 u ON a.user_id=u.user_id 
join (select cid2 as id from connections 
     where cid1=10001006 AND type="MM" AND status="A" 
     union 
     select cid1 as id from connections 
     where cid2=10001006 AND type="MM" AND status="A") c 
on a.user_id = c.id 
order by RAND() LIMIT 4; 
+0

+ oned。我觉得这个__分解select和2个不同查询中的哪一个会对性能有很大的帮助。 – stefgosselin 2011-05-26 04:00:45

+0

哇!我不得不说,我不容易留下深刻的印象,但重写查询是令人印象深刻的。像魅力一样工作。我甚至不知道你可以这样做。谢谢您的帮助! – 2011-05-26 15:45:45

0

你尝试删除order by RAND()并再次运行?

我的结果低于:

+----+--------------------+-------------+----------------+-------------------+---------+---------+------------------+------+----------------------------------------------+ 
| id | select_type  | table  | type   | possible_keys  | key  | key_len | ref    | rows | Extra          | 
+----+--------------------+-------------+----------------+-------------------+---------+---------+------------------+------+----------------------------------------------+ 
| 1 | PRIMARY   | a   | ALL   | PRIMARY   | NULL | NULL | NULL    | 2 | Using where; Using temporary; Using filesort | 
| 1 | PRIMARY   | u   | ALL   | PRIMARY   | NULL | NULL | NULL    | 2 | Using where; Using join buffer    | 
| 3 | DEPENDENT SUBQUERY | connections | index_subquery | PRIMARY,cid1,cid2 | PRIMARY | 14  | func,const,const | 1 | Using where         | 
| 2 | DEPENDENT SUBQUERY | connections | ref   | PRIMARY,cid1,cid2 | PRIMARY | 14  | const,func,const | 1 | Using where         | 
+----+--------------------+-------------+----------------+-------------------+---------+---------+------------------+------+----------------------------------------------+ 
+0

我有,它根本不影响查询。这个问题似乎与帐户和users_site1表之间的连接有关。任何其他想法/建议? – 2011-05-26 03:27:52

+0

@ russell-c我已经在我的本地进行过测试,它与您的结果不同。什么是你的MySql版本?顺便说一句,表'帐户'错过列'twitter_id',表'users_site1'错过列'评论'。 – silverfox 2011-05-26 03:39:17

0

我不是一个MySQL的大师以任何手段,但一直都参与比在优化一次的高性能应用程序,尽管我更关注优化过程的执行结束,而不是寻找需要优化的部分。

我看到的最基本的东西是子查询看起来很有效率,但第一个查询的运行方式是使用where子句:...其中a.user_id IN(select cid2 ...)或a.user_id IN(select cid1来自...)在我非常愚蠢的意见中是一个表现杀手。

我会尝试优化性能的第一件事情,考虑尝试加入分解,将请求分成2个甚至3个查询。代码不太漂亮,但数据库将能够更高效地工作。在一个查询中做所有事情都会更好,这是一个神话。

这会给你带来什么?如果使用MyISam表,当您在查询中拥有较少的表时,锁定策略效率更高,并且您将减少冗余行访问,缓存将更加高效。如果你可以通过使用where来获得主查询(如果你分解的话,这将是最后一个)使用临时;使用filesort,你将有更快的响应。

通过配置SHOW SESSION STATUS和FLUSH状态的不同选项,还可以禁用缓存以获得真实的比较结果,通过在查询中添加SQL_NO_CACHE来尝试不同的选项,即SELSECT SQL_NO_CACHE a.username ...等。

分析和测量结果是您能够确定性能增益的唯一方法。不幸的是,这一步经常被忽视。

祝你好运!