高效能的MySQL(6)查詢慢與重構查詢

2021-09-04 01:53:46 字數 3268 閱讀 2144

只有好的庫表結構、合理的索引還不夠,我們還需要合理的設計查詢,齊頭並進,乙個不少才能充分發揮mysql的優勢。

一、查詢為什麼會慢?

每乙個查詢由一系列的子任務組成,每個子任務都會消耗一定的時間。這個我們在之前的單個查詢分析時已經簡單介紹了,當然還有額外的因素,比方說包括網路,cpu計算,統計資訊,執行計畫,鎖等待等操作,或者底層引擎在呼叫記憶體,cpu操作,i/o操作等上的消耗時間。、

優化查詢的目的就是減少和消除這些操作所花費的時間。

查詢效能低下的最基本原因是訪問的資料太多,大部分的效能低下的查詢可以通過減少訪問的資料量進行優化,一般有2個簡單的分析方法:

1、確認應用程式是否返回了大量超過需要的資料,這就是說訪問了太多的行,也有時候是因為訪問了太多的列,這會增加很多額外的開銷,包括,網路,cpu,記憶體等。

一些常見的例子:

a、查詢不需要的記錄

乙個常見的錯誤是常常會以為mysql會只返回需要的資料,實際上卻是先返回全部的查詢結果再進行計算,乙個簡單有效的解決方法是在查詢後面加上limit。

b、多表關聯時返回全部列

比如,要查詢所有在電影hreo中出現的演員,不要這樣去寫

select * from actor

inner join film_actor using(actor_id)

inner join film using(film_id)

where film.title = "hreo";

這會返回三個表的全部列,應該只返回需要的列

select actor.* from actor

inner join film_actor using(actor_id)

inner join film using(film_id)

where film.title = "hreo";

c、不要總是取出全部的列

select * from actor....
d、重複查詢相同的資料

2、確認mysql服務層是否在返回前檢索大量超過需要的資料行。

如果查詢為了返回結果掃瞄過多的資料,那麼就不合適了,一般看3個指標:

a、響應時間

響應時間分為服務時間和排隊時間。這個很難細分,如果是在乙個合理的值,那就可以接受。

b、掃瞄的行和返回的行

這個在一定程度上能夠說明該查詢找到需要的資料效率怎麼樣。理想的情況下,掃瞄的行和返回的行是相同的,不過實際中這是很難的,特別是做關聯查詢時。

c、掃瞄的行和訪問型別

在explain語句中的type列反應了訪問型別。從全表掃瞄到索引掃瞄,範圍掃瞄,唯一索引查詢,常數引用等,速度從慢到快,掃瞄的行從大到小。一般我們增加乙個合適的索引就可以很高效了。

create table `emp5` (

`id` int(11) not null default '0',

`name` varchar(100) not null,

`job` varchar(100) not null,

`num1` int(10) default null,

`num2` int(10) default null,

`num3` int(10) default null,

`job_num` int(10) default null,

`d` date default null,

primary key (`id`),

key `job_num` (`job_num`)

) engine=myisam default charset=utf8;

這裡說明使用了索引型別,如果去掉了索引

訪問型別為全表掃瞄(all)

一般mysql能夠使用如下三種方式應用where,從好到壞依次是

a、在索引中使用where過濾不匹配的資料,引擎層完成。

b、使用索引覆蓋掃瞄,在extra中出現了using index,直接從索引中過濾掉不需要的記錄,服務層完成,不需回表取資料。

c、從資料表返回資料,然後過濾,在extra中出現了using where ,在服務層完成。

二、重構查詢

乙個複雜查詢還是多個簡單查詢,是否需要將乙個複雜的查詢分成多個簡單的查詢,這是乙個需要好好衡量的問題了。

1、切分查詢

刪除資料就是乙個很好的例子。定期的清楚大量的資料,可能需要鎖住大量的資料,佔滿整個事務,耗盡資源,阻塞很多小的查詢,切分是乙個很好的辦法。

比方說:把如下的句子

delete from message where create_time < date_sub(now(),interval 3 month);
換成如下:

rows_affected=0

dowhile rows_affected > 0

一次刪除10000行,影響就會很小,壓力就會分擔開來了。

2、分解關聯查詢

把關聯查詢進行分解,例如下面的查詢:

select * from tag

join tag_post on tag_post.tag_id = tag_id

join post on tag_post.post_id = post.id

where tag.tag = 'hreo';

分解為:

select * from tag where tag = 'hreo';

select * from tag_post where tag_id=1234;

select * from post where id in (123,546,432);

乍一看,我們好像複雜化了,但是分解後還是有很多好處的,有的時候我們的卻是需要這樣做的 。

a、讓快取的效率更高,如果第乙個查詢的結果已經快取了,那麼就可以跳過第乙個查詢,另外對一mysql的查詢快取query cache來說,如果關聯的表發生了修改,就無法使用快取了,拆分後,那麼乙個表的改變不會影響其他表的快取。

b、單個查詢可以減少鎖的競爭。

c、在應用層做關聯,有更好的擴充套件性。

d、可以減少冗餘記錄的查詢,因為資料庫關聯查詢時,可能需要重複的訪問一部分資料。

e、這個相當於實現了雜湊關聯,而不是mysql的巢狀迴圈關聯,某些時候雜湊關聯效率高很多,這點以後會有介紹。

這個效果,在負載均衡,或者資料分布在不同的資料庫是更明顯。

《高效能MySQL》第6章 查詢效能優化

6.2 慢查詢基礎 優化資料訪問 6.2.1 是否向資料庫請求了不需要的資料 有些查詢會請求超過實際需要的資料,然後這些多餘的資料會被應用程式丟棄。這會給mysql伺服器帶來額外的負擔,並增加網路開銷,另外也會消耗應用伺服器的cpu和記憶體資源。6.2.2 mysql是否在掃瞄額外的記錄6.3 重構...

《高效能MySQL》第6章查詢效能優化(4)

mysql通過建立並填充臨時表的方式來執行union,因此很多優化無法在union查詢中很好應用。所以需要手動將where limit order by等語句下推到union子查詢中。如果不是需要對結果去重,請使用union all。即使有all,mysql也會將結果先存於臨時表中,再讀出,這很多時...

如何寫出高效能的MySQL查詢

想寫這樣一篇文章很久了,但始終沒有下手。最近幫同事看了幾個查詢,而且自己也在考慮乙個索引系統的問題,所以今天就把這個寫了。介紹一下mysql的索引機制,還有一些mysql查詢的優化策略。鄙人才疏學淺,很可能說的不對,請路過的各位大俠批評指正,獻醜了。jiajun.org 官方分割線 首先,說說mys...