重慶分公司,新征程啟航
為企業提供網站建設、域名注冊、服務器等服務
為企業提供網站建設、域名注冊、服務器等服務
建立索引,要使用離散度(選擇度)更高的字段。
創新互聯建站公司2013年成立,先為象山等服務建站,象山等地企業,進行企業商務咨詢服務。為象山企業網站制作PC+手機+微官網三網同步一站式服務解決您的所有建站問題。
我們先來看一個重要的屬性列的 離散度,
count(distinct(column_name)) : count(*) -- 列的全部不同值個數:所有數據行行數
數據行數相同的情況下,分子越大,列的離散度就越高。簡單來說,如果列的重復值越多,離散度就越低,重復值越少,離散度就越高。
當字段值比較長的時候,建立索引會消耗很多的空間,搜索起來也會很慢。我們可以通過截取字段的前面一部分內容建立索引,這個就叫前綴索引。
創建一張商戶表,因為地址字段比較長,在地址字段上建立前綴索引
create table shop(address varchar(120) not null);
alter table shop add key(address(12));? // 截取12個字符作為前綴索引是最優的嗎?
問題是,截取多少呢?截取得多了,達不到節省索引存儲空間的目的,截取得少了,重復內容太多,字段的散列度(選擇性)會降低。怎么計算不同的長度的選擇性呢?
先看一下字段在全部數據中的選擇度計算公式:
select count(distinct address) / count(*) from shop;
select count(distinct left(address, n)) / count(*) as subn from shop;
count(distinct left(address,n)) / count(*) 的結果是會隨著 n 的變大而變大。舉個例子,現在有兩個address(東大街長興小區,東大街福樂小區),那么 distinct(address,2) distinct(address,3)
==所以,截取的長度越長就會越接近字段在全部數據中的選擇度
==所以,我們要權衡索引大小和查詢速度。
舉個例子,通過不同長度去計算,與全表的選擇性對比:
SELECT? COUNT(DISTINCT(address))/COUNT(*) sub,? ? ? ? ? ? -- 字段在全部數據中的選擇度
COUNT(DISTINCT(LEFT(address,5)))/COUNT(*) sub5,? -- 截取前5個字符的選擇度
COUNT(DISTINCT(LEFT(address,7)))/COUNT(*) sub7,?
COUNT(DISTINCT(LEFT(address,9)))/COUNT(*) sub9,
COUNT(DISTINCT(LEFT(address,10)))/COUNT(*) sub10,? -- 截取前10個字符的選擇度
COUNT(DISTINCT(LEFT(address,11)))/COUNT(*) sub11,
COUNT(DISTINCT(LEFT(address,12)))/COUNT(*) sub12,
COUNT(DISTINCT(LEFT(address,13)))/COUNT(*) sub13,
COUNT(DISTINCT(LEFT(address,15)))/COUNT(*) sub15
FROM shop;
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| sub? ? | sub5? | sub7? | sub9? | sub10? | sub11? | sub12? | sub13? | sub15? |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| 0.9993 | 0.0225 | 0.4663 | 0.8618 | 0.9734 | 0.9914 | 0.9943 | 0.9943 | 0.9958 |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
可以看到在截取 11 個字段時 sub11(0.9993) 就已經很接近字段在全部數據中的選擇度 sub(0.9958)了,而且長度也相較后面更短一些, 綜合考慮比較合適。
ALTER TABLE shop ADD KEY (address(11));
1.索引的個數不要過多(浪費空間,更新變慢)
2.在用于 where 判斷 order 排序和 join 的(on)字段上創建索引
3.區分度低的字段,例如性別,不要建索引(離散度太低,導致掃描行數過多)
4.更新頻繁的值,不要作為主鍵或者索引(頁分裂)
5.不建議用無序的值作為索引,例如身份證、UUID(在索引比較時需要轉為ASCII,并且插入時可能造成頁分裂)
6.若在多個字段都要創建索引的情況下,聯合索引優于單值索引
7.聯合索引把散列性高(區分度高)的值放在前面
MySQL數據庫建立索引步驟:
1、使用SQLPlus或者PLSQL登錄MySQL數據庫。
2、執行語句create
index
indexname
on
table(col)
tablespace
tablespacename;其中
"indexname"為需要建立索引的索引名;
"table"是需要建立索引的表;
"col"為需要建立索引的字段列;
”tablespacename“為建立索引存放的空間(如果不需要制定表空間則使用“create
index
indexname
on
table(col)”)。
MySQL 前綴索引能有效減小索引文件的大小,提高索引的速度。但是前綴索引也有它的壞處:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前綴索引,也不能把它們用作覆蓋索引(Covering Index)。
集一個索引包含多個列(最左前綴匹配原則)
索引列的值必須唯一,但允許有空值
全文索引為FUllText,在定義索引的列上支持值的全文查找,允許在這些索引列中插入重復值和空值,全文索引可以在CHAR,VARCHAR,TEXT類型列上創建
設定主鍵后數據會自動建立索引,InnoDB為聚簇索引
即一個索引只包含單個列,一個表可以有多個單列索引
覆蓋索引是指一個查詢語句的執行只用從所有就能夠得到,不必從數據表中讀取,覆蓋索引不是索引樹,是一個結果,當一條查詢語句符合覆蓋索引條件時候,MySQL只需要通過索引就可以返回查詢所需要的數據,這樣避免了查到索引后的回表操作,減少了I/O效率
查看索引
列名解析:
刪除索引
查看:
刪除前:
刪除后:
普通的索引,沒有什么介紹
查看:(注意和前綴索引Sub_part的區別)
當索引的列是unique的時候,會生成唯一索引,唯一索引關于null有下列兩種情況
SQLSERVER 下的唯一索引的列,允許null值,但最多允許有一個空值
MYSQL下的唯一索引的列,允許null值,并且允許多個空值
查看:
會建立兩個索引,一個非聚簇索引,一個是唯一索引
結果:
可以插入兩個空值(明人不說暗話,我喜歡MySQL)
一方面,它不會索引所有字段所有字符,會減小索引樹的大小.
另外一方面,索引只是為了區別出值,對于某些列,可能前幾位區別很大,我們就可以使用前綴索引。
一般情況下某個前綴的選擇性也是足夠高的,足以滿足查詢性能。對于BLOB,TEXT,或者很長的VARCHAR類型的列,必須使用前綴索引,因為MySQL不允許索引這些列的完整長度。
查看:
查看:
復合索引的最左前綴匹配原則 :
對于復合索引,查詢在一定條件才會使用該索引
減少開銷。 建一個聯合索引(col1,col2,col3),實際相當于建了(col1),(col1,col2),(col1,col2,col3)三個索引。每多一個索引,都會增加寫操作的開銷和磁盤空間的開銷。對于大量數據的表,使用聯合索引會大大的減少開銷!
覆蓋索引。 對聯合索引(col1,col2,col3),如果有如下的sql: select col1,col2,col3 from test where col1=1 and col2=2。那么MySQL可以直接通過遍歷索引取得數據,而無需回表,這減少了很多的隨機io操作。減少io操作,特別的隨機io其實是dba主要的優化策略。所以,在真正的實際應用中,覆蓋索引是主要的提升性能的優化手段之一。
效率高。 索引列越多,通過索引篩選出的數據越少。有1000W條數據的表,有如下sql:select from table where col1=1 and col2=2 and col3=3,假設假設每個條件可以篩選出10%的數據,如果只有單值索引,那么通過該索引能篩選出1000W10%=100w條數據,然后再回表從100w條數據中找到符合col2=2 and col3= 3的數據,然后再排序,再分頁;如果是聯合索引,通過索引篩選出1000w10% 10% *10%=1w。
在模糊搜索中很有效,搜索全文中的某一個字段,可以參考這篇博文
:
我們先進行下面一個實驗看看InnoDB下的主鍵索引的一個現象。
查看:
我們插入進去的時候,數據的id都是亂序的,為什么這里最后select查詢出來的結果都是進行了排序?
這是因為InnoDB索引底層實現的是B+tree,B+tree具有下列的特點:
所以上面的排序是為了使用B+tree的結構 ,B+tree為了范圍搜索,將主鍵按照從小到大排序后,拆分成節點。后續還有新的節點進入的時候,和B-tree相同的操作,會進行分裂。
一般來說,聚簇索引的B+tree都是三層
InnoDB中主鍵索引一定是聚簇索引,聚簇索引一定是主鍵索引。
為什么這里輔助索引葉子結點不直接存儲數據呢?
MYISAM只有非聚簇索引,索引最終指向的都是物理地址。
Q:既然有回表的存在,那么聚簇索引的優勢在哪里?
Q:主鍵索引作為聚簇索引需要注意什么
在查詢語句中使用LIke關鍵字進行查詢時,如果匹配字符串的第一個字符為"%",索引不會使用。如果“%”不是在第一位,索引就會使用
多列索引是在表的多個字段上創建的索引,滿足最左前綴匹配原則,索引才會被使用
查詢語句只有Or關鍵字時候,如果OR前后的兩個條件都是索引,這這次查詢將會使用索引,否則Or前后有一個條件的列不是索引,那么查詢中將不使用索引
在mysql中,索引是一種特殊的數據庫結構,由數據表中的一列或多列組合而成,可以用來快速查詢數據表中有某一特定值的記錄。
通過索引,查詢數據時不用讀完記錄的所有信息,而只是查詢索引列即可。
通過索引,查詢數據時不用讀完記錄的所有信息,而只是查詢索引列。否則,數據庫系統將讀取每條記錄的所有信息進行匹配。
可以把索引比作新華字典的音序表。例如,要查“庫”字,如果不使用音序,就需要從字典的 400 頁中逐頁來找。但是,如果提取拼音出來,構成音序表,就只需要從 10 多頁的音序表中直接查找。這樣就可以大大節省時間。
因此,使用索引可以很大程度上提高數據庫的查詢速度,還有效的提高了數據庫系統的性能。
索引的優缺點
索引有其明顯的優勢,也有其不可避免的缺點。
優點
索引的優點如下:
1、通過創建唯一索引可以保證數據庫表中每一行數據的唯一性。
2、可以給所有的 MySQL 列類型設置索引。
3、可以大大加快數據的查詢速度,這是使用索引最主要的原因。
4、在實現數據的參考完整性方面可以加速表與表之間的連接。
5、在使用分組和排序子句進行數據查詢時也可以顯著減少查詢中分組和排序的時間
缺點
增加索引也有許多不利的方面,主要如下:
1、創建和維護索引組要耗費時間,并且隨著數據量的增加所耗費的時間也會增加。
2、索引需要占磁盤空間,除了數據表占數據空間以外,每一個索引還要占一定的物理空間。如果有大量的索引,索引文件可能比數據文件更快達到最大文件尺寸。
3、當對表中的數據進行增加、刪除和修改的時候,索引也要動態維護,這樣就降低了數據的維護速度。
使用索引時,需要綜合考慮索引的優點和缺點。
在實際開發中使用數據庫時,難免會遇到一些大表數據,對這些數據進行查詢時,有時候SQL會查詢得特別慢,這時候,有經驗的老師傅會告訴你,你看一下哪幾個字段查的多,加一個索引就好了。
那么,怎么合理地建立索引呢?這里分享一下我的一些經驗,如有不妥之處,歡迎批評指正。
1、不要盲目建立索引 , 先分析再創建
索引雖然能大幅度提升我們的查詢性能,但也要知道,在你進行增刪改時,索引樹也要同樣地進行維護。所以,索引不是越多越好,而是按需建立。最好是在一整塊模塊開發完成后,分析一下,去針對大多數的查詢,建立聯合索引。
2、使用聯合索引盡量覆蓋多的條件
這是說在一個慢sql里假如有五個where ,一個 order by ,那么我們的聯合索引盡量覆蓋到這五個查詢條件,如果有必要,order by 也覆蓋上 。
3、小基數字段不需要索引
這個意思是,如果一張表里某個字段的值只有那么幾個,那么你針對這個字段建立的索引其實沒什么意義,比如說,一個性別字段就兩種結果,你建了索引,排序也沒什么意思(也就是索引里把男女給分開了)
所以說,索引盡量選擇基數大的數據去建立,能最大化地利用索引
4、長字符串可以使用前綴索引
我們建立索引的字段盡量選擇字段類型較小的,比如一個varchar(20)和varchar(256)的,我們在20的上面建立的索引和在256上就有明顯的差距(字符串那么長排序也不好排呀,唉)。
當然,如果一定是要對varchar(256)建立索引,我們可以選擇里面的前20個字符放在索引樹里(這里的20不絕對,選擇能盡量分辨數據的最小字符字段設計),類似這樣KEY index(name(20),age,job) ,索引只會對name的前20個字符進行搜索,但前綴索引無法適用于order by 和 group by。
5、對排序字段設計索引的優先級低
如果一個SQL里我們出現了范圍查找,后邊又跟著一個排序字段,那么我們優先給范圍查找的字段設置索引,而不是優先排序。
6、如果出現慢SQL,可以設計一個只針對該條SQL的聯合索引。
不過慢SQL的優化,需要一步步去進行分析,可以先用explain查看SQL語句的分析結果,再針對結果去做相應的改進。explain的東西我們下次再講。
PS:在 select 語句之前增加 explain 關鍵字,MySQL 會在查詢上設置一個標記,執行查詢會返回執行計劃的信息,而不是 執行這條SQL。
1.選擇唯一性索引
唯一性索引的值是唯一的,可以更快速的通過該索引來確定某條記錄。例如,學生表中學號是具有唯一性的字段。為該字段建立唯一性索引可以很快的確定某個學生的信息。如果使用姓名的話,可能存在同名現象,從而降低查詢速度。
2.為經常需要排序、分組和聯合操作的字段建立索引
經常需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的字段,排序操作會浪費很多時間。如果為其建立索引,可以有效地避免排序操作。
3.為常作為查詢條件的字段建立索引
如果某個字段經常用來做查詢條件,那么該字段的查詢速度會影響整個表的查詢速度。因此,為這樣的字段建立索引,可以提高整個表的查詢速度。
4.限制索引的數目
索引的數目不是越多越好。每個索引都需要占用磁盤空間,索引越多,需要的磁盤空間就越大。修改表時,對索引的重構和更新很麻煩。越多的索引,會使更新表變得很浪費時間。
5.盡量使用數據量少的索引
如果索引的值很長,那么查詢的速度會受到影響。例如,對一個CHAR(100)類型的字段進行全文檢索需要的時間肯定要比對CHAR(10)類型的字段需要的時間要多。
6.盡量使用前綴來索引
如果索引字段的值很長,最好使用值的前綴來索引。例如,TEXT和BLOG類型的字段,進行全文檢索會很浪費時間。如果只檢索字段的前面的若干個字符,這樣可以提高檢索速度。
7.刪除不再使用或者很少使用的索引
表中的數據被大量更新,或者數據的使用方式被改變后,原有的一些索引可能不再需要。數據庫管理員應當定期找出這些索引,將它們刪除,從而減少索引對更新操作的影響。
8 . 最左前綴匹配原則,非常重要的原則。
mysql會一直向右匹配直到遇到范圍查詢(、、between、like)就停止匹配,比如a 1=”” and=”” b=”2” c=”“ 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整。
9 .=和in可以亂序。
比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優化器會幫你優化成索引可以識別的形式
。
10 . 盡量選擇區分度高的列作為索引。
區分度的公式是count(distinct col)/count(*),表示字段不重復的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就 是0,那可能有人會問,這個比例有什么經驗值嗎?使用場景不同,這個值也很難確定,一般需要join的字段我們都要求是0.1以上,即平均1條掃描10條 記錄
11 .索引列不能參與計算,保持列“干凈”。
比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本 太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’);
12 .盡量的擴展索引,不要新建索引。
比如表中已經有a的索引,現在要加(a,b)的索引,那么只需要修改原來的索引即可
注意:選擇索引的最終目的是為了使查詢的速度變快。上面給出的原則是最基本的準則,但不能拘泥于上面的準則。讀者要在以后的學習和工作中進行不斷的實踐。根據應用的實際情況進行分析和判斷,選擇最合適的索引方式。