一、索引是對DB優化最有效的方式html
varchar(10)定義的是字符的個數,若是是utf-8的話,最大是3X10個字節mysql
2、索引類型sql
一、MySql的索引是在存儲引擎層實現的,各個存儲引擎的的索引方式也是不一樣的數據庫
二、B-Tree索引api
MyISAM索引經過數據的物理位置引用被索引的行(數據存儲位置變化時須要更新索引),INNODB則根據主鍵引用被索引的行(沒有主鍵默則根據默認的策略生成主鍵)。緩存
B+樹是平衡樹性能優化
聚簇索引和非聚簇索引的分析:http://www.cnblogs.com/Arlen/articles/1605626.htmlbash
主鍵索引是聚簇索引,二級索引是非聚簇索引。服務器
INNODB索引的使用網絡
一、全值匹配
二、匹配最左前綴
三、匹配列前綴
四、匹配範圍值
五、精確匹配某一列並範圍匹配另外一列
六、索引覆蓋
七、由於索引樹的節點是有序的,因此除了按值查找以外,索引還能用於order by和group by操做。
通常,若是B樹能夠按照某種方式找到值,那麼也能按照這種方式排序。
限制:
一、必須匹配最左前綴
二、不能跳過索引中的列
三、若是某個列使用了範圍查詢,則其右邊的全部列都有不能使用索引了。
四、order by 要麼都是升序,要麼都是降序
五、若是是多個表,只有所有是第一個表才能使用
三、Hash 索引
在MySql裏,只有Memory引擎顯式的支持hash索引。
哈希索引自身只存儲對應的哈希值和行索引,而不存儲字段值,因此不能使用索引的值來避免讀取行。
哈希不支持部分索引列的查找,只支持所有匹配
哈希索引只支持列的全值匹配,只支持等值比較查詢。
INNODB裏面內置了自適應hash索引
四、全文索引
五、空間數據索引
六、在INNODB裏面模擬hash索引來優化
如url列的內容很大,建立的索引佔用的磁盤塊數也很大。若是按照url列來等值查找數據(where url=www.yunzhu.com),則下面的優化方法較好:
加一列hash_url,該列存儲的是url的MD5值。同時對hash_url建立索引,而後這樣查找:where url=www.yunzhu.com and
hash_url=MD5(www.yunzhu.com)
則mysql會優先走hash_url索引,找到對應的行而後比較url是否相同。
原理:hash_url索引佔的磁盤塊數很小,並且是是整數比較(比字符串比較快不少),因此會很快。
3、高性能索引策略
一、不是獨立的列(索引列是表達式或者函數的入參)
二、前綴索引(原理:給索引瘦身)
有時候某個列值是text或者varchar類型,並且值很大。這個時候若是直接對該列建立索引,磁盤塊數將會很大,能夠採起前綴索引進行優化。
前綴索引:只對某個列的前幾位作索引,而不是全部位。沒法使用前綴索引進行order by和group by
建立前綴索引語法:alter table mytable add key city(n) 則建立的索引對對city的前n個字符建立索引。
如何選擇n
實驗找出選擇性較高的n。
不能進行索引排序
三、多列索引
問題:index_merge
在每一個列上都建立索引,並不能提升mysql的查詢性能。
當出現服務器對多個索引作相交操做的時候(多個and條件),一般意味着須要一個包含全部相關列的索引,而不是多個獨立的單列索引。
當服務器出現對多個索引作相交操做的時候(多個or條件),須要消耗大量的CPU資源去重,排序等操做上。
四、選擇合適的索引列順序(下面的兩種條件都須要綜合考慮)
一、在不考慮排序和分組的時候,將選擇性較高的列放在前面一般是很好的。
考慮下面的一種極端的狀況:
雖然列A的選擇性很高,可是A的列有個值B重複率很大,若是where A=B來查詢的話,則效率會很低。此時應該由上層的程序來控制不走這個索引。
極端的狀況下B值的重複lv很高,高到和不走索引是同樣的。
二、須要根據運行頻率最高的查詢來調整索引列的順序。
3、聚簇索引
不是一種索引方式而是一種存儲方式,表明主鍵和數據緊湊的存儲在一塊兒。思考下聚簇索引的優勢和缺點:
優勢:一、覆蓋索引查詢能夠直接使用主鍵值
二、由於索引和數據存儲在同一個樹上,所以查找快。
三、聚簇
缺點:一、主鍵須要遞增,不然會形成頁分裂和碎片等。
MySql的數據文件就是索引文件,非葉子節點存儲的是主鍵,葉子節點存儲了所有的數據。
非聚簇索引也叫輔助索引。查詢的時候通常是先根據輔助索引查詢到主鍵以後再根據聚簇索引查詢到全部的數據行。
若是沒有定義主鍵,INNODB會選擇一個惟一的非空索引代替。若是沒有這樣的索引,INNODB會隱式的定義一個主鍵做爲聚簇索引。
聚簇索引的插入速度嚴重依賴插入順序。
更新聚簇索引的成本好高,由於須要將索引移到新的位置。
基於聚簇索引的表在插入新行,或者主鍵被更新致使須要移動行的時候,可能致使列分裂的問題。
使用optimize table命令來重建表並進行優化頁的填充。
INNODB應該儘量的按照主鍵的順序插入數據,而且儘量的使用單調增長的聚簇值來插入新行,避免更新主鍵值。 (不然須要移動聚簇索引,同時形成頁分裂,形成碎
片)。
4、索引覆蓋
若是二級索引可以覆蓋查詢,則能夠避免對主鍵索引的二次查詢
在發起一個覆蓋查詢的時候,explain的extrea列能夠看到using index的信息
不少的查詢語句能夠經過部分走索引覆蓋而進行優化。
能夠經過擴展索引來實現索引覆蓋從而進行優化
5、使用索引排序
若是explain的type值爲index,則說明mysql可能使用了索引掃描來排序。
MySql可使用同一個索引既知足排序,也能用於查找行。
只有當索引的列順序和order by自居的順序徹底一致,而且全部的列的排序方向都一致時,MySql才能使用索引來對結果進行排序。
若是查詢須要關聯多個表,只有當order by子句引用的字段所有爲第一個表時(優化執行器選擇執行的第一個表,不是sql的第一個select表),才能使用索引作排序。
order by子句和查找型查詢的限制是同樣的:須要知足索引的最左前綴要求,不然mysql都須要執行排序操做,而沒法利用索引排序。
6、 kengyu索引
有時候坑與索引能在必定的程度上優化查詢。
7、鏈接查詢
Inner Join
Natural Join
Left Outer Join
Right Outer Join
Full Outer Join
Cross Join
8、查詢性能優化
一、 主要是兩個方面進行性能優化
一、客戶端到服務器端之間的性能優化(減小數據包的傳輸等)
一、客戶端是否請求了過多的行和列(列少的話可能會走覆蓋索引,同時能減少數據包的大小)
二、客戶端能夠先查緩存減小查db的次數
二、mysql服務器端和存儲引擎端的優化
主要是根據3個指標
一、響應時間(請求鎖時間,排隊時間,IO時間)
二、掃描的行數和返回的行數(通常這個比例是10:1甚至更大)
二、where條件的利用好壞,從好到壞排序
一、where用於索引
二、服務器層作過濾
三、切分查詢(講一個大的查詢切分紅小的)
爲何切分查詢
一、如今的網絡帶寬比較大,因此將一個查詢切分紅多個是可行的
二、一次執行大量數據的刪除和查詢,會一次佔用不少的資源和鎖,給DB形成很大的壓力。這樣作能夠將DB的壓力分散到不一樣的時間。
三、將一個大的查詢(如:鏈接查詢)切分紅小的查詢能更好的利用緩存和操做緩存。
四、在應用層作關聯,能夠更好的對 數據庫進行切分,更容易作到高性能和可擴展。
五、在應用層作關聯查詢,在一個事務中,能夠把某個請求結果存儲起來,而後再找個事務的執行過程當中就不斷的重複去DB查了,這樣就減小了請求DB的次數。
如:若是直接請求mysql執行一個個大sql,返回的結果是沒有子查詢的結果的,若是下次須要子查詢的結果還須要如DB查。
四、客戶端/服務器端的通訊協議
一、半雙工,這致使兩方對通信過程不能有很好的控制能力,在發送出去報文以後就只能乾等着了。
二、客戶端用一個單獨的數據包將查詢發送給服務器。max_allowed_packet就規定了這個數據包的大小,客戶端發送完請求以後只能等着了。
三、服務器端的響應由多個數據包組成,客戶端只有接收到完整的所有結果以後,服務器端纔會釋放資源。
四、客戶端默認是先緩存所有的結果集,使得mysql服務器端能儘快的釋放掉資源。客戶端能夠經過參數決定本身是否是緩存所有的結果集。jdbc也有api能設置。
五、服務器獲得第一條數據的時候就能夠返回數據了,這樣客戶端也能夠同時處理數據了。服務器不是先查詢到全部的數據而後才返回數據,是查詢到第一條有效數據就返回數
據,這樣能夠避免即便服務器端須要返回大量的數據,也不會佔用大量的內存。mysql會針對每一行使用通信協議進行包裝而後寫到socket,固然tcp可能會把多行以一個批次傳輸。問題:客戶端能夠指定是否緩存服務器端的數據,那麼這個時候服務器端返回數據是否是就
受到限制了?
SQL_BUFFER_RESULT forces the result to be put into a temporary table. This helps MySQL free the table locks early and helps in cases where it takes a long time to send the result set to the client. This option can be used only for top-level SELECT statements, not for subqueries or following UNION. 這樣能儘快的釋放鎖,可是會佔用mysql的大量內存和資源 |
9、其餘
一、mysql在生成執行計劃的時候,5.6版本以前若是一個語句裏面含有子查詢時,子查詢是須要執行的,可是5.6解除了這個限制。
二、當掃描大量的行數時,能夠採起下面的措施(p201)
一、使用索引覆蓋
二、使用匯總表
三、重寫查詢
三、查看各個鏈接的狀態
show full processlist
在command列能看到各個查詢如今的狀態
一、sleep
二、query
三、locked
四、analysing and statistics
五、copying to tmp table[on disk]
group by,sort,union的時候會使用臨時文件
六、sorting result
七、sending data
10、explain和explain extend
explain extand能額外的查到mysql執行優化器優化以後的sql語句
一、type(訪問類型)
all 聚簇索引去掃描整個表(limit時不會掃描整個表),此時磁盤是順序訪問的。
一、只有聚簇索引 二、不能走索引覆蓋
index 索引掃描整個表(使用索引排序的時候會走)
一、extra列裏面有using index時只是磁盤順序讀取全部的索引,並不會再根據聚簇索引去磁盤隨機讀取其餘列的數據
二、extra列裏面沒有using index時,此時還須要根據聚簇索引去隨機讀取其餘列,比較好性能。
range
使用索引範圍查詢
IN,OR也是顯示的range,可是和正常的>這種範圍查詢有區別
ref(非惟一性索引或者使用索引前綴查詢)
索引等值查詢,可是可能查詢多多行
eq_ref(惟一性索引,例如主鍵id)
索引等值查詢,可是隻可能查詢到一條語句。
const,system
不用執行能根據SQL和mysql統計信息就能獲得結果,如:select id from XXX where id=1;
NULL
不用執行就能獲得結果
分析:all和index?
all和index都是掃表。當是索引覆蓋查詢,或者能利用索引排序的時候纔會走
二、rows
大概顯示會掃描到多少行(若是索引覆蓋查詢的話,一個索引也算一行,因此這裏rows指的不是掃描行同時獲得全部的列)
rows也不能代表limit,如select * from XX limit 1,實際上mysql只會查詢掃描一列就結束。
三、key_len(所使用索引的最大程度)
索引的長度,能根據這個值肯定最終sql在聯合索引裏面到底使用了前幾個列走索引
四、possible_keys
可能走的索引,具體的例子見案例分析的第6個
五、key
優化器從possible_keys裏最終選擇的索引(根據IO次數選擇合適的,具體的例子見案例分析的第6個)
六、id
根據sql裏面的id的順序由1遞增排列
七、explain的執行結果順序
explain的執行結果裏面的id可能不是遞增的,explain顯示的列的順序就是mysql的執行順序。
八、select_type
一、simple
二、SUBQUERY(DEPENDENT_SUBQUERY)
select (select XX from XX) from XX
括號裏面的是SUBQUERY
三、UNION
四、DERIVED
from (select XXX from XXX) 括號裏面的是DERIVED
五、PRIMARY
最外層循環
九、table
一、DERIVED
二、真實表
三、UNION以後的表
<UNION 1,2>
十、extra
using index (說明只使用該索引,不用再根據索引得到的id去查詢聚簇索引得到數據,也就是索引覆蓋)
,using where (不帶where的也可能出現using where,說明查詢可收益於不一樣的索引)
using tempory(臨時表)
using filesore(多是在內存或者磁盤進行排序)
11、案例分析
一、status命令
能查看mysql的版本號和服務器的運行快照信息,以及mysql客戶端的信息
二、show create table;
查看建表語句
三、索引覆蓋案例
表結構:
CREATE TABLE `test` ( `id` int(11) NOT NULL, `name` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
select * from test;
執行結果以下:
能夠看出執行優化器選擇了索引覆蓋查詢。
注意:任何的索引默認主鍵都是索引的一部分
當sql沒有指定order by的時候,查詢出的結果是無序的。
select * from test order by id;
執行結果以下:
能夠看出優化器選擇了使用索引排序
四、聚簇索引和不是聚簇索引的選擇
select count(*) from test;
能夠看出count(*)也是掃描整個索引
count(*)由於只用統計數據,確定走索引覆蓋(聚簇索引或者非聚簇索引)。
selct count(*)和select a,b基本相似,可是count(*)通常能夠走索引覆蓋,extra 確定包含using index。
CREATE TABLE `AA` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵', `gmt_create` datetime NOT NULL COMMENT '建立時間', `gmt_modified` datetime NOT NULL COMMENT '修改時間', `appkey` varchar(32) NOT NULL COMMENT , `std_api_id` varchar(64) NOT NULL COMMENT , `cfg_h_group` varchar(128) NOT NULL COMMENT , `cfg_key` varchar(128) NOT NULL COMMENT , `cfg_value` varchar(300) NOT NULL COMMENT '配置項value', `creator` varchar(32) NOT NULL COMMENT '建立人', `modifier` varchar(32) NOT NULL COMMENT '修改人', `cfg_v_group` varchar(32) NOT NULL COMMENT , PRIMARY KEY (`id`), KEY `index_appkey_std_api_id` (`appkey`,`std_api_id`), KEY `index_appkey_h_group` (`appkey`,`cfg_h_group`) ) ENGINE=InnoDB AUTO_INCREMENT=490004 DEFAULT CHARSET=utf8 COMMENT=''
explain select count(*) from AA
該表存在聚簇索引還有兩個聯合索引,由於確定要所有掃描全部的行(但不必定是全部的列),優化執行器會比較怎麼掃描讀的磁盤塊數(IO數)更少。
一、聚簇索引:葉子部分存儲的是所有的列,因此pass。因此通常count(*)都不會走聚簇索引掃描
二、聯合索引 選擇聯合索引數據量較小的,因此最後優化執行器選擇了下面的執行計劃
1
|
|
1 |
2
|
|
SIMPLE |
3
|
|
AA |
4
|
|
index |
5
|
|
6
|
|
index_appkey_std_api_id |
7
|
|
292 |
8
|
|
9
|
|
61181 |
10
|
|
Using index |
六、索引的選擇
explain select count(*) from AA where appkey='aa';
從執行計劃能夠看出,優化執行器鎖定兩個索引(從possible_keys上看出),可是兩個聯合索引都只能選擇使用第一列,因此在兩個聯合索引中選擇索引佔的數據較小的那個索
引,最終生成了下面的執行計劃
1
|
|
1 |
2
|
|
SIMPLE |
3
|
|
AA |
4
|
|
ref |
5
|
|
index_appkey_std_api_id,index_appkey_h_group |
6
|
|
index_appkey_std_api_id |
7
|
|
98 |
8
|
|
const |
9
|
|
1 |
10
|
|
Using where; Using index |
七、 select (select XX from XX) from XX類型分析
返回的行數? 等於select count(*) from XX;
select (select id from s) from test;
本質上是個循環嵌套查詢
八、select常量
返回多少行數? select count(*) from XX;
從執行計劃能夠看出,這種sql確定走索引覆蓋(using index,由於查的是常量)。分析這種sql時和普通的sql同樣對待,只是查的列比較特殊,確定走索引覆蓋
九、鏈接查詢(只支持左外鏈接,右外鏈接,內鏈接(等值鏈接))
雖然沒有 join 關鍵字,可是下面的查詢也是鏈接查詢。
下面的查詢沒有where語句,實際是獲得一個笛卡爾乘積,執行引擎在執行的時候是遞歸執行,隨意因此外層循環的數量儘量少,因此查執行table s,而後再執行table
test。table S只有主鍵,因此type是all。table test能夠進行索引覆蓋,因此type是index。
十、雖然select多是所有數據,或者部分數據可是聯合索引能作到覆蓋查詢,並且可使用索引排序。
十一、IN查詢
注意:using where
十二、derived深刻分析
一、生成derived表的查詢是能夠走索引的
二、primary對derived的查詢時不能走索引的,只能走where。
12、具體的優化
一、limit分頁
limit 10000,10,此時mysql須要讀取不少的行去得到偏移量爲10000的數據,可是以前讀取過的10000個數據是無用的。
方案1:
select film_id,description from sakila.film order by title limit 50,5
能夠看出須要全表掃描,而後再根據title排序,而後在取偏移量爲50的5條數據。性能弱爆了。
優化成:select film.film_id,film.dscription from sakila.film inner join (select film_id from sakila.film order by title limit 50,5)as lim using(fim_id);
mysql雖然查的前50個沒有用,可是由於using index,只是浪費了50次查一個索引,沒有查到一個索引獲得主鍵再去查全部的數據。這裏實際上用了延遲技術。
方案2:
想辦法使得limit A,B中的A變小
二、UNION
UNION確定會使用臨時表存放下來而後再發送數據給客戶端,而其實是能夠直接發送數據給客戶端的。
通常儘可能使用UNION ALL,不然mysql須要對UNION的結果排除重複的
三、最大值最小值
優化方法:由於最大值和最小值只多是一個值,因此可使用limit1來進行優化。
極端的狀況下會進行掃表而後才獲得一個最大或者最小值,經過改寫SQL能改變這種狀況。
查索引上的最大值和最小值時,能夠直接獲得。
四、優化count查詢
一、count(A),其中A能夠是表達式,能夠是列,也能夠是*。*表明查詢所有的行數,列表明查詢非NULL值的個數,表達式表明有值的個數。
二、MYISAM在查詢不帶where條件的count(*)時是const類型,可是若是帶where或者count(A)不能轉換成count(*)和其餘的引擎的執行同樣。
在MYISAM:select count(*) from a where id>5;則執行時會掃描不少的行數。能夠優化成:
select count(*) from a - select count(*) from a where id<=5;
三、使用近似值,彙總表等
五、優化鏈接查詢
一、A和B鏈接時,只須要在一個表上創建索引就能夠了。
二、確保group by 和order by 中的表達式只涉及到一個表中的列,只有這樣mysql才能使用索引優化這個過程
六、優化group by和distinct
當沒有辦法使用索引的時候,mysql會使用臨時表或者文件排序來分組。
使用分組的時候select的列儘可能是和group by 有關的字段。
十3、其餘
一、建議執行優化器
SE INDEX
在你查詢語句中表名的後面,添加 USE INDEX 來提供你但願 MySQ 去參考的索引列
表,就可讓 MySQL 再也不考慮其餘可用的索引。
Eg:SELECT * FROM mytable USE INDEX (mod_time, name) ...
IGNORE INDEX
若是你只是單純的想讓 MySQL 忽略一個或者多個索引,可使用 IGNORE INDEX 做
爲 Hint。
Eg:SELECT * FROM mytale IGNORE INDEX (priority) ...
FORCE INDEX爲強制 MySQL 使用一個特定的索引,可在查詢中使用 FORCE INDEX 做爲 Hint。Eg:SELECT * FROM mytable FORCE INDEX (mod_time) ...