1. 程式人生 > 其它 >MySql面試高頻

MySql面試高頻

面試高頻

  • MySQL引擎
  • InnoDB底層原理
  • 索引
  • 索引優化
  • 一些題目

一、MySQL引擎

資料庫引擎儲存引擎是資料庫底層軟體組織,資料庫管理系統(DBMS)使用資料引擎進行建立、查詢、更新和刪除資料。不同的儲存引擎提供不同的儲存機制、索引技巧、鎖定水平等功能,使用不同的儲存引擎,還可以 獲得特定的功能。現在許多不同的資料庫管理系統都支援多種不同的資料引擎。MySQL的核心就是儲存引擎。

1、MyISAM

使用這個儲存引擎,每個MyISAM在磁碟上儲存成三個檔案。

(1)frm檔案:儲存表的定義資料

(2)MYD檔案:存放表具體記錄的資料

(3)MYI檔案:儲存索引

frm和MYI可以存放在不同的目錄下。MYI檔案用來儲存索引,但僅儲存記錄所在頁的指標,索引的結構是B+樹結構。下面這張圖就是MYI檔案儲存的機制:

從這張圖可以發現,這個儲存引擎通過MYI的B+樹結構來查詢記錄頁,再根據記錄頁查詢記錄。並且支援全文索引、B樹索引和資料壓縮。

支援資料的型別也有三種:

(1)靜態固定長度表

這種方式的優點在於儲存速度非常快,容易發生快取,而且表發生損壞後也容易修復。缺點是佔空間。這也是預設的儲存格式。

(2)動態可變長表

優點是節省空間,但是一旦出錯恢復起來比較麻煩。

(3)壓縮表

上面說到支援資料壓縮,說明肯定也支援這個格式。在資料檔案發生錯誤時候,可以使用check table工具來檢查,而且還可以使用repair table工具來恢復。

有一個重要的特點那就是不支援事務,但是這也意味著他的儲存速度更快,如果你的讀寫操作允許有錯誤資料的話,只是追求速度,可以選擇這個儲存引擎。

2、InnoDB

InnoDB是預設的資料庫儲存引擎,他的主要特點有:

(1)可以通過自動增長列,方法是auto_increment。

(2)支援事務。預設的事務隔離級別為可重複度,通過MVCC(併發版本控制)來實現的。

(3)使用的鎖粒度為行級鎖,可以支援更高的併發;

(4)支援外來鍵約束;外來鍵約束其實降低了表的查詢速度,但是增加了表之間的耦合度。

(5)配合一些熱備工具可以支援線上熱備份;

(6)在InnoDB中存在著緩衝管理,通過緩衝池,將索引和資料全部快取起來,加快查詢的速度;

(7)對於InnoDB型別的表,其資料的物理組織形式是聚簇表。所有的資料按照主鍵來組織。資料和索引放在一塊,都位於B+數的葉子節點上;

當然InnoDB的儲存表和索引也有下面兩種形式:

(1)使用共享表空間儲存:所有的表和索引存放在同一個表空間中。

(2)使用多表空間儲存:表結構放在frm檔案,資料和索引放在IBD檔案中。分割槽表的話,每個分割槽對應單獨的IBD檔案,分割槽表的定義可以檢視我的其他文章。使用分割槽表的好處在於提升查詢效率。

對於InnoDB來說,最大的特點在於支援事務。但是這是以損失效率來換取的。

3、Memory

將資料存在記憶體,為了提高資料的訪問速度,每一個表實際上和一個磁碟檔案關聯。檔案是frm。

(1)支援的資料型別有限制,比如:不支援TEXT和BLOB型別,對於字串型別的資料,只支援固定長度的行,VARCHAR會被自動儲存為CHAR型別;

(2)支援的鎖粒度為表級鎖。所以,在訪問量比較大時,表級鎖會成為MEMORY儲存引擎的瓶頸;

(3)由於資料是存放在記憶體中,一旦伺服器出現故障,資料都會丟失;

(4)查詢的時候,如果有用到臨時表,而且臨時表中有BLOB,TEXT型別的欄位,那麼這個臨時表就會轉化為MyISAM型別的表,效能會急劇降低;

(5)預設使用hash索引。

(6)如果一個內部表很大,會轉化為磁碟表。

在這裡只是給出3個常見的儲存引擎。使用哪一種引擎需要靈活選擇,一個數據庫中多個表可以使用不同引擎以滿足各種效能和實際需求,使用合適的儲存引擎,將會提高整個資料庫的效能

二、InnoDB底層原理

nnoDB是目前使用最廣的MySQL儲存引擎,MySQL從5.5版本開始InnoDB就已經是預設的儲存引擎了。那你知道為什麼InnoDB被廣泛的使用呢?先把這個問題放一放,我們先來了解一下InnoDB儲存引擎的底層原理。

InnoDB的記憶體架構主要分為三大塊,緩衝池(Buffer Pool)、重做緩衝池(Redo Log Buffer)和額外記憶體池

1.緩衝池

InnoDB為了做資料的持久化,會將資料儲存到磁碟上。但是面對大量的請求時,CPU的處理速度和磁碟的IO速度之間差距太大,為了提高整體的效率, InnoDB引入了緩衝池

當有請求來查詢資料時,如果快取池中沒有,就會去磁碟中查詢,將匹配到的資料放入快取池中。同樣的,如果有請求來修改資料,MySQL並不會直接去修改磁碟,而是會修改已經在緩衝池的頁中的資料,然後再將資料刷回磁碟,這就是緩衝池的作用,加速讀,加速寫,減少與磁碟的IO互動。

緩衝池說白了就是把磁碟中的資料丟到記憶體,那既然是記憶體就會存在沒有記憶體空間可以分配的情況。所以緩衝池採用了LRU演算法,在緩衝池中沒有空閒的頁時,來進行頁的淘汰。但是採用這種演算法會帶來一個問題叫做緩衝池汙染

當你在進行批量掃描甚至全表掃描時,可能會將緩衝池中的熱點頁全部替換出去。這樣以來可能會導致MySQL的效能斷崖式下降。所以InnoDB對LRU做了一些優化,規避了這個問題。

MySQL採用日誌先行,在真正寫資料之前,會首先記錄一個日誌,叫Redo Log,會定期的使用CheckPoint技術將新的Redo Log刷入磁碟,這個後面會講。

除了資料之外,裡面還儲存了索引頁、Undo頁、插入緩衝、自適應雜湊索引、InnoDB鎖資訊和資料字典。下面選幾個比較重要的來簡單聊一聊。

2.插入緩衝

插入緩衝針對的操作是更新或者插入,我們考慮最壞的情況,那就是需要更新的資料都不在緩衝池中。那麼此時會有下面兩種方案。

  1. 來一條資料就直接寫入磁碟
  2. 等資料達到某個閾值(例如50條)才批量的寫入磁碟

很明顯,第二種方案要好一點,減少了與磁碟IO的互動。

2.1兩次寫

鑑於都聊到了插入緩衝,我就不得不需要提一嘴兩次寫,因為我認為這兩個InnoDB的特性是相輔相成的。

插入緩衝提高了MySQL的效能,而兩次寫則在此基礎上提高了資料的可靠性。我們知道,當資料還在緩衝池中的時候,當機器宕機了,發生了寫失效,有Redo Log來進行恢復。但是如果是在從緩衝池中將資料刷回磁碟的時候宕機了呢?

這種情況叫做部分寫失效,此時重做日誌就無法解決問題。

圖片來源於網路, 侵刪

在刷髒頁時,並不是直接刷入磁碟,而是copy到記憶體中的Doublewrite Buffer中,然後再拷貝至磁碟共享表空間(你可以就理解為磁碟)中,每次寫入1M,等copy完成後,再將Doublewrite Buffer中的頁寫入磁碟檔案。

有了兩次寫機制,即使在刷髒頁時宕機了,在例項恢復的時候也可以從共享表空間中找到Doublewrite Buffer的頁副本,直接將其覆蓋原來的資料頁即可。

3.自適應雜湊索引

自適應索引就跟JVM在執行過程中,會動態的把某些熱點程式碼編譯成Machine Code一樣,InnoDB會監控對所有索引的查詢,對熱點訪問的頁建立雜湊索引,以此來提升訪問速度。

你可能多次看到了一個關鍵字,接下來那我們就來聊一下頁是什麼?

3.1頁

,是InnoDB中資料管理的最小單位。當我們查詢資料時,其是以頁為單位,將磁碟中的資料載入到緩衝池中的。同理,更新資料也是以頁為單位,將我們對資料的修改刷回磁碟。每頁的預設大小為16k,每頁中包含了若干行的資料,頁的結構如下圖所示。

圖片來源於網路, 侵刪

不用太糾結每個區是幹嘛的,我們只需要知道這樣設計的好處在哪兒。每一頁的資料,可以通過FileHeader中的上一下和下一頁的資料,頁與頁之間可以形成雙向連結串列。因為在實際的物理儲存上,資料並不是連續儲存的。你可以把他理解成G1的Region在記憶體中的分佈。

而一頁中所包含的行資料,行與行之間則形成了單向連結串列。我們存入的行資料最終會到User Records中,當然最初User Records並不佔據任何儲存空間。隨著我們存入的資料越來越多,User Records會越來越大,Free Space的空間會越來越小,直到被佔用完,就會申請新的資料頁。

User Records中的資料,是按照主鍵id來進行排序的,當我們按照主鍵來進行查詢時,會沿著這個單向連結串列一直往後找,

4.重做日誌緩衝

上面聊過,InnoDB中緩衝池中的頁資料更新會先於磁碟資料更新的,InnoDB也會採用日誌先行(Write Ahead Log)策略來重新整理資料,什麼意思呢?當事務開始時,會先記錄Redo Log到Redo Log Buffer中,然後再更新緩衝池頁資料。

Redo Log Buffer中的資料會按照一定的頻率寫到重做日誌中去。被更改過的頁就會被標記成髒頁,InnoDB會根據CheckPoint機制來將髒頁刷到磁碟。

4.1日誌

上面提到了Redo log,這一小節就專門來講一講日誌,日誌分為如下兩個維度。

MySQL層面

InnoDB層面

4.2MySQL日誌

MySQL的日誌可以分為錯誤日誌、二進位制檔案、查詢日誌和滿查詢日誌。

  • 錯誤日誌 很好理解,就是服務執行過程中發生的嚴重錯誤日誌。當我們的資料庫無法啟動時,就可以來這裡看看具體不能啟動的原因是什麼
  • 二進位制檔案 它有另外一個名字你應該熟悉,叫Binlog,其記錄了對資料庫所有的更改。
  • 查詢日誌 記錄了來自客戶端的所有語句
  • 慢查詢日誌 這裡記錄了所有響應時間超過閾值的SQL語句,這個閾值我們可以自己設定,引數為long_query_time,其預設值為10s,且預設是關閉的狀態,需要手動的開啟。
4.3InnoDB日誌

InnoDB日誌就只有兩種,Redo Log和Undo Log,

  • Redo Log 重做日誌,用於記錄事務操作的變化,且記錄的是修改之後的值。不管事務是否提交都會記錄下來。例如在更新資料時,會先將更新的記錄寫到Redo Log中,再更新快取中頁中的資料。然後按照設定的更新策略,將記憶體中的資料刷回磁碟。
  • Undo Log 記錄的是記錄的事務開始之前的一個版本,可用於事務失敗之後發生的回滾。

Redo Log記錄的是具體某個資料頁上的修改,只能在當前Server使用,而Binlog可以理解為可以給其他型別的儲存引擎使用。這也是Binlog的一個重要作用,那就是主從複製,另外一個作用是資料恢復

上面提到過,Binlog中記錄了所有對資料庫的修改,其記錄日誌有三種格式。分別是Statement、Row和MixedLevel。

  • Statement 記錄所有會修改資料的SQL,其只會記錄SQL,並不需要記錄下這個SQL影響的所有行,減少了日誌量,提高了效能。但是由於只是記錄執行語句,不能保證在Slave節點上能夠正確執行,所以還需要額外的記錄一些上下文資訊
  • Row 只儲存被修改的記錄,與Statement只記錄執行SQL來比較,Row會產生大量的日誌。但是Row不用記錄上下文資訊了,只需要關注被改成啥樣就行。
  • MixedLevel 就是Statement和Row混合使用。

具體使用哪種日誌,需要根據實際情況來決定。例如一條UPDATE語句更新了很多的資料,採用Statement會更加節省空間,但是相對的,Row會更加的可靠。

5.InnoDB和MyISAM的區別

由於MyISAM並不常用,我也不打算去深究其底層的一些原理和實現。我們在這裡簡單的對比一下這兩個儲存引擎的區別就好。我們分點來一點點描述。

  • 事務 InnoDB支援事務、回滾、事務安全和奔潰恢復。而MyISAM不支援,但查詢的速度要比InnoDB更快
  • 主鍵 InnoDB規定,如果沒有設定主鍵,就自動的生成一個6位元組的主鍵,而MyISAM允許沒有任何索引和主鍵的存在,索引就是行的地址
  • 外來鍵 InnoDB支援外來鍵,而MyISAM不支援
  • 表鎖 InnoDB支援行鎖表鎖,而MyISAM只支援表鎖
  • 全文索引 InnoDB不支援全文索引,但是可以用外掛來實現相應的功能,而MyISAM是本身就支援全本索引
  • 行數 InnoDB獲取行數時,需要掃全表。而MyISAM儲存了當前表的總行數,直接讀取即可。

所以,簡單總結一下,MyISAM只適用於查詢大於更新的場景,如果你的系統查詢的情況佔絕大多數(例如報表系統)就可以使用MyISAM來儲存,除此之外,都建議使用InnoDB。

三、索引

一、MySQL中索引的語法

建立索引

在建立表的時候新增索引

CREATE TABLE mytable(
ID INT NOT NULL,
username VARCHAR(16) NOT NULL,
INDEX [indexName] (username(length))
);
在建立表以後新增索引

ALTER TABLE my_table ADD [UNIQUE] INDEX index_name(column_name);
或者
CREATE INDEX index_name ON my_table(column_name);
注意:

1、索引需要佔用磁碟空間,因此在建立索引時要考慮到磁碟空間是否足夠

2、建立索引時需要對錶加鎖,因此實際操作中需要在業務空閒期間進行

根據索引查詢

具體查詢:
SELECT * FROM table_name WHERE column_1=column_2;(為column_1建立了索引)

或者模糊查詢
SELECT * FROM table_name WHERE column_1 LIKE '%三'
SELECT * FROM table_name WHERE column_1 LIKE '三%'
SELECT * FROM table_name WHERE column_1 LIKE '%三%'

SELECT * FROM table_name WHERE column_1 LIKE ''

如果要表示在字串中既有A又有B,那麼查詢語句為:
SELECT * FROM table_name WHERE column_1 LIKE '%A%' AND column_1 LIKE '%B%';

SELECT * FROM table_name WHERE column_1 LIKE '[張李王]三'; //表示column_1中有匹配張三、李三、王三的都可以
SELECT * FROM table_name WHERE column_1 LIKE '[^張李王]三'; //表示column_1中有匹配除了張三、李三、王三的其他三都可以

//在模糊查詢中,%表示任意0個或多個字元;_表示任意單個字元(有且僅有),通常用來限制字串長度;[]表示其中的某一個字元;[^]表示除了其中的字元的所有字元

或者在全文索引中模糊查詢
SELECT * FROM table_name WHERE MATCH(content) AGAINST('word1','word2',...);
刪除索引

DROP INDEX my_index ON tablename;
或者
ALTER TABLE table_name DROP INDEX index_name;
查看錶中的索引

SHOW INDEX FROM tablename
檢視查詢語句使用索引的情況

//explain 加查詢語句
explain SELECT * FROM table_name WHERE column_1='123';

二、索引的優缺點

優勢:可以快速檢索,減少I/O次數,加快檢索速度;根據索引分組和排序,可以加快分組和排序;

劣勢:索引本身也是表,因此會佔用儲存空間,一般來說,索引表佔用的空間的資料表的1.5倍;索引表的維護和建立需要時間成本,這個成本隨著資料量增大而增大;構建索引會降低資料表的修改操作(刪除,新增,修改)的效率,因為在修改資料表的同時還需要修改索引表;

三、索引的分類

常見的索引型別有:主鍵索引、唯一索引、普通索引、全文索引、組合索引

1、主鍵索引:即主索引,根據主鍵pk_clolum(length)建立索引,不允許重複,不允許空值;

ALTER TABLE 'table_name' ADD PRIMARY KEY pk_index('col');
2、唯一索引:用來建立索引的列的值必須是唯一的,允許空值

ALTER TABLE 'table_name' ADD UNIQUE index_name('col');
3、普通索引:用表中的普通列構建的索引,沒有任何限制

ALTER TABLE 'table_name' ADD INDEX index_name('col');
4、全文索引:用大文字物件的列構建的索引(下一部分會講解)

ALTER TABLE 'table_name' ADD FULLTEXT INDEX ft_index('col');
5、組合索引:用多個列組合構建的索引,這多個列中的值不允許有空值

ALTER TABLE 'table_name' ADD INDEX index_name('col1','col2','col3');
*遵循“最左字首”原則,把最常用作為檢索或排序的列放在最左,依次遞減,組合索引相當於建立了col1,col1col2,col1col2col3三個索引,而col2或者col3是不能使用索引的。

*在使用組合索引的時候可能因為列名長度過長而導致索引的key太大,導致效率降低,在允許的情況下,可以只取col1和col2的前幾個字元作為索引

ALTER TABLE 'table_name' ADD INDEX index_name(col1(4),col2(3));
表示使用col1的前4個字元和col2的前3個字元作為索引

四、索引的實現原理

MySQL支援諸多儲存引擎,而各種儲存引擎對索引的支援也各不相同,因此MySQL資料庫支援多種索引型別,如BTree索引,B+Tree索引,雜湊索引,全文索引等等,

1、雜湊索引:
只有memory(記憶體)儲存引擎支援雜湊索引,雜湊索引用索引列的值計算該值的hashCode,然後在hashCode相應的位置存執該值所在行資料的物理位置,因為使用雜湊演算法,因此訪問速度非常快,但是一個值只能對應一個hashCode,而且是雜湊的分佈方式,因此雜湊索引不支援範圍查詢和排序的功能。

2、全文索引:
FULLTEXT(全文)索引,僅可用於MyISAM和InnoDB,針對較大的資料,生成全文索引非常的消耗時間和空間。對於文字的大物件,或者較大的CHAR型別的資料,如果使用普通索引,那麼匹配文字前幾個字元還是可行的,但是想要匹配文字中間的幾個單詞,那麼就要使用LIKE %word%來匹配,這樣需要很長的時間來處理,響應時間會大大增加,這種情況,就可使用時FULLTEXT索引了,在生成FULLTEXT索引時,會為文字生成一份單詞的清單,在索引時及根據這個單詞的清單來索引。FULLTEXT可以在建立表的時候建立,也可以在需要的時候用ALTER或者CREATE INDEX來新增:

//建立表的時候新增FULLTEXT索引
CTREATE TABLE my_table(
id INT(10) PRIMARY KEY,
name VARCHAR(10) NOT NULL,
my_text TEXT,
FULLTEXT(my_text)
)ENGINE=MyISAM DEFAULT CHARSET=utf8;
//建立表以後,在需要的時候新增FULLTEXT索引
ALTER TABLE my_table ADD FULLTEXT INDEX ft_index(column_name);
全文索引的查詢也有自己特殊的語法,而不能使用LIKE %查詢字串%的模糊查詢語法

SELECT * FROM table_name MATCH(ft_index) AGAINST('查詢字串');
注意:

*對於較大的資料集,把資料新增到一個沒有FULLTEXT索引的表,然後新增FULLTEXT索引的速度比把資料新增到一個已經有FULLTEXT索引的錶快。

*5.6版本前的MySQL自帶的全文索引只能用於MyISAM儲存引擎,如果是其它資料引擎,那麼全文索引不會生效。5.6版本之後InnoDB儲存引擎開始支援全文索引

*在MySQL中,全文索引支隊英文有用,目前對中文還不支援。5.7版本之後通過使用ngram外掛開始支援中文。

*在MySQL中,如果檢索的字串太短則無法檢索得到預期的結果,檢索的字串長度至少為4位元組,此外,如果檢索的字元包括停止詞,那麼停止詞會被忽略。

  • 更深入的理解參考文章:全文索引的深入理解

3、BTree索引和B+Tree索引

BTree索引
BTree是平衡搜尋多叉樹,設樹的度為2d(d>1),高度為h,那麼BTree要滿足以一下條件:

每個葉子結點的高度一樣,等於h;
每個非葉子結點由n-1個key和n個指標point組成,其中d<=n<=2d,key和point相互間隔,結點兩端一定是key;
葉子結點指標都為null;
非葉子結點的key都是[key,data]二元組,其中key表示作為索引的鍵,data為鍵值所在行的資料;
BTree的結構如下:

在BTree的機構下,就可以使用二分查詢的查詢方式,查詢複雜度為h*log(n),一般來說樹的高度是很小的,一般為3左右,因此BTree是一個非常高效的查詢結構。

BTree的查詢、插入、刪除過程可以參考:https://blog.csdn.net/endlu/article/details/51720299

B+Tree索引
B+Tree是BTree的一個變種,設d為樹的度數,h為樹的高度,B+Tree和BTree的不同主要在於:

B+Tree中的非葉子結點不儲存資料,只儲存鍵值;
B+Tree的葉子結點沒有指標,所有鍵值都會出現在葉子結點上,且key儲存的鍵值對應data資料的實體地址;
B+Tree的每個非葉子節點由n個鍵值key和n個指標point組成;
B+Tree的結構如下:

B+Tree對比BTree的優點:

1、磁碟讀寫代價更低

一般來說B+Tree比BTree更適合實現外存的索引結構,因為儲存引擎的設計專家巧妙的利用了外存(磁碟)的儲存結構,即磁碟的最小儲存單位是扇區(sector),而作業系統的塊(block)通常是整數倍的sector,作業系統以頁(page)為單位管理記憶體,一頁(page)通常預設為4K,資料庫的頁通常設定為作業系統頁的整數倍,因此索引結構的節點被設計為一個頁的大小,然後利用外存的“預讀取”原則,每次讀取的時候,把整個節點的資料讀取到記憶體中,然後在記憶體中查詢,已知記憶體的讀取速度是外存讀取I/O速度的幾百倍,那麼提升查詢速度的關鍵就在於儘可能少的磁碟I/O,那麼可以知道,每個節點中的key個數越多,那麼樹的高度越小,需要I/O的次數越少,因此一般來說B+Tree比BTree更快,因為B+Tree的非葉節點中不儲存data,就可以儲存更多的key。

2、查詢速度更穩定

由於B+Tree非葉子節點不儲存資料(data),因此所有的資料都要查詢至葉子節點,而葉子節點的高度都是相同的,因此所有資料的查詢速度都是一樣的。

更多作業系統內容參考:

硬碟結構

扇區、塊、簇、頁的區別

作業系統層優化(進階,初學不用看)

帶順序索引的B+TREE
很多儲存引擎在B+Tree的基礎上進行了優化,添加了指向相鄰葉節點的指標,形成了帶有順序訪問指標的B+Tree,這樣做是為了提高區間查詢的效率,只要找到第一個值那麼就可以順序的查詢後面的值。

B+Tree的結構如下:

聚簇索引和非聚簇索引
分析了MySQL的索引結構的實現原理,然後我們來看看具體的儲存引擎怎麼實現索引結構的,MySQL中最常見的兩種儲存引擎分別是MyISAM和InnoDB,分別實現了非聚簇索引和聚簇索引。

聚簇索引的解釋是:聚簇索引的順序就是資料的物理儲存順序

非聚簇索引的解釋是:索引順序與資料物理排列順序無關

(這樣說起來並不好理解,讓人摸不著頭腦,清繼續看下文,並在插圖下方對上述兩句話有解釋)

首先要介紹幾個概念,在索引的分類中,我們可以按照索引的鍵是否為主鍵來分為“主索引”和“輔助索引”,使用主鍵鍵值建立的索引稱為“主索引”,其它的稱為“輔助索引”。因此主索引只能有一個,輔助索引可以有很多個。

MyISAM——非聚簇索引

MyISAM儲存引擎採用的是非聚簇索引,非聚簇索引的主索引和輔助索引幾乎是一樣的,只是主索引不允許重複,不允許空值,他們的葉子結點的key都儲存指向鍵值對應的資料的實體地址。
非聚簇索引的資料表和索引表是分開儲存的。
非聚簇索引中的資料是根據資料的插入順序儲存。因此非聚簇索引更適合單個數據的查詢。插入順序不受鍵值影響。
只有在MyISAM中才能使用FULLTEXT索引。(mysql5.6以後innoDB也支援全文索引)
*最開始我一直不懂既然非聚簇索引的主索引和輔助索引指向相同的內容,為什麼還要輔助索引這個東西呢,後來才明白索引不就是用來查詢的嗎,用在那些地方呢,不就是WHERE和ORDER BY 語句後面嗎,那麼如果查詢的條件不是主鍵怎麼辦呢,這個時候就需要輔助索引了。

InnoDB——聚簇索引

聚簇索引的主索引的葉子結點儲存的是鍵值對應的資料本身,輔助索引的葉子結點儲存的是鍵值對應的資料的主鍵鍵值。因此主鍵的值長度越小越好,型別越簡單越好。
聚簇索引的資料和主鍵索引儲存在一起。
聚簇索引的資料是根據主鍵的順序儲存。因此適合按主鍵索引的區間查詢,可以有更少的磁碟I/O,加快查詢速度。但是也是因為這個原因,聚簇索引的插入順序最好按照主鍵單調的順序插入,否則會頻繁的引起頁分裂,嚴重影響效能。
在InnoDB中,如果只需要查詢索引的列,就儘量不要加入其它的列,這樣會提高查詢效率。

*使用主索引的時候,更適合使用聚簇索引,因為聚簇索引只需要查詢一次,而非聚簇索引在查到資料的地址後,還要進行一次I/O查詢資料。

*因為聚簇輔助索引儲存的是主鍵的鍵值,因此可以在資料行移動或者頁分裂的時候降低成本,因為這時不用維護輔助索引。但是由於主索引儲存的是資料本身,因此聚簇索引會佔用更多的空間。

*聚簇索引在插入新資料的時候比非聚簇索引慢很多,因為插入新資料時需要檢測主鍵是否重複,這需要遍歷主索引的所有葉節點,而非聚簇索引的葉節點儲存的是資料地址,佔用空間少,因此分佈集中,查詢的時候I/O更少,但聚簇索引的主索引中儲存的是資料本身,資料佔用空間大,分佈範圍更大,可能佔用好多的扇區,因此需要更多次I/O才能遍歷完畢。

下圖可以形象的說明聚簇索引和非聚簇索引的區別

從上圖中可以看到聚簇索引的輔助索引的葉子節點的data儲存的是主鍵的值,主索引的葉子節點的data儲存的是資料本身,也就是說資料和索引儲存在一起,並且索引查詢到的地方就是資料(data)本身,那麼索引的順序和資料本身的順序就是相同的;

而非聚簇索引的主索引和輔助索引的葉子節點的data都是儲存的資料的實體地址,也就是說索引和資料並不是儲存在一起的,資料的順序和索引的順序並沒有任何關係,也就是索引順序與資料物理排列順序無關。

MyISAM和innoDB引擎對比
MyISAM	innoDB

索引型別 非聚簇 聚簇
支援事務 是 否
支援表鎖 是 是
支援行鎖 否 是(預設)
支援外來鍵 否 是
支援全文索引 是 是(5.6以後支援)
適用操作型別 大量select下使用 大量insert、delete和update下使用
總結如下:

InnoDB 支援事務,支援行級別鎖定,支援 B-tree、Full-text 等索引,不支援 Hash 索引;
MyISAM 不支援事務,支援表級別鎖定,支援 B-tree、Full-text 等索引,不支援 Hash 索引;
此外,Memory 不支援事務,支援表級別鎖定,支援 B-tree、Hash 等索引,不支援 Full-text 索引;

四、索引優化

1,建立索引
對於查詢佔主要的應用來說,索引顯得尤為重要。很多時候效能問題很簡單的就是因為我們忘了新增索引而造成的,或者說沒有新增更為有效的索引導致。如果不加索引的話,那麼查詢任何哪怕只是一條特定的資料都會進行一次全表掃描,如果一張表的資料量很大而符合條件的結果又很少,那麼不加索引會引起致命的效能下降。但是也不是什麼情況都非得建索引不可,比如性別可能就只有兩個值,建索引不僅沒什麼優勢,還會影響到更新速度,這被稱為過度索引。
2,複合索引
比如有一條語句是這樣的:select * from users where area=’beijing’ and age=22;
如果我們是在area和age上分別建立單個索引的話,由於mysql查詢每次只能使用一個索引,所以雖然這樣已經相對不做索引時全表掃描提高了很多效率,但是如果在area、age兩列上建立複合索引的話將帶來更高的效率。如果我們建立了(area, age, salary)的複合索引,那麼其實相當於建立了(area,age,salary)、(area,age)、(area)三個索引,這被稱為最佳左字首特性。因此我們在建立複合索引時應該將最常用作限制條件的列放在最左邊,依次遞減。
3,索引不會包含有NULL值的列
只要列中包含有NULL值都將不會被包含在索引中,複合索引中只要有一列含有NULL值,那麼這一列對於此複合索引就是無效的。所以我們在資料庫設計時不要讓欄位的預設值為NULL。
4,使用短索引
對串列進行索引,如果可能應該指定一個字首長度。例如,如果有一個CHAR(255)的 列,如果在前10 個或20 個字元內,多數值是惟一的,那麼就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁碟空間和I/O操作。
5,排序的索引問題
mysql查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。因此資料庫預設排序可以符合要求的情況下不要使用排序操作;儘量不要包含多個列的排序,如果需要最好給這些列建立複合索引。
6,like語句操作
一般情況下不鼓勵使用like操作,如果非使用不可,如何使用也是一個問題。like “%aaa%” 不會使用索引而like “aaa%”可以使用索引。
7,不要在列上進行運算
select * from users where YEAR(adddate)<2007;
將在每個行上進行運算,這將導致索引失效而進行全表掃描,因此我們可以改成
select * from users where adddate<‘2007-01-01’;
8,不使用NOT IN和操作
NOT IN和操作都不會使用索引將進行全表掃描。NOT IN可以NOT EXISTS代替,id3則可使用id>3 or id<3來代替。

五、一些題目

1.什麼是索引
索引指資料庫的目錄,比如:字典上面的字母目錄 (適用於大資料量)

2.建立索引的優缺點
優點:查詢速度快
缺點:增刪改慢,因為資料庫要同步去維護索引檔案,所以速度慢

3.索引有哪些
普通 主鍵 唯一 組合

  1. 索引檢索為什麼快
    索引結構:B+Tree

5.一般你們會在什麼情況下加索引
(1)主鍵自動建立唯一索引
(2)頻繁作為查詢條件的欄位應該建立索引
(3)查詢中與其他表關聯的欄位,外來鍵關係建立索引
(4)單鍵/組合索引的選擇問題,組合索引的價效比更高
(5)查詢中排序的欄位,排序欄位若通過索引去訪問將大大提高排序速度
(6)查詢中統計或者分組欄位。
(7)過濾條件好的欄位選擇一段選擇加索引

6.怎麼知道索引用沒用上
通過explain查詢sql執行計劃,主要看key使用的是哪個索引

7.用過組合索引嗎,是有序的嗎
用過, 有序

8.什麼情況下會使索引失效?
(1)like
(2)like “%123%,前面不能+%
(3)使用 關鍵字 in ,or ,null,!=

  1. sql優化您們是怎麼做的?
    一.首先開啟資料庫慢查詢日誌,定位到查詢效率比較低的sql , 找出對應的sql語句並進行分析
    1.表設計是否規範,是否符合三正規化的標準
    (1)第一正規化:保證原子性(不可拆分)
    (2)第二正規化:每張表都有主鍵
    (3)第三正規化(每一列都有主鍵相關)
    2.檢視資料表中是否存在大量的冗餘欄位,欄位資料型別是否合理
    3.儘可能的使用varchar代替char 建表資料型別,能用數值的絕對不用字元儲存
    4.儘量避免null值,使用預設值替代空值,數值型可以使用0,字元型可以使用空字串

二.檢視sql語句是否規範
(1)避免使用關鍵字:or ,in,not in ,!=,<>,避免使用select *
(2)儘量避免子查詢,大部分子查詢都可以連線查詢
(3)用到or的地方可以使用union去代替實現
(4)用到in的地方可以使用exists去代替

三.分析sql的索引是否可以用上
(1) explain查詢sql的執行計劃,重點關注的幾個列就是,type是不是全表掃描
(2)看一下索引是否能夠用的上,主要看key使用的是哪個索引
(3)看一下rows掃描行數是不是很大