1. 程式人生 > >MySQL索引原理與演算法

MySQL索引原理與演算法

B+ 樹索引

B 代表 balance 平衡;

B+ 樹索引  通過鍵值(如 id=3 ) 並不能直接找到具體的行。 它是把 行(資料行 記錄)所在的頁,讀入記憶體,再從記憶體中查詢, 最後得到要找的記錄(資料)。

 

二分查詢法:(折半查詢法)

 

首先  是 有一組  排好順序的 記錄。 如 10, 20,30, 40,50,60,70, 80,90,100

問題是 從這樣的一組排好順序的記錄中 查詢 某一個指定 記錄。?

採取的方法是:1. 首先將 中間位置的記錄 作為比較物件。 2; 要找的元素和 比較物件 比較 ,如果小於比較物件 縮小到左半部分;如果大於比較物件 縮小到右半部分。

 

結論: 平均來說 二分查詢法 比順序查詢法要好,效率高。

 

二叉樹:/(二叉查詢樹)。

 

二叉樹有哪些遍歷方法?

前序遍歷:  先訪問根結點, 再訪問左子樹, 再訪問右子樹;

中序遍歷: 先訪問左子樹,在父節點, 再右子樹;

後序遍歷:先訪問左子樹,再右子樹; 最後是訪問根結點;

層序遍歷: 先訪問根結點, 從上到下逐層遍歷。同一層中從左到右訪問。

 

二叉排序樹 /   二叉查詢樹。 binary sort tree

它 特點1:   左子樹上 所有結點的值 均小於 它的 根結點的值;

2. 右子樹上 所有結點的值,均大於 它的根結點的值;

 

平衡二叉樹  (Self -Balancing Binary  Search Tree)  :   首先是一種二叉排序樹; 其中每一個結點 的左子樹 和 右子樹的 高度差 小於等於 1;

 

維護一個平衡二叉樹 ,比如 插入,更新 和刪除操作。 這些都是通過 左旋 或右旋 來實現的, 這都是開銷。

B+  樹: 是一種 平衡查詢樹。 葉子結點上 從小到大排序順序排序。各個葉子結點 使用指標連線。

示意圖: todo;

 

B+ 樹 插入操作:  3種情況 ; Leaf Page滿;  Index Page滿   操作   表 todo;   有拆頁的可能

 

旋轉發生在Leaf Page已滿。 但是其左右結點沒有滿的情況下,這時 B+樹 不急於拆分 頁的操作,而是將記錄移到頁的 兄弟結點上。

旋轉 使B+ 樹減少了一次 拆分操作。

 

 

B+ 樹的刪除操作。

B+ 樹 使用  填充因子 fill factor  來控制 樹的刪除; /依據填充因子來 決定怎麼刪除; 填充因子 >= 50%

葉子節點 小於填充因子, 中間節點小於 填充因子 , 操作 三種情況  表 todo;  有合併頁的可能。

 

B+ 樹 索引 ; B+ 樹 在資料庫的應用/實現。

B+ 樹 索引 特點 :高 扇出性,B+ 樹 的高度 一般 2-4 層。 查詢一行記錄(ID = xxx, ID 是主鍵) 最多需要2到4次 IO; 假如機械硬碟每秒100次IO, 則查詢一次需要時間 0.02——0.04 秒。

 

B+ 樹 索引 分為 : 聚集索引(clustered index) ;    輔助索引(secondary index) /  非聚集索引(non-clustered index);

葉子節點 存放資料; 聚集索引的 葉子節點 存放時一整行的資料(完整的記錄);

 

聚集索引, 中每個葉子節點 都是一個頁; 葉子節點 之間使用 雙向連結串列來進行連結。

 

可以使用 py_innodb_page_info.py 工具來分析表空間。

使用  hexdump 工具來 檢視資料。

圖: 5-14   todo;

 

注意:儲存方式: 首先頁不是 物理上連續的;通過雙向連結; 再者。頁中的記錄 也是通過雙向連結串列進行維護的。

 

聚集索引 好處 : 對 主鍵的 排序 查詢 ; 和  範圍查詢   查詢速度非常快。

mysql > explain select * from  Profile order by id limit 10;

mysql > explain select * from Profile  where id > and id < 10000\G;

 

輔助索引:

葉子節點 包含 鍵值(索引列欄位值); 還包含 bookmark(主鍵的值)

每張表中可以有多個輔助索引。

例如: 通過輔助索引 怎麼找到一行資料 ?

例如: 在一棵高度 為3的 輔助索引樹 中查詢資料,首先需要對這顆輔助索引遍歷3次 找到 指定主鍵, 如果

指定的聚集索引樹的高度 同樣 為 3, 那麼還需要對  聚集索引樹進行3次 查詢。最終找到一個完整的資料行所在的頁。算下來,一共需要6次邏輯IO得到最終的一個數據頁。

例子分析  圖  5-16  todo;

 

B+ 樹索引的分裂( 拆分頁)

InnoDB儲存引擎 的  Page Header 中有幾個部分來儲存插入的順序資訊。  PAGE_LAST_INSERT   PAGE_DIRECTION   PAGE_N_DIRECTION

增值插入 時 分裂點就是插入記錄本身(如果要分裂的話);其他插入情況 暫時不深究。

 

 

 

索引建立和刪除:

兩種方式:

一:

ALTER  TABLE   tbl_name

ADD  {INDEX| KEY }   [index_name]   [index_type]  (index_col_name ,…)  [index_option] …

 

ALTER TABLE tbl_name

DROP PRIMARY KEY

| DROP  FOREIGN KEY  fk_symbol

| DROP  {INDEX| KEY} index_name

 

二:

CREATE  【UNIQUE  | FULLTEXT | SPATIAL ] INDEX index_name

[index_type]

ON  tbl_name  (index_col_name,…)   [index_option]   [algorithm_option | lock_option]  …

 

DROP INDEX index_name  ON  tbl_name  [algorithm_option | lock_option]  …

 

algorithm_option :

ALGORITHM [=]  {DEFAULT | INPLACE | COPY}

 

lock_option :

LOCK [=]  {DEFAULT |NONE  |SHARED | EXCLUSIVE}

 

檢視索引:

SHOW INDEX FROM  tbl_name;

 

例子: 使用者可以設定整個列的資料進行索引,也可以只索引一個列的開頭部分資料, 如 b  為 varchar(8000) , 使用者可以只索引 前 100個欄位,如:

ALTER TABLE t       ADD  KEY idx_b (b(100));

 

 

SHOW  INDEX  結果 每一列的含義。

 

Collation:  列以什麼方式儲存在索引中, B+ 樹 總是 A

Cardinality:  索引中 唯一值的數目的估計值。;  它不是 實時更新的 ,是個大概的值。

優化器 會 根據 Cardinality 的值來選擇是否使用這個索引。

 

ANALYZE  TABLE  操作 會跟新 Cardinality 的值。

 

對 現有的資料表 (有很多資料) 進行 索引的 建立 或刪除 , 會造成 什麼影響,效率怎麼樣?以前是怎麼做的,現在是怎麼做的?

InnoDB  1.0.x   開始支援  快速索引建立 Fast Index Creation   簡稱: FIC。 針對的是輔助索引。

 

對於輔助索引的建立;  InnoDB儲存引擎 會 對建立索引的表 加上一個 S 鎖。 在建立過程中不需要重新建表。

輔助索引的刪除:  更新內部檢視, 將輔助索引的空間標記為可讀, 同時刪除內部檢視上 對該表的索引定義。

 

主鍵的建立 和刪除 同樣需要重建一張表。

 

線上資料定義  Online DDL

MySQL 5.6版本開始 支援 Online DDL 線上資料定義 操作;  允許輔助索引建立的同時,還可以允許其他 像 INSERT  UPDATE  DELETE  這類DML 操作,

這極大地提高了Mysql 資料庫在 生成環境中的可用性。

還支援的“線上”操作如:

 

輔助索引的建立與 刪除

改變自增長值

新增或刪除外來鍵約束

列的重新命名。

 

CREATE  【UNIQUE  | FULLTEXT | SPATIAL ] INDEX index_name

[index_type]

ON  tbl_name  (index_col_name,…)   [index_option]   [algorithm_option | lock_option]  …

ALGORITHM  指定了 建立 或刪除索引的演算法  可以取值如: COPY  INPLACE  DEFAULT;    預設 採用 DEFAULT  方式。

LOCK      建立或刪除索引  新增鎖的情況 ,可以取值如:

NONE,   //不加鎖,這種模式可以獲得最大的併發度

SHARE,    // S 鎖, 併發的讀可以,遇到寫的事務,寫事務就要等待。

EXCLUSIVE,   //  X 鎖, 對目標 表 加上一個X 鎖。 讀寫事務都不能進行。

DEFAULT ,  //  1, 首先判斷能不能 使用 NONE,  2, 能不能使用 SHARE  , 3 能不能使用  EXCLUSIVE  .

 

Online DDL的原理: 在執行 建立或刪除操作的同時,將 INSERT ,UPDATE,  DELETE,  這類DML操作日誌寫入到一個快取中,等到完成索引建立後,再將重做應用到表上。  這個快取預設大小是 128M  (由引數  innodb_online_alter_log_max_size 引數控制)。

 

在索引的建立過程中,SQL 優化器 不會 選擇 正在建立中的索引。

 

 

什麼樣的情況下,適合加索引? 哪些欄位適合加索引?

 

像 性別, 地區, 型別 欄位 ,他們的取值範圍很小,低選擇性; 所以沒必要加索引

像 姓名 就可以加索引。

Cardinality/ n_rows_in_table 應儘可能接近1。  如果非常小,那麼使用者需要考慮是否有必要加索引。

 

Cardinality 是怎麼統計的?  是怎麼計算的?

統計時通過取樣 來完成的; Cardinality統計更新發生在 INSERT  和 UPDATE 。

策略: 1. 表中 1/16 的資料 已發生過 變化。

2.  stat_modified_counter > 2 000 000 000 .

預設取樣數量 是 8

 

當 執行SQL 語句:

ANALYZE  TABLE;

SHOW  TABLE STATUS;

SHOW  INDEX;

以及 訪問 information_schema  下的表 tables   和  statistics

時, 會導致InnoDB 儲存引擎去重複計算索引 Cardinality 值。

如果表中 資料量 很大,並且表中有多個輔助索引,執行上述操作可能會非常慢。

 

不同應用中B+ 樹索引的應用?

OLTP  應用  一般只從資料庫中取得一小部分資料,一般 10條 ,這種建立 B+樹索引有意義。

OLAP  應用, 都需要訪問大量資料,多是面向分析的查詢。 這個時候通常對時間欄位進行索引。因為大多數統計需要根據時間維度來進行資料的篩選。

 

聯合索引:

 

create table t(

a int,

b int,

primary key (a),

key idx_a_b (a,b)

)engine = innoDB

 

圖  5-22  todo;

select  * from  t  where  a=xxx  and b=xxx   //可以使用到索引;

select  *  from t where  b=xxx;       //使用不到這棵索引;

select *  from  t  where a=xxx  order by b;      //可以使用到聯合索引

 

 

聯合索引的好處是 : 已經對 第二個鍵值進行了 排序處理。例如:

create table buy_log(

userid  int unsigned  not null,

buy_date date

)engine=InnoDB

 

alter  table buy_log add key(userid);

alter  table buy_log  add  key(userid,  buy_date);

 

select *  from  buy_log  where userid=2;

//分析 有兩個索引 可以使用; 最終選擇的是索引 userid;

 

select * from buy_log  where  userid=1  order by  buy_date  desc  limit 3;

//分析 可以用使用 userid, (userid, buy_date) 兩個索引; 最終選擇了聯合索引  userid_2;  因為 聯合索引中buy_date已經排好了, 根據聯合索引取出資料,無須對buy_date做一次額外的排序操作。

 

 

 

對 a,  b,  c新增 聯合 索引(a, b, c);  如下:

select … from  table   where a=xxx  order by  b;   //可以使用索引

select …  from table  where a=xxx  and  b=xxx  order by  c;   //可以使用索引。

 

 

 

覆蓋索引: /  索引覆蓋 (covering index):

從輔助索引中可以得到查詢的話; 就不需要查詢聚集索引中的記錄了。  使用 覆蓋索引的好處 是輔助索引中不包含整行記錄的所有資訊),所以大小要遠小於聚集索引,因此可以減少大量的IO操作。

若 葉子節點 存放的資料 為 (primary key1,  primary key2, …,   key1, key2, ….).  下面語句都可以僅使用 一次輔助聯合索引來完成查詢。

 

select key2  from  table   where   key1=xxx;

select  primary key2, key2  from  table  where  key1=xxx;

select  primary key1, key2  from  table  where  key1=xxx;

select primary key1,  primary key2, key2  from table  where key1=xxx;

 

  1. 對於某些統計 問題 也可以 僅使用 輔助索引。

如  select count(*)  from  buy_log;           //Extra  Using index 代表使 優化器  進行了覆蓋索引操作。

select count(*)  from buy_log  where  buy_date>=’2011-01-01’  and  buy_date<’2011-02-01’;   //

//(a, b) 的這種聯合索引,一般是b  作為查詢條件 是使用不到索引的,但是 如果是統計操作則 優化器 會進行選擇。

 

什麼情況下 使用不到索引?  什麼情況下優化器 不使用索引。

多 發生在 範圍查詢 , join連結 等情況下。

 

select *  from  orderdetails  where  orderid > 10000  and  orderid  < 102000;

如果 要求訪問的資料量很小, 則優化器還是會選擇輔助索引;  如果當訪問的資料佔整個表中資料蠻大一部分(20% 左右),優化器會選擇聚集索引來來查詢資料。因為 順序讀取的速度遠遠快於離散讀。

 

索引提示:index  hint

以下兩種情況 可以用到 index hint

  1. MySQL 資料庫的優化器錯誤地選擇了某個索引。 很少見
  2. 某個SQL語句可以使用的索引很多,這時 查詢優化器執行計劃時間的開銷可能會大於 SQL語句本身。

 

語法:

 

USE index  只是告訴優化器可以選擇索引, 實際上優化器還是根據自己的判斷進行操作。 可以使用 FORCE index 來強制使用索引。

 

Multi-Range  Read 優化 /  MRR 優化;(InnoDB  MyISAM 都支援)

MySQL 5.6 開始支援 MRR 優化;  MRR 適用於  range, ref,  eq_ref 型別 的查詢。

MRR 的工作原理:/方式:

  1. 將 查詢得到的輔助索引 鍵值存放於一個 快取中(  預設 256k), 這時快取中的資料是安裝輔助索引  的 鍵值 進行排序的。
  2. 將快取中的鍵值 根據 rowID(主鍵ID)進行排序。
  3.   根據RowID 的排序順序來訪問實際的資料檔案。

 

之所以稱為 優化,就是因為  避免了 離散讀取。

 

select * from  salaries  where salary > 10000  and  salary < 40000;

開不開 差 10倍。

 

Multi-Range Read 還可以將某些範圍查詢 ,拆分為鍵值 對, 來進行批量查詢。如:

select * from t  where  key_part1 >=1000  and  key_part1 < 2000   and  key_part2 = 10000;

//優化器 會將 查詢條件 拆分為(1000, 1000), (1001, 1000), (1002, 1000)…, (1999, 1000);

 

總是開啟MRR:

mysql >  set @@optimizer_switch=‘mrr=on, mrr_cost_based=off’;

 

//檢視快取的大小

mysql > select @@read_rnd_buffer_size\G;

 

Index Condition Pushdown  ICP  優化;

msyql5.6 開始支援:  開啟 ICP 後, 會在取出索引的同時,判斷是否可以進行where 條件的過濾。

ICP 優化支援 range ,ref,  eq_ref,  ref_or_null  型別的查詢。

 

如: 某表有聯合 索引

 

開啟 ICP  後 執行時間的對比 表5-5  todo;

 

雜湊表:

一般來說都將關鍵字轉換為自然樹,然後通過除法散列表。  h(k)  = k  mod  m

例如: innodb_buffer_pool_size  的大小 為 10M ,則共有 640個  16KB的頁。 對 雜湊表來說 需要 640 X 2 = 1280個槽, 但不是質數,應該 是 1399;

 

在InnoDB 儲存引擎 的  緩衝池中 對於其中的 頁 是怎麼進行查詢的呢?

關鍵字  K=  space_id<<20  +  space_id  + offset;

 

自適應雜湊:

hash 索引只能用來搜尋等值的查詢。範圍查詢是不能使用雜湊索引的

select * from table where index_col=‘xxx’;

 

全文索引 【暫時不深入研究】

 

參考書: MySQL技術內幕: