1. 程式人生 > 其它 >Effective C++ 筆記 —— Item 2: Prefer consts, enums, and inlines to #defines

Effective C++ 筆記 —— Item 2: Prefer consts, enums, and inlines to #defines

索引

建立索引的目的是減小查詢時間,提高查詢效率。減少磁碟的讀取次數

1. 索引的含義

​ 索引是一個排序的列表,在這個列表中儲存著索引的值和包含這個值的資料所在行的實體地址,在資料十分龐大的時候,索引可以大大加快查詢的速度,這是因為使用索引後可以不用掃描全表來定位某行的資料,而是先通過索引表找到該行資料對應的實體地址然後訪問相應的資料。

2.索引的原理

​ 索引一般以檔案的形式儲存在磁碟中,索引的儲存原理是以空間換取時間。未新增索引時查詢是全域性掃描,有多少資料就要掃描多少條資料。新增索引後,會將建立的索引的key新增到B-Tree上;B樹的特點就是適合在磁碟等直接儲存裝置上組織動態查詢表,每次以索引進行條件查詢時,會去樹上根據key值直接進行搜尋。

3.索引資料結構 :

二叉樹、 紅黑樹、 Hash表、 B-Tree。

不同的儲存引擎索引的資料結構不同

4.與索引資料結構相關的計算機原理

兩種型別的儲存:計算機系統一般包含兩種型別的儲存,計算機主存(RAM)和外部儲存器(如硬碟、CD、SSD等)。主存的讀取速度快,記憶體小。相對於主存,外部磁碟的資料讀取速率要比主從慢好幾個數量級,記憶體大。實際資料庫中資料都是儲存到外部儲存器的。

主存儲存原理:當系統需要讀取主存時,則將地址訊號放到地址總線上傳給主存,主存讀到地址訊號後,解析訊號並定位到指定儲存單元,然後將此儲存單元資料放到資料匯流排上,供其它部件讀取。寫主存的過程類似,系統將要寫入單元地址和資料分別放在地址匯流排和資料匯流排上,主存讀取兩個匯流排的內容,做相應的寫操作。

磁碟儲存原理:索引一般以檔案形式儲存在磁碟上,索引檢索需要磁碟I/O操作。與主存不同,磁碟I/O存在機械運動耗費,因此磁碟I/O的時間消耗是巨大的。磁碟讀取資料靠的是機械運動,當需要從磁碟讀取資料時,系統會將資料邏輯地址傳給磁碟,磁碟的控制電路按照定址邏輯將邏輯地址翻譯成實體地址,即確定要讀的資料在哪個磁軌,哪個扇區。為了讀取這個扇區的資料,需要將磁頭放到這個扇區上方,為了實現這一點,磁頭需要移動對準相應磁軌,這個過程叫做尋道,所耗費時間叫做尋道時間,然後磁碟旋轉將目標扇區旋轉到磁頭下,這個過程耗費的時間叫做旋轉時間,最後便是對讀取資料的傳輸。 所以每次讀取資料花費的時間可以分為尋道時間、旋轉延遲、傳輸時間三個部分。

區域性性原理和磁碟預讀:磁碟的存取本身比主存慢,加上磁碟的機械運動消耗使得磁碟的存取更慢。所以為了提高查詢速度就要減少減少磁碟I/O。為了達到這個目的,磁碟往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個位元組,磁碟也會從這個位置開始,順序向後讀取一定長度的資料放入記憶體。這樣做的理論依據是電腦科學中著名的區域性性原理:當一個數據被用到時,其附近的資料也通常會馬上被使用。程式執行期間所需要的資料通常比較集中。

​ 由於磁碟順序讀取的效率很高(不需要尋道時間,只需很少的旋轉時間),因此對於具有區域性性的程式來說,預讀可以提高I/O效率。預讀的長度一般為頁(page)的整倍數。頁是計算機管理儲存器的邏輯塊,硬體及作業系統往往將主存和磁碟儲存區分割為連續的大小相等的塊,每個儲存塊稱為一頁(在許多作業系統中,頁得大小通常為4k),主存和磁碟以頁為單位交換資料。當程式要讀取的資料不在主存中時,會觸發一個缺頁異常,此時系統會向磁碟發出讀盤訊號,磁碟會找到資料的起始位置並向後連續讀取一頁或幾頁載入記憶體中,然後異常返回,程式繼續執行。

5.MySQL的儲存引擎

MySQl預設的儲存引擎是InnoDB: InnoDB 底層索引的資料結構為B+Tree;(B+Tree是B-Tree的變種)

6.B-Tree和B+Tree,Hash的區別

B-Tree

​ 每個節點都儲存key和data,所有節點組成這棵樹,並且葉子節點指標為null。

B+Tree

	只有葉子節點儲存data,葉子節點包含了這棵樹的所有鍵值,葉子節點儲存一個指向相鄰葉子節點的指標。
Hash

Hash 索引結構的特殊性,其檢索效率非常高,索引的檢索可以一次定位,不像B-Tree 索引需要從根節點到枝節點,最後才能訪問到頁節點這樣多次的IO訪問,所以 Hash 索引的查詢效率要遠高於 B-Tree 索引。

(1)Hash 索引僅僅能滿足"=","IN"和"<=>"查詢,不能使用範圍查詢。

由於 Hash 索引比較的是進行 Hash 運算之後的 Hash 值,所以它只能用於等值的過濾,不能用於基於範圍的過濾,因為經過相應的 Hash 演算法處理之後的 Hash 值的大小關係,並不能保證和Hash運算前完全一樣。

(2)Hash 索引無法被用來避免資料的排序操作。

由於 Hash 索引中存放的是經過 Hash 計算之後的 Hash 值,而且Hash值的大小關係並不一定和 Hash 運算前的鍵值完全一樣,所以資料庫無法利用索引的資料來避免任何排序運算;

(3)Hash 索引不能利用部分索引鍵查詢。

對於組合索引,Hash 索引在計算 Hash 值的時候是組合索引鍵合併後再一起計算 Hash 值,而不是單獨計算 Hash 值,所以通過組合索引的前面一個或幾個索引鍵進行查詢的時候,Hash 索引也無法被利用。

(4)Hash 索引在任何時候都不能避免表掃描。

前面已經知道,Hash 索引是將索引鍵通過 Hash 運算之後,將 Hash運算結果的 Hash 值和所對應的行指標資訊存放於一個 Hash 表中,由於不同索引鍵存在相同 Hash 值,所以即使取滿足某個 Hash 鍵值的資料的記錄條數,也無法從 Hash 索引中直接完成查詢,還是要通過訪問表中的實際資料進行相應的比較,並得到相應的結果。

(5)Hash 索引遇到大量Hash值相等的情況後效能並不一定就會比B-Tree索引高。

對於選擇性比較低的索引鍵,如果建立 Hash 索引,那麼將會存在大量記錄指標資訊存於同一個 Hash 值相關聯。這樣要定位某一條記錄時就會非常麻煩,會浪費多次表資料的訪問,而造成整體效能低下

7.explain

explain模擬執行SQL語句,對SQL語句進行分析並輸出詳細資訊,便於開發人員進行優化操作。

explain的詳細資訊:

  • id:執行順序(先執行id值大的,相同id值先執行前面的)

  • select_type:查詢的型別

    • SIMPLE, 表示此查詢不包含 UNION 查詢或子查詢
    • PRIMARY, 表示此查詢是最外層的查詢
    • UNION, 表示此查詢是 UNION 的第二或隨後的查詢
    • DEPENDENT UNION, UNION 中的第二個或後面的查詢語句, 取決於外面的查詢
    • UNION RESULT, UNION 的結果
    • SUBQUERY, 子查詢中的第一個 SELECT
    • DEPENDENT SUBQUERY: 子查詢中的第一個 SELECT, 取決於外面的查詢. 即子查詢依賴於外層查詢的結果.
  • table:查詢的表名

  • partitions:查詢進行匹配分割槽

  • type:判斷查詢是否高效的重要依據

    • system: 表中只有一條資料. 這個型別是特殊的 const 型別.
    • const: 針對主鍵或唯一索引的等值查詢掃描, 最多隻返回一行資料. const 查詢速度非常快, 因為它僅僅讀取一次即可.
    • eq_ref: 此型別通常出現在多表的 join 查詢, 表示對於前表的每一個結果, 都只能匹配到後表的一行結果. 並且查詢的比較操作通常是 =, 查詢效率較高.
    • ref: 此型別通常出現在多表的 join 查詢, 針對於非唯一或非主鍵索引, 或者是使用了 最左字首 規則索引的查詢.
    • range: 表示使用索引範圍查詢, 通過索引欄位範圍獲取表中部分資料記錄. 這個型別通常出現在 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN() 操作中.
    • index: 表示全索引掃描(full index scan), 和 ALL 型別類似, 只不過 ALL 型別是全表掃描, 而 index 型別則僅僅掃描所有的索引, 而不掃描資料.
    • ALL: 表示全表掃描, 這個型別的查詢是效能最差的查詢之一. 通常來說, 我們的查詢不應該出現 ALL 型別的查詢, 因為這樣的查詢在資料量大的情況下, 對資料庫的效能是巨大的災難. 如一個查詢是 ALL 型別查詢, 那麼一般來說可以對相應的欄位新增索引來避免.
  • possible_keys:能夠使用到的索引

  • key:實際用到的索引

  • key_len:檢視索引是否全部被使用(值越大越好)

  • rows:掃描的資料行數(值越小越好)

  • Extra:額外資訊

    • Using filesort:當 Extra 中有 Using filesort 時, 表示 MySQL 需額外的排序操作, 不能通過索引順序達到排序效果. 一般有 Using filesort, 都建議優化去掉, 因為這樣的查詢 CPU 資源消耗大.

    • Using index
      "覆蓋索引掃描", 表示查詢在索引樹中就可查詢所需資料, 不用掃描表資料檔案, 往往說明效能不錯

    • Using temporary
      查詢有使用臨時表, 一般出現於排序, 分組和多表 join 的情況, 查詢效率不高, 建議優化.

8.索引的型別

  • 普通索引:單列索引是最基本的索引,它沒有任何限制。
  • 組合索引:組合索引是在多個欄位上建立的索引。組合索引遵守“最左字首”原則即在查詢條件中使用了組合索引的第一個欄位,索引才會被使用。
  • 唯一索引:唯一索引和普通索引類似,主要的區別在於,唯一索引限制列的值必須唯一,但允許存在空值(只允許存在一條空值)。
  • 主鍵索引:主鍵索引是一種特殊的唯一索引,一個表只能有一個主鍵,不允許有空值。
  • 全文索引:全文索引主要用來查詢文字中的關鍵字,而不是直接與索引中的值相比較。fulltext索引跟其它索引大不相同,它更像是一個搜尋引擎,而不是簡單的where語句的引數匹配。fulltext索引配合match against操作使用,而不是一般的where語句加like。目前只有char、varchar,text 列上可以建立全文索引。

建立索引的語句

CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name [USING index_type]

on tbl_nam(index_col_name,……)

刪除索引

DROP INDEX index_name ON tbl_name

9.設定索引的規則

1.最適合索引的列是出現在where子句中的列,或連線子句中指定的列,而不是出現在select關鍵字後的選擇列表中的列。
2.索引的列的基數越大,索引的效果越好。
3.儘量使用短索引。能夠節省大量索引空間,也可能使查詢更快。
4.不要過度索引。索引都要佔用額外的磁碟空間,並降低寫操作的效能。在修改表的內容時,索引必須進行更新,有時可能需要重構。

10.注意事項

1.索引不會包含有null值的列

只要列中包含有null值都將不會被包含在索引中,複合索引中只要有一列含有null值,那麼這一列對於此複合索引就是無效的。所以我們在資料庫設計時不要讓欄位的預設值為null。

2.使用短索引

對串列進行索引,如果可能應該指定一個字首長度。例如,如果有一個char(255)的列,如果在前10個或20個字元內,多數值是惟一的,那麼就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁碟空間和I/O操作。

3.索引列排序

查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。因此資料庫預設排序可以符合要求的情況下不要使用排序操作;儘量不要包含多個列的排序,如果需要最好給這些列建立複合索引。

4.like語句操作

一般情況下不推薦使用like操作,如果非使用不可,如何使用也是一個問題。like “%aaa%” 不會使用索引而like “aaa%”可以使用索引。

5.不要在列上進行運算

這將導致索引失效而進行全表掃描

6.不使用not in和<>操作

11.索引的優缺點

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

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