1. 程式人生 > >Mysql學習-02 sql執行順序與索引分析

Mysql學習-02 sql執行順序與索引分析

1.效能下降SQL慢 執行時間長 等待時間長原因:

  •   查詢資料過多
  •   關聯了太多的表,太多join 
  •   沒有充分利用到索引 -- > 單值,複合 Mysql一般情況下,查詢一張表只會用到其中的一個索引,所以單值索引有時候並不能把sql的查詢條件都納入索引查詢範圍。所以需要適當的使用複合索引。
  •   伺服器調優及各個引數設定(緩衝、執行緒數等)

2.常見通用的Join查詢:

  1. SQL執行順序

       

      優化器會分析不同執行順序產生的效能消耗不同而動態調整執行順序。

     2.Join圖

     

CREATE TABLE `t_dept` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`deptName` VARCHAR ( 30 ) DEFAULT NULL,
`address` VARCHAR ( 40 ) DEFAULT NULL,
PRIMARY KEY ( `id` ) 
) ENGINE = INNODB AUTO_INCREMENT = 7 DEFAULT CHARSET = utf8;

CREATE TABLE `t_emp` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `age` int(3) DEFAULT NULL,
  `deptId` int(11) DEFAULT NULL,
  `empno` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_dept_id` (`deptId`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

-- 1   所有有門派的人員資訊 
-- ( A、B兩表共有)
 select * from t_emp a inner join t_dept b on a.deptId = b.id; 
 
-- 2   列出所有使用者,並顯示其機構資訊 
 -- (A的全集)
 select * from t_emp a left join t_dept b on a.deptId = b.id; 
 
-- 3   列出所有門派 
-- (B的全集)
 select * from  t_dept  b  
 
-- 4   所有不入門派的人員 
-- (A的獨有)
select * from t_emp a left join t_dept b on a.deptId = b.id where b.id is null; 
 
-- 5   所有沒人入的門派 
-- (B的獨有)
 select * from t_dept b left join  t_emp a on a.deptId = b.id where a.deptId is null;  
 
-- 6  列出所有人員和機構的對照關係
-- (AB全有)
-- #MySQL Full Join的實現 因為MySQL不支援FULL JOIN,下面是替代方法
--  #left join + union(可去除重複資料)+ right join
 
SELECT * FROM t_emp A LEFT JOIN t_dept B ON A.deptId = B.id
UNION
SELECT * FROM t_emp A RIGHT JOIN t_dept B ON A.deptId = B.id
 
-- 7 列出所有沒入派的人員和沒人入的門派
-- (A的獨有+B的獨有)
SELECT * FROM t_emp A LEFT JOIN t_dept B ON A.deptId = B.id WHERE B.`id` IS NULL
UNION
SELECT * FROM t_emp A RIGHT JOIN t_dept B ON A.deptId = B.id WHERE A.`deptId` IS NULL;

3.索引簡介:

    3.1 介紹:索引(Index)是幫助MySQL高效獲取資料的資料結構。可以得到索引的本質:索引是資料結構。

    索引的目的在於提高查詢效率,可以類比字典,如果要查“mysql”這個單詞,我們肯定需要定位到m字母,然後從下往下找到y字母,再找到剩下的sql。
     可以簡單理解為“排好序的快速查詢資料結構”。

     在資料之外,資料庫系統還維護著滿足特定查詢演算法的資料結構,這些資料結構以某種方式引用(指向)資料,
這樣就可以在這些資料結構上實現高階查詢演算法。這種資料結構,就是索引。下圖就是一種可能的索引方式示例:

    

      左邊是資料表,一共有兩列七條記錄,最左邊的是資料記錄的實體地址
      為了加快Col2的查詢,可以維護一個右邊所示的二叉查詢樹,每個節點分別包含索引鍵值和一個指向對應資料記錄實體地址的指標,這樣就可以運用二叉查詢在一定的複雜度內獲取到相應資料,從而快速的檢索出符合條件的記錄。
     一般來說索引本身也很大,不可能全部儲存在記憶體中,因此索引往往以索引檔案的形式儲存的磁碟上.

      我們平常所說的索引,如果沒有特別指明,都是指B樹(多路搜尋樹,並不一定是二叉的)結構組織的索引。其中聚集索引,次要索引,覆蓋索引,複合索引,字首索引,唯一索引預設都是使用B+樹索引,統稱索引。當然,除了B+樹這種型別的索引之外,還有哈稀索引(hash index)等。

3.2 優勢
               索引,可以提高資料檢索的效率,降低資料庫的IO成本。
               通過索引列對資料進行排序,降低資料排序的成本,降低了CPU的消耗。

  3.3 劣勢
        雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如對錶進行INSERT、UPDATE和DELETE。 因為更新表時,MySQL不僅要儲存資料,還要儲存一下索引檔案每次更新添加了索引列的欄位, 都會調整因為更新所帶來的鍵值變化後的索引資訊
  實際上索引也是一張表,該表儲存了主鍵與索引欄位,並指向實體表的記錄,所以索引列也是要佔用空間的.

  3.4 mysql索引結構

  • BTree索引

    原理圖:

     

     【初始化介紹】 
           一顆b樹,淺藍色的塊我們稱之為一個磁碟塊,可以看到每個磁碟塊包含幾個資料項(深藍色所示)和指標(黃色所示),如磁碟塊1包含資料項17和35,包含指標P1、P2、P3,P1表示小於17的磁碟塊,P2表示在17和35之間的磁碟塊,P3表示大於35的磁碟塊。真實的資料存在於葉子節點即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非葉子節點只不儲存真實的資料,只儲存指引搜尋方向的資料項,如17、35並不真實存在於資料表中。
    【查詢過程】
            如果要查詢資料項29,那麼首先會把磁碟塊1由磁碟載入到記憶體,此時發生一次IO,在記憶體中用二分查詢確定29在17和35之間,鎖定磁碟塊1的P2指標,記憶體時間因為非常短(相比磁碟的IO)可以忽略不計,通過磁碟塊1的P2指標的磁碟地址把磁碟塊3由磁碟載入到記憶體,發生第二次IO,29在26和30之間,鎖定磁碟塊3的P2指標,通過指標載入磁碟塊8到記憶體,發生第三次IO,同時記憶體中做二分查詢找到29,結束查詢,總計三次IO。 
           真實的情況是,3層的b+樹可以表示上百萬的資料,如果上百萬的資料查詢只需要三次IO,效能提高將是巨大的,如果沒有索引,每個資料項都要發生一次IO,那麼總共需要百萬次的IO,顯然成本非常非常高。

     時間複雜度:

      

       

  • B+Tree索引

       原理圖:

       

      B樹和B+樹的區別:
       1)B-樹的關鍵字和記錄是放在一起的,葉子節點可以看作外部節點,不包含任何資訊;B+樹的非葉子節點中只有關鍵字和指向下一個節點的索引,記錄只放在葉子節點中。
       2)在B-樹中,越靠近根節點的記錄查詢時間越快,只要找到關鍵字即可確定記錄的存在;而B+樹中每個記錄的查詢時間基本是一樣的,都需要從根節點走到葉子節點,而且在葉子節點中還要再比較關鍵字。從這個角度看B-樹的效能好像要比B+樹好,而在實際應用中卻是B+樹的效能要好些。因為B+樹的非葉子節點不存放實際的資料,這樣每個節點可容納的元素個數比B-樹多,樹高比B-樹小,這樣帶來的好處是減少磁碟訪問次數。儘管B+樹找到一個記錄所需的比較次數要比B-樹多,但是一次磁碟訪問的時間相當於成百上千次記憶體比較的時間,因此實際中B+樹的效能可能還會好些,而且B+樹的葉子節點使用指標連線在一起,方便順序遍歷(例如檢視一個目錄下的所有檔案,一個表中的所有記錄等),這也是很多資料庫和檔案系統使用B+樹的緣故。  
思考:為什麼說B+樹比B-樹更適合實際應用中作業系統的檔案索引和資料庫索引? 
     1) B+樹的磁碟讀寫代價更低 
       B+樹的內部結點並沒有指向關鍵字具體資訊的指標。因此其內部結點相對B 樹更小。如果把所有同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入記憶體中的需要查詢的關鍵字也就越多。相對來說IO讀寫次數也就降低了。 
     2) B+樹的查詢效率更加穩定 
    由於非終結點並不是最終指向檔案內容的結點,而只是葉子結點中關鍵字的索引。所以任何關鍵字的查詢必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同,導致每一個數據的查詢效率相當

  •  聚簇索引與非聚簇索引 

         聚簇索引並不是一種單獨的索引型別,而是一種資料儲存方式。術語‘聚簇’表示資料行和相鄰的鍵值進錯的儲存在一起。
   如下圖,左側的索引就是聚簇索引,因為資料行在磁碟的排列和索引排序保持一致。

   

   聚簇索引的好處:
      按照聚簇索引排列順序,查詢顯示一定範圍資料的時候,由於資料都是緊密相連,資料庫不不用從多個數據塊中提取資料,所以節省了大量的io操作。
   聚簇索引的限制:
      對於mysql資料庫目前只有innodb資料引擎支援聚簇索引,而Myisam並不支援聚簇索引。
由於資料物理儲存排序方式只能有一種,所以每個Mysql的表只能有一個聚簇索引。一般情況下就是該表的主鍵。
為了充分利用聚簇索引的聚簇的特性,所以innodb表的主鍵列儘量選用有序的順序id,而不建議用無序的id,比如uuid這種。 

  •  full-text全文索引 

       全文索引(也稱全文檢索)是目前搜尋引擎使用的一種關鍵技術。它能夠利用【分詞技術】等多種演算法智慧分析出文字文字中關鍵詞的頻率和重要性,然後按照一定的演算法規則智慧地篩選出我們想要的搜尋結果。

CREATE TABLE `article` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(200) DEFAULT NULL,
  `content` text,
  PRIMARY KEY (`id`),
  FULLTEXT KEY `title` (`title`,`content`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
不同於like方式的的查詢:
SELECT * FROM article WHERE content LIKE ‘%查詢字串%’;
全文索引用match+against方式查詢:
SELECT * FROM article WHERE MATCH(title,content) AGAINST (‘查詢字串’);
 
明顯的提高查詢效率。
 
限制:
mysql5.6.4以前只有Myisam支援,5.6.4版本以後innodb才支援,但是官方版本不支援中文分詞,
需要第三方分詞外掛。
5.7以後官方支援中文分詞。但是不建議使用 需要分詞建議es
  • Hash索引 

       Hash索引只有Memory, NDB兩種引擎支援,Memory引擎預設支援Hash索引,如果多個hash值相同,出現雜湊碰撞,那麼索引以連結串列方式儲存。 

  3.5 mysql索引分類

  •   單值索引  即一個索引只包含單個列,一個表可以有多個單列索引
隨表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED  AUTO_INCREMENT 
,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id),
  KEY (customer_name)
);
  
單獨建單值索引:
CREATE  INDEX idx_customer_name ON customer(customer_name); 
 
刪除索引:
DROP INDEX idx_customer_name  on customer;
  • 唯一索引 索引列的值必須唯一,但允許有空值
隨表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED  AUTO_INCREMENT 
,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id),
  KEY (customer_name),
  UNIQUE (customer_no)
);
  
單獨建唯一索引:
CREATE UNIQUE INDEX idx_customer_no ON customer(customer_no); 
 
刪除索引:
DROP INDEX idx_customer_no on customer ;
  • 主鍵索引 設定為主鍵後資料庫會自動建立索引,innodb為聚簇索引
隨表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED  
AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id) 
);
   
CREATE TABLE customer2 (id INT(10) UNSIGNED 
  ,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id) 
);
 
 單獨建主鍵索引:
ALTER TABLE customer 
 add PRIMARY KEY customer(customer_no);  
 
刪除建主鍵索引:
ALTER TABLE customer 
 drop PRIMARY KEY ;  
 
修改建主鍵索引:
必須先刪除掉(drop)原索引,再新建(add)索引
  • 複合索引  即一個索引包含多個列
 隨表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED 
 AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
  PRIMARY KEY(id),
  KEY (customer_name),
  UNIQUE (customer_name),
  KEY (customer_no,customer_name)
);
 
單獨建索引:
CREATE  INDEX idx_no_name ON customer(customer_no,customer_name); 
 
刪除索引:
DROP INDEX idx_no_name  on customer ;
  • 基本語法
  1.  建立  ALTER mytable ADD  [UNIQUE ]  INDEX [indexName] ON (columnname(length)) 
  2. 刪除 DROP INDEX [indexName] ON mytable; 
  3. 檢視 SHOW INDEX FROM table_name\G
  4. 使用ALTER命令
有四種方式來新增資料表的索引:
ALTER TABLE tbl_name ADD PRIMARY KEY (column_list): 該語句新增一個主鍵,
這意味著索引值必須是唯一的,且不能為NULL。
 
ALTER TABLE tbl_name ADD UNIQUE index_name (column_list): 這條語句建立索引的
值必須是唯一的(除了NULL外,NULL可能會出現多次)。
 
ALTER TABLE tbl_name ADD INDEX index_name (column_list): 新增普通索引,索引值可出現多次。
 
ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list):該語句指定了索引為 FULLTEXT ,用於全文索引。
 

 3.6 哪些情況需要建立索引

  1.   主鍵自動建立唯一索引
  2.   頻繁作為查詢條件的欄位應該建立索引
  3.   查詢中與其它表關聯的欄位,外來鍵關係建立索引
  4.   單鍵/組合索引的選擇問題,who?(在高併發下傾向建立組合索引)
  5.   查詢中排序的欄位,排序欄位若通過索引去訪問將大大提高排序速度
  6.   查詢中統計或者分組欄位

  3.7 哪些情況不要建立索引

  •   表記錄太少
  •   經常增刪改的表 -->提高了查詢速度,同時卻會降低更新表的速度,如對錶進行INSERT、UPDATE和DELETE。 因為更新表時,MySQL不僅要儲存資料,還要儲存一下索引檔案
  •   Where條件裡用不到的欄位不建立索引