mysql優化實踐

mysql單表超過50W會比較卡,而且in查詢也很耗時間。現在有個需求是,查詢關注圈的動態

首先是把關注的人查詢出來,然後再查詢關注的人的動態,最後再把自己的動態查詢出來,關聯分頁,排序。

界面顯示效果是這樣的

動態表:

CREATE TABLE `dynamic_state` (
  `id` BIGINT(20) NOT NULL,
  `name` VARCHAR(50) DEFAULT NULL COMMENT '暱稱',
  `head_portrait` VARCHAR(500) DEFAULT NULL COMMENT '頭像',
  `talk_id` VARCHAR(50) DEFAULT NULL,
  `content` VARCHAR(1080) DEFAULT NULL COMMENT '內容'
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;

關注表:

CREATE TABLE `concern` (
  `id` BIGINT(20) NOT NULL,
  `talk_id` BIGINT(20) DEFAULT NULL,
  `concern_talk_id` BIGINT(20) DEFAULT NULL COMMENT '我關注的talkId'
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
SELECT id,talk_id,content,head_portrait,NAME FROM dynamic_state WHERE  talk_id IN(SELECT concern_talk_id FROM concern WHERE talk_id='18977197')  UNION 
SELECT id,talk_id,content,head_portrait,NAME  FROM dynamic_state WHERE    talk_id='18977197' ORDER BY id desc limit 0,10;

再沒有使用任何優化的情況下,查詢速度是13秒左右。

這裏首先用一下索引,因爲我們都是根據talk_id關聯查詢的所以我們需要對動態表和關注表,建立索引。                                     
 ALTER TABLE dynamic_state ADD INDEX talkId (talk_id);
 ALTER TABLE concern ADD INDEX talkId (talk_id);   

建立了索引以後速度在1.5秒左右。

n 和exists 
in是把外表和內表作hash 連接,而exists 是對外表作loop 循環,每次loop 循環再對內表進行查詢。 
一直以來認爲exists 比in 效率高的說法是不準確的。如果查詢的兩個表大小相當,那麼用in 和exists 差別不大。

如果兩個表中一個較小,一個是大表,則子查詢表大的用exists,子查詢表小的用in
這裏動態表的數據量在20W,而關注表的數量在40W左右。所以這裏用exists優化會更好一點。

SELECT *FROM dynamic_state d WHERE EXISTS(SELECT talk_id FROM concern WHERE talk_id=d.talk_id AND talk_id='18977197')

無論我把判斷條件放在外面還是裏面,都是一樣的結果,查詢很慢,很慢,差不多20幾秒,完全比in慢很多。

這裏來分析一下他們的實現原理:

select * from A
where id in(select id from B)

以上查詢使用了in語句,in()只執行一次,它查出B表中的所有id字段並緩存起來.之後,檢查A表的id是否與B表中的id相等,如果相等則將A表的記錄加入結果集中,直到遍歷完A表的所有記錄.
它的查詢過程類似於以下過程

List resultSet=[];
Array A=(select * from A);
Array B=(select id from B);

for(int i=0;i<A.length;i++) {
   for(int j=0;j<B.length;j++) {
      if(A[i].id==B[j].id) {
         resultSet.add(A[i]);
         break;
      }
   }
}
return resultSet;

可以看出,當B表數據較大時不適合使用in(),因爲它會B表數據全部遍歷一次.
如:A表有10000條記錄,B表有1000000條記錄,那麼最多有可能遍歷10000*1000000次,效率很差.
再如:A表有10000條記錄,B表有100條記錄,那麼最多有可能遍歷10000*100次,遍歷次數大大減少,效率大大提升.

結論:in()適合B表比A表數據小的情況

select a.* from A a 
where exists(select 1 from B b where a.id=b.id)

以上查詢使用了exists語句,exists()會執行A.length次,它並不緩存exists()結果集,因爲exists()結果集的內容並不重要,重要的是結果集中是否有記錄,如果有則返回true,沒有則返回false.
它的查詢過程類似於以下過程

List resultSet=[];
Array A=(select * from A)

for(int i=0;i<A.length;i++) {
   if(exists(A[i].id) {    //執行select 1 from B b where b.id=a.id是否有記錄返回
       resultSet.add(A[i]);
   }
}
return resultSet;

當B表比A表數據大時適合使用exists(),因爲它沒有那麼遍歷操作,只需要再執行一次查詢就行.
如:A表有10000條記錄,B表有1000000條記錄,那麼exists()會執行10000次去判斷A表中的id是否與B表中的id相等.
如:A表有10000條記錄,B表有100000000條記錄,那麼exists()還是執行10000次,因爲它只執行A.length次,可見B表數據越多,越適合exists()發揮效果.
再如:A表有10000條記錄,B表有100條記錄,那麼exists()還是執行10000次,還不如使用in()遍歷10000*100次,因爲in()是在內存裏遍歷比較,而exists()需要查詢數據庫,我們都知道查詢數據庫所消耗的性能更高,而內存比較很快.

但是經過我的實驗發現加了索引之後in 要比exists 快很多。

 

                                                                                      

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章