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 快很多。