2011-05-26 53 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 ...等。

分析和測量結果是您能夠確定性能增益的唯一方法。不幸的是,這一步經常被忽視。

祝你好運!