Mysql性能優化二:索引優化

1 索引的類型

UNIQUE唯一索引

不可以出現相同的值,可以有NULL值。

INDEX普通索引

允許出現相同的索引內容。

PRIMARY KEY主鍵索引

不允許出現相同的值,且不能爲NULL值,一個表只能有一個primary_key索引。

fulltext index 全文索引

上述三種索引都是針對列的值發揮作用,但全文索引,可以針對值中的某個單詞,比如一篇文章中的某個詞,然而並沒有什麼卵用,因爲只有myisam以及英文支持,並且效率讓人不敢恭維,但是可以用coreseek和xunsearch等第三方應用來完成這個需求。

2 索引的CURD

索引的創建

ALTER TABLE

適用於表創建完畢之後再添加。

ALTER TABLE 表名 ADD 索引類型 (unique,primary key,fulltext,index)[索引名](字段名)

ALTER TABLE `table_name` ADD INDEX `index_name` (`column_list`) -- 索引名,可要可不要;如果不要,當前的索引名就是該字段名。 
ALTER TABLE `table_name` ADD UNIQUE (`column_list`) 
ALTER TABLE `table_name` ADD PRIMARY KEY (`column_list`) 
ALTER TABLE `table_name` ADD FULLTEXT KEY (`column_list`)

 

CREATE INDEX

CREATE INDEX可對錶增加普通索引或UNIQUE索引。

--例:只能添加這兩種索引 
CREATE INDEX index_name ON table_name (column_list) 
CREATE UNIQUE INDEX index_name ON table_name (column_list)

 

另外,還可以在建表時添加:

CREATE TABLE `test1` ( 
  `id` smallint(5) UNSIGNED AUTO_INCREMENT NOT NULL, -- 注意,下面創建了主鍵索引,這裏就不用創建了 
  `username` varchar(64) NOT NULL COMMENT '用戶名', 
  `nickname` varchar(50) NOT NULL COMMENT '暱稱/姓名', 
  `intro` text, 
  PRIMARY KEY (`id`),  
  UNIQUE KEY `unique1` (`username`), -- 索引名稱,可要可不要,不要就是和列名一樣 
  KEY `index1` (`nickname`), 
  FULLTEXT KEY `intro` (`intro`) 
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='後臺用戶表';

 

索引的刪除

DROP INDEX `index_name` ON `talbe_name`  
ALTER TABLE `table_name` DROP INDEX `index_name` 
-- 這兩句都是等價的,都是刪除掉table_name中的索引index_name; 

ALTER TABLE `table_name` DROP PRIMARY KEY -- 刪除主鍵索引,注意主鍵索引只能用這種方式刪除

 

索引的查看

show index from tablename;

 

索引的更改

更改個毛線,刪掉重建一個既可

 

3 創建索引的技巧

  • 維度高的列創建索引。

  • 數據列中不重複值出現的個數,這個數量越高,維度就越高。

  • 如數據表中存在8行數據a,b ,c,d,a,b,c,d這個表的維度爲4。

  • 要爲維度高的列創建索引,如性別和年齡,那年齡的維度就高於性別。

性別這樣的列不適合創建索引,因爲維度過低。

  • 對 where,on,group by,order by 中出現的列使用索引。

  • 對較小的數據列使用索引,這樣會使索引文件更小,同時內存中也可以裝載更多的索引鍵。

  • 爲較長的字符串使用前綴索引。

  • 不要過多創建索引,除了增加額外的磁盤空間外,對於DML操作的速度影響很大,因爲其每增刪改一次就得從新建立索引。

  • 使用組合索引,可以減少文件索引大小,在使用時速度要優於多個單列索引。

     

4 組合索引與前綴索引

注意,這兩種稱呼是對建立索引技巧的一種稱呼,並非索引的類型。

組合索引

MySQL單列索引和組合索引究竟有何區別呢?

爲了形象地對比兩者,先建一個表:

CREATE TABLE `myIndex` ( 
  `i_testID` INT NOT NULL AUTO_INCREMENT,  
  `vc_Name` VARCHAR(50) NOT NULL,  
  `vc_City` VARCHAR(50) NOT NULL,  
  `i_Age` INT NOT NULL,  
  `i_SchoolID` INT NOT NULL,  
  PRIMARY KEY (`i_testID`)  
);

 

假設表內已有1000條數據,在這 10000 條記錄裏面 7 上 8 下地分佈了 5 條 vc_Name=”erquan” 的記錄,只不過 city,age,school 的組合各不相同。來看這條 T-SQL:

SELECT `i_testID` FROM `myIndex` WHERE `vc_Name`='erquan' AND `vc_City`='鄭州' AND `i_Age`=25; -- 關聯搜索;

 

首先考慮建MySQL單列索引:

在 vc_Name 列上建立了索引。執行 T-SQL 時,MYSQL 很快將目標鎖定在了 vc_Name=erquan 的 5 條記錄上,取出來放到一中間結果集。在這個結果集裏,先排除掉 vc_City 不等於”鄭州”的記錄,再排除 i_Age 不等於 25 的記錄,最後篩選出唯一的符合條件的記錄。雖然在 vc_Name 上建立了索引,查詢時MYSQL不用掃描整張表,效率有所提高,但離我們的要求還有一定的距離。同樣的,在 vc_City 和 i_Age 分別建立的MySQL單列索引的效率相似。

爲了進一步榨取 MySQL 的效率,就要考慮建立組合索引。就是將 vc_Name,vc_City,i_Age 建到一個索引裏:

ALTER TABLE `myIndex` ADD INDEX `name_city_age` (vc_Name(10),vc_City,i_Age);

 

建表時,vc_Name 長度爲 50,這裏爲什麼用 10 呢?這就是下文要說到的前綴索引,因爲一般情況下名字的長度不會超過 10,這樣會加速索引查詢速度,還會減少索引文件的大小,提高 INSERT 的更新速度。

執行 T-SQL 時,MySQL 無須掃描任何記錄就到找到唯一的記錄!

如果分別在 vc_Name,vc_City,i_Age 上建立單列索引,讓該表有 3 個單列索引,查詢時和上述的組合索引效率一樣嗎?答案是大不一樣,遠遠低於我們的組合索引。雖然此時有了三個索引,但 MySQL 只能用到其中的那個它認爲似乎是最有效率的單列索引,另外兩個是用不到的,也就是說還是一個全表掃描的過程。

建立這樣的組合索引,其實是相當於分別建立了:

  • vc_Name,vc_City,i_Age

  • vc_Name,vc_City

  • vc_Name

這樣的三個組合索引!爲什麼沒有 vc_City,i_Age 等這樣的組合索引呢?這是因爲 mysql 組合索引 “最左前綴” 的結果。簡單的理解就是隻從最左面的開始組合。並不是只要包含這三列的查詢都會用到該組合索引,下面的幾個 T-SQL 會用到:

SELECT * FROM myIndex WHREE vc_Name=”erquan” AND vc_City=”鄭州” SELECT * FROM myIndex WHREE vc_Name=”erquan”

 

而下面幾個則不會用到:

SELECT * FROM myIndex WHREE i_Age=20 AND vc_City=”鄭州” SELECT * FROM myIndex WHREE vc_City=”鄭州”

 

也就是,name_city_age(vc_Name(10),vc_City,i_Age) 從左到右進行索引,如果沒有左前索引Mysql不執行索引查詢。

前綴索引

如果索引列長度過長,這種列索引時將會產生很大的索引文件,不便於操作,可以使用前綴索引方式進行索引前綴索引應該控制在一個合適的點,控制在0.31黃金值即可(大於這個值就可以創建)。

SELECT COUNT(DISTINCT(LEFT(`title`,10)))/COUNT(*) FROM Arctic; — 這個值大於0.31就可以創建前綴索引,Distinct去重複 ALTER TABLE `user` ADD INDEX `uname`(title(10)); — 增加前綴索引SQL,將人名的索引建立在10,這樣可以減少索引文件大小,加快索引查詢速度。

 

5 什麼樣的sql不走索引

要儘量避免這些不走索引的sql

SELECT `sname` FROM `stu` WHERE `age`+10=30;-- 不會使用索引,因爲所有索引列參與了計算 

SELECT `sname` FROM `stu` WHERE LEFT(`date`,4) <1990; -- 不會使用索引,因爲使用了函數運算,原理與上面相同 

SELECT * FROM `houdunwang` WHERE `uname` LIKE'後盾%' -- 走索引 

SELECT * FROM `houdunwang` WHERE `uname` LIKE "%後盾%" -- 不走索引 

-- 正則表達式不使用索引,這應該很好理解,所以爲什麼在SQL中很難看到regexp關鍵字的原因 

-- 字符串與數字比較不使用索引; 
CREATE TABLE `a` (`a` char(10)); 
EXPLAIN SELECT * FROM `a` WHERE `a`="1" -- 走索引 
EXPLAIN SELECT * FROM `a` WHERE `a`=1 -- 不走索引 

select * from dept where dname='xxx' or loc='xx' or deptno=45 --如果條件中有or,即使其中有條件帶索引也不會使用。換言之,就是要求使用的所有字段,都必須建立索引,我們建議大家儘量避免使用or 關鍵字 

-- 如果mysql估計使用全表掃描要比使用索引快,則不使用索引

 

多表關聯時的索引效率

SELECT `sname` FROM `stu` WHERE LEFT(`date`,4) <1990; — 不會使用索引,因爲使用了函數運算,原理與上面相同
SELECT * FROM `houdunwang` WHERE `uname` LIKE’後盾%’ — 走索引
SELECT * FROM `houdunwang` WHERE `uname` LIKE “%後盾%” — 不走索引

 

 

從上圖可以看出,所有表的type爲all,表示全表索引。也就是6 6 6,共遍歷查詢了216次。

除第一張表示全表索引(必須的,要以此關聯其他表),其餘的爲range(索引區間獲得),也就是6+1+1+1,共遍歷查詢9次即可。

所以我們建議在多表join的時候儘量少join幾張表,因爲一不小心就是一個笛卡爾乘積的恐怖掃描,另外,我們還建議儘量使用left join,以少關聯多。因爲使用join 的話,第一張表是必須的全掃描的,以少關聯多就可以減少這個掃描次數。

6 索引的弊端

不要盲目的創建索引,只爲查詢操作頻繁的列創建索引,創建索引會使查詢操作變得更加快速,但是會降低增加、刪除、更新操作的速度,因爲執行這些操作的同時會對索引文件進行重新排序或更新。

但是,在互聯網應用中,查詢的語句遠遠大於DML的語句,甚至可以佔到80%~90%,所以也不要太在意,只是在大數據導入時,可以先刪除索引,再批量插入數據,最後再添加索引。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章