MySQL中Cardinality值的介紹


1)         什么是Cardinality

不是所有的查詢條件出現的列都需要添加索引。對於什么時候添加B+樹索引。一般的經驗是,在訪問表中很少一部分時使用B+樹索引才有意義。對於性別字段、地區字段、類型字段,他們可取值范圍很小,稱為低選擇性。如

SELECT * FROM student WHERE sex='M'

按性別進行查詢時,可取值一般只有M、F。因此SQL語句得到的結果可能是該表50%的數據(加入男女比例1:1)這時添加B+樹索引是完全沒有必要的。相反,如果某個字段的取值范圍很廣,幾乎沒有重復,屬於高選擇性。則此時使用B+樹的索引是最合適的。例如對於姓名字段,基本上在一個應用中不允許重名的出現

怎樣查看索引是否有高選擇性?通過SHOW INDEX結果中的列Cardinality來觀察。非常關鍵,表示所以中不重復記錄的預估值,需要注意的是Cardinality是一個預估值,而不是一個准確值基本上用戶也不可能得到一個准確的值,在實際應用中,Cardinality/n_row_in_table應盡可能的接近1,如果非常小,那用戶需要考慮是否還有必要創建這個索引。故在訪問高選擇性屬性的字段並從表中取出很少一部分數據時,對於字段添加B+樹索引是非常有必要的。如

SELECT * FROM member WHERE usernick='David';

表member大約有500W行數據,usernick字段上有一個唯一索引。這是如果查找用戶名為David的用戶,將得到如下執行計划

可以看到使用了usernick這個索引。這也符合之前提到的高可選擇性,即SQL語句取表中較少行的原則

2)         InnoDB存儲引擎的Cardinality統計

建立索引的前提是高選擇性。這對數據庫來說才具有實際意義,那么數據庫是怎樣統計Cardinality的信息呢?因為MySQL數據庫中有各種不同的存儲引擎,而每種存儲引擎對於B+樹索引的實現又各不相同。所以對Cardinality統計時放在存儲引擎層進行的

在生成環境中,索引的更新操作可能非常頻繁。如果每次索引在發生操作時就對其進行Cardinality統計,那么將會對數據庫帶來很大的負擔。另外需要考慮的是,如果一張表的數據非常大,如一張表有50G的數據,那么統計一次Cardinality信息所需要的時間可能非常長。這樣的環境下,是不能接受的。因此,數據庫對於Cardinality信息的統計都是通過采樣的方法完成

在InnoDB存儲引擎中,Cardinality統計信息的更新發生在兩個操作中:insert和update。InnoDB存儲引擎內部對更新Cardinality信息的策略為:

表中1/16的數據已發生了改變

stat_modified_counter>2000 000 000

 

第一種策略為自從上次統計Cardinality信息后,表中的1/16的數據已經發生過變化,這是需要更新Cardinality信息

第二種情況考慮的是,如果對表中某一行數據頻繁地進行更新操作,這時表中的數據實際並沒有增加,實際發生變化的還是這一行數據,則第一種更新策略就無法適用這種情況,故在InnoDB存儲引擎內部有一個計數器start_modified_counter,用來表示發生變化的次數,當start_modified_counter>2 000 000 000 時,則同樣更新Cardinality信息

 

接着考慮InnoDB存儲引擎內部是怎樣進行Cardinality信息統計和更新操作呢?同樣是通過采樣的方法。默認的InnoDB存儲引擎對8個葉子節點Leaf Page進行采用。采用過程如下

取得B+樹索引中葉子節點的數量,記為A

隨機取得B+樹索引中的8個葉子節點,統計每個頁不同記錄的個數,即為P1,P2....P8

通過采樣信息給出Cardinality的預估值:Cardinality=(P1+P2+...+P8)*A/8

根據上述的說明可以發現,在InnoDB存儲引擎中,Cardinality值通過對8個葉子節點預估而得的。而不是一個實際精確的值。再者,每次對Cardinality值的統計,都是通過隨機取8個葉子節點得到的,這同時有暗示了另外一個Cardinality現象,即每次得到的Cardinality值可能不同的,如

SHOW INDEX FROM OrderDetails

上述SQL語句會觸發MySQL數據庫對於Cardinality值的統計,第一次運行得到的結果如圖5-20

 

在上述測試過程中,並沒有通過INSERT、UPDATE這類的操作來改變OrderDetails中的內容,但是當第二次運行SHOW INDEX FROM OrderDetails語句是,發生了變化,如圖5-21

可以看到,當第二次運行SHOW INDEX FROM OrderDetails語句時,表OrderDetails索引中的Cardinality值發生了變化,雖然表OrderDetails本身並沒有發生任何變化,但是由於Cardinality是隨機取8個葉子節點進行分析,所以即使表沒有發生變化,用戶觀察到索引Cardinality值還是會發生變化,這本身不是Bug,而是隨機采樣而導致的結果

當然,有一種情況可以使得用戶每次觀察到的索引Cardinality值是一樣的。那就是表足夠小,表的葉子節點樹小於或者等於8個。這時即使隨機采樣,也總是會采取倒這些頁,因此每次得到的Cardinality值是相同的

在InnoDB1.2版本之前,可以通過innodb_stats_sample_pages用來設置統計Cardinality時每次采樣頁的數量,默認為8.同時,參數innodb_stats_method用來判斷如何對待索引中出現NULL值記錄。該參數默認值為nulls_equal,表示將NULL值記錄為相等的記錄。其有效值還nulls_unequal,nulls_ignored,分別表示將NULL值記錄視為不同的記錄和忽略NULL值記錄。例如某夜中索引記錄為NULL、NULL、1、2、2、3、3、3,在參數innodb_stats_method默認設置下,該頁的Cardinality為4;若參數innodb_stats_method為nulls_unequal,則該頁的Cardinality為5,若參數innodb_stats_method為nulls_ignored,則Cardinality值為3

當執行ANALYZE TABLE、SHOW TABLE STATUS、SHOW INDEX 以及訪問INFORMATION_SCHEMA架構下的表TABLES和STATISTICS時會導致InnoDB存儲引擎會重新計算索引Cardinality值,若表中的數據量非常大,並且表中存在多個輔助索引時,執行上述操作可能會非常慢,雖然用戶可能並不希望去更新Cardinality值

InnoDB1.2版本提供了更多參數對Cardinality進行設置。如表


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM