天天看點

春華秋實之MySQL進階-02 索引2 索引

春華秋實之MySQL進階-02 索引2 索引

2 索引

2.1 安裝linux下的mysql

  1. 去官網下載下傳mysql
春華秋實之MySQL進階-02 索引2 索引
  1. 解壓并安裝
  • 壓縮檔案通過xftp解壓并且使用rpm安裝依賴
[root@hadoop soft]#tar -xvf mysql-8.0.26-1.el7.x86_64.rpm-bundle.tar -C /home/hadoop/app/
[root@hadoop soft]cd ..
[root@hadoop hadoop]#cd app
[root@hadoop app]# rpm -ivh mysql-community-common-8.0.26-1.el7.x86_64.rpm
[root@hadoop app]# yum remove mysql-libs
[root@hadoop app]# rpm -ivh mysql-community-client-plugins-8.0.26-1.el7.x86_64.rpm 
[root@hadoop app]# rpm -ivh mysql-community-libs-8.0.26-1.el7.x86_64.rpm
[root@hadoop app]# rpm -ivh mysql-community-libs-compat-8.0.26-1.el7.x86_64.rpm
[root@hadoop app]# yum install openssl-devel
[root@hadoop app]# rpm -ivh  mysql-community-devel-8.0.26-1.el7.x86_64.rpm
           
  • 安裝用戶端
[root@hadoop app]#rpm -ivh mysql-community-client-8.0.26-1.el7.x86_64.rpm
           
  • 安裝服務端
[root@hadoop app]#rpm -ivh  mysql-community-server-8.0.26-1.el7.x86_64.rpm
           
  1. 啟動mysql服務
[root@hadoop app]# systemctl start mysqld
           
  1. 去日志找下随機生成的mysql的root密碼
[root@hadoop app]# vim /var/log/mysqld.log
/password
           
  1. 登入mysql
[root@hadoop app]# mysql -u root -p
Enter password: 
           
嫌麻煩練習的時候直接跳過密碼算了:(88條消息) Linux系統安裝MySQL報錯“ Access denied for user ‘root‘@‘localhost‘ (using password: YES)“_二木成林的部落格-CSDN部落格
  1. 把密碼等級調低并且更改密碼
mysql>set global validate_password_policy = LOW;
mysql>set global validate_password.length = 8;
           
mysql>ALTER  USER  'root'@'localhost'  IDENTIFIED BY 'xxxxxxxx';
           
  1. 建立供遠端通路的一個使用者
mysql>drop user 'root'@'%';
mysql>flush privileges;
mysql>create user 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'xxxxxxxx';
表示在所有的主機上都能通路
           
  1. 給建立可以遠端通路的root使用者配置設定權限
mysql>grant all on *.* to 'root'@'%';
           
  1. 用Datagrip連接配接看看
  • 如果連不上看下linux的防火牆有沒有開放3306的端口,或者直接關閉防火牆看看
春華秋實之MySQL進階-02 索引2 索引
春華秋實之MySQL進階-02 索引2 索引

2.2 索引概述

  1. 含義:是幫助MySQL高效擷取資料的資料結構(有序)。在資料結構上實作進階查找算法引用(指向)資料。
  2. 比較
  • 無索引,全表掃描,直至結束。性能極低。
  • 有索引
    • 假如是二叉查找樹:如果select * from user from age = 45,那麼45會跟36比較,走右邊,然後再跟48比較,走左邊,然後就找到45了,隻需要比對3次。效率高。
      春華秋實之MySQL進階-02 索引2 索引
  1. 優缺點
  • 優點:
    • 高效的擷取資料,降低資料庫擷取資料的磁盤IO成本。
    • 通過索引對資料進行排序,降低資料排序成本,降低CPU的消耗。
  • 缺點:
    • 索引也是要占空間滴
    • 提高查詢效率,但是降低表的更新效率,因為也要維護索引

2.3 索引結構

  1. 含義:索引是在存儲引擎層實作的,不同的存儲引擎有不同的索引結構。
  2. 分類
春華秋實之MySQL進階-02 索引2 索引
春華秋實之MySQL進階-02 索引2 索引
  1. 二叉樹
  • 左邊節點小于自身,右側節點大于自身
  • 順序插入,形成連結清單,性能降低。針對大量資料,層次深,檢索慢。
春華秋實之MySQL進階-02 索引2 索引
  • 解決辦法:左旋右旋形成紅黑樹(平衡二叉查找樹)
    • 但是還是因為隻有兩個節點導緻的在大量資料會形成較深的層次的,是以檢索慢的問題
春華秋實之MySQL進階-02 索引2 索引
  1. B-Tree(多路平衡查找樹)
  • 概念:以一顆最大度數(max-degree)為5(5階)的b-tree為例,度數就是節點的子節點個數,那就表示5階的b樹每個節點最多存儲4個key,5個指針
  • 看圖了解下:
    • 5階表示下面有有5個節點,圖中根節點就是5個指針灰色的那邊,第一個灰色的指針表示小于20這個key,第二個灰色的指針表示在20這個key和30這個key之間,依次共5個,然後每個指針對應下面每個節點。是以得出結論,如果有n個key就有n+1個指針。
春華秋實之MySQL進階-02 索引2 索引
  • 來個案例
    春華秋實之MySQL進階-02 索引2 索引
春華秋實之MySQL進階-02 索引2 索引
春華秋實之MySQL進階-02 索引2 索引
春華秋實之MySQL進階-02 索引2 索引
春華秋實之MySQL進階-02 索引2 索引
  1. B+tree
  • 概念:以一顆最大度數(max-degree)為4(4階)的B+Tree為例
  • 看圖了解
    • 與BTree的差別是,所有的資料存放都會出現在葉子的節點,并且形成了單項連結清單,上面的非葉子節點起到的作用是索引的作用
  • 也來個例子
    春華秋實之MySQL進階-02 索引2 索引
    春華秋實之MySQL進階-02 索引2 索引
春華秋實之MySQL進階-02 索引2 索引
春華秋實之MySQL進階-02 索引2 索引
春華秋實之MySQL進階-02 索引2 索引
春華秋實之MySQL進階-02 索引2 索引
  1. MySQL的b+tree索引
  • 概念:在原來的B+Tree上進行了優化,增加了一個指向相鄰葉子節點的連結清單指針,就形成了帶有順序的B+Tree,提高區間通路的性能。簡單說就是把單項連結清單變成了雙向連結清單
  • 看圖了解:
    • 每個節點存儲在頁當中,回憶下前面的InnoDB的邏輯存儲結構,表空間、段、區、頁、行,一頁預設16K,底層就是這麼存儲的。
      春華秋實之MySQL進階-02 索引2 索引
  1. Hash索引
  • 概念:哈希索引就是采用一定的hash算法,将鍵值換算成新的hash值,映射到對應的槽位上,存儲在hash表中。
  • 看圖了解
    • 背景:id是主鍵,要為name創造一個哈希索引的資料結構
    • 第一步:先計算出每一行資料的哈希值
    • 第二步:name字段的所有值根據哈希函數去計算每一個name值應該放在哈希表的哪個槽位上。例如金庸算出來是槽位值005,對應的槽位就會存儲金庸這個key,以及金庸這一行對應哈希值58dda,也就是第一步對應的值,怎麼了解呢,就了解為58dda是一個引用,指向了金庸所處的那一行的位址
      春華秋實之MySQL進階-02 索引2 索引
    • 楊逍和金庸算出來的槽位一樣,就叫做哈希沖突,跟java中一樣用連結清單解決就可以
春華秋實之MySQL進階-02 索引2 索引
  • 特點:
    • 隻能用于等值
    • 無法排序,因為運算結果無序
    • 但是查詢效率高,通常一次檢索比對就可以了,效率通常高于B+Tree索引
  • 支援哈希索引的引擎
    • Memory引擎,但是InnoDB中具有的自适應hash功能,就是說有時候可以将B+Tree索引在指定條件下自動建構成哈希索引
  1. 問:為啥InnoDB存儲引擎選擇B+Tree索引結構?
  • 相對于二叉樹,層級更少,搜尋效率高
  • B-tree,所有節點都會存儲資料,導緻存key和指針的存儲的少
  • 相對于Hash索引,支援範圍比對和排序操作

2.4 索引分類

  1. 分類
春華秋實之MySQL進階-02 索引2 索引
  • 細節:PK不能為空,unique可以為空值,PK和unique在限制時候自動建立了索引
  1. 根據存儲形式的分類
    春華秋實之MySQL進階-02 索引2 索引
  • 規則
    • 如果存在主鍵,主鍵索引就是聚集索引
    • 沒有主鍵,将使用第一個唯一索引作為聚集索引
    • 啥都沒有咋辦,會生成一個rowid所謂隐藏的聚集索引
  • 來張圖了解下
    • 聚集索引葉子挂着資料,二級索引下面挂着聚集索引字段
    • 查name = 'Arm'的時候,走的是二級索引,字元是按照字典序序走的索引,找到id後,再到上面的聚集索引走一遍,定位10,再把整個資料傳回出來,專業資料叫做回表查詢
春華秋實之MySQL進階-02 索引2 索引
  1. 來道題
select * from user where id = 10;
select * from user where name = 'Arm';
備注:id為主鍵,name字段建立的時候有索引
問:哪個sql語句執行效率較高?
           

答案:

  • 第一句隻要去聚集索引中走一遍,然後擷取的資料就好啦
  • 第二句去二級索引中走一遍,然後再拿着id去聚集索引中再去擷取*的資料,即回表查詢,掃描了兩個字段的索引,效率就不高
  1. 再來道題

問題:InnoDB主鍵索引的B+Tree高度為多高?

前提:假設一行資料為1k,一個指針占用6個位元組,主鍵為bigint,占用8個位元組。

解答:(n表示key的數量)

  • n*8+(n+1)6=161024 非葉的節點 n算出來1170個key,指針就有1171個
  • 1171*16 = 18736 指針數乘最大的葉子下面最大的行數,高度為2
  • 18736 *1171 = 21939856 高度為3 如下圖
春華秋實之MySQL進階-02 索引2 索引

2.5 索引的操作文法

  1. 建立索引
create [unique|fulltext] index index_name on table_name(index_col_name,...)
           
  • unique表示唯一索引,fulltext表示全文索引
  • 一個索引可以關聯多個字段的,一個字段叫單列索引,多個字段叫做聯合索引
  1. 檢視索引
show index from table_name;
           
  1. 删除索引
drop index index_name on table_name;
           
  1. 練習
  • name字段為姓名字段,該字段的值可能會重複,為該字段建立索引
    • 看下現在有什麼索引先
    • mysql> show index from tb_user\G;
      *************************** 1. row ***************************
              Table: tb_user
         Non_unique: 0
           Key_name: PRIMARY
       Seq_in_index: 1
        Column_name: id
          Collation: A
        Cardinality: 24
           Sub_part: NULL
             Packed: NULL
               Null: 
         Index_type: BTREE
            Comment: 
      Index_comment: 
            Visible: YES
         Expression: NULL
      1 row in set (0.00 sec)
                 
    • 建立索引
    • mysql> create index idx_user_name on tb_user(name);
      Query OK, 0 rows affected (0.03 sec)
                 
  • phone手機号字段的值,是非空的且唯一的,給他建立唯一索引
mysql> create unique index idx_user_phone on tb_user(phone);
Query OK, 0 rows affected (0.01 sec)
           
  • 為profession、age、status建立聯合索引
    • 聯合索引字段的講究:使用最頻繁的字段放在左側。根據使用頻繁程度從重到輕,要遵循''最左字首原則''
mysql> create index idx_user_pro_age_status on tb_user(profession,age,status);
Query OK, 0 rows affected (0.01 sec)
           
  • 為email字段建立合适的索引來提升查詢效率
mysql> create index idx_user_email on tb_user(email);
Query OK, 0 rows affected (0.01 sec)
           
  • 删除索引
mysql> drop index idx_user_email on tb_user;
Query OK, 0 rows affected (0.01 sec)
           

2. 6 SQL性能分析

  1. SQL的執行頻率
  • 看下是以增删改查什麼為主進行操作
  • 檢視伺服器狀态狀态
mysql> show global status like 'Com_______';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_binlog    | 0     |
| Com_commit    | 0     |
| Com_delete    | 0     |
| Com_import    | 0     |
| Com_insert    | 24    |
| Com_repair    | 0     |
| Com_revoke    | 0     |
| Com_select    | 644   |
| Com_signal    | 0     |
| Com_update    | 0     |
| Com_xa_end    | 0     |
+---------------+-------+
11 rows in set (0.00 sec)
           
  1. 慢查詢日志
  • 含義:記錄了所有執行時間超過指定參數的所有SQL語句的日志。
  • 檢視開啟情況
mysql> show variables like 'slow_query_log';
+----------------+-------+
| Variable_name  | Value |
+----------------+-------+
| slow_query_log | OFF   |
+----------------+-------+
1 row in set (0.01 sec)
           
  • MySQL的慢查詢日志預設沒有開啟,需要在My'SQL的配置檔案(/etc/my.cnf)中配置如下資訊
[root@hadoop ~]# vi /etc/my.cnf
#開啟慢查詢日志
slow_query_log=1

#超過兩秒即為慢查詢
long_query_time=0.1秒
           
  • 重新開機下伺服器
[root@hadoop ~]# systemctl restart mysqld
           
  • 再次查詢慢日志情況發現就開了
mysql> show variables like 'slow_query_log';
+----------------+-------+
| Variable_name  | Value |
+----------------+-------+
| slow_query_log | ON    |
+----------------+-------+
           
  • 去查詢一百萬條資料
mysql> select count(*) from tb_sku;
+----------+
| count(*) |
+----------+
|  1000000 |
+----------+
1 row in set (13.35 sec)
           
  • 去檢視對應的慢查詢日志
[root@hadoop ~]# cd /var/lib/mysql
[root@hadoop mysql]# tail -f hadoop-slow.log 
           
春華秋實之MySQL進階-02 索引2 索引

3. profile詳情

  • 含義:可以幫助我們了解時間都耗費到哪兒去了
  • 檢視profile操作,yes表示支援
mysql> select @@have_profiling;
+------------------+
| @@have_profiling |
+------------------+
| YES              |
+------------------+
           
  • profile預設關閉,可以通過set開啟
mysql> select @@profiling;
+-------------+
| @@profiling |
+-------------+
|          0 |
+-------------+
mysql> set profiling = 1;
Query OK, 0 rows affected, 1 warning (0.00 sec)
           
  • 通過profile檢視下每一條SQL的耗時情況
mysql> show profiles;
+----------+------------+---------------------------------------------+
| Query_ID | Duration   | Query                                       |
+----------+------------+---------------------------------------------+
|        1 | 0.00025125 | select @@profiling                          |
|        2 | 0.00080300 | select * from tb_user                       |
|        3 | 0.00080375 | select * from tb_user where id = 1          |
|        4 | 0.00077725 | select * from tb_user where name = '白起'   |
|        5 | 0.00065025 | select count(*) from tb_sku                 |
|        6 | 0.00056925 | select count(*) from tb_user                |
+----------+------------+---------------------------------------------+
           
  • 檢視指定query_id 的SQL語句各個階段的耗時情況
mysql> show profile for query 6;
+--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.000127 |開啟時間
| Executing hook on transaction  | 0.000009 |執行事務時間
| starting                       | 0.000017 |
| checking permissions           | 0.000013 |檢查權限
| Opening tables                 | 0.000055 |打開表
| init                           | 0.000011 |進行初始化的操作
| System lock                    | 0.000015 |
| optimizing                     | 0.000030 |優化操作
| statistics                     | 0.000030 |統計操作
| preparing                      | 0.000034 |
| executing                      | 0.000137 |執行
| end                            | 0.000010 |
| query end                      | 0.000007 |
| waiting for handler commit     | 0.000015 |執行送出的操作
| closing tables                 | 0.000014 |
| freeing items                  | 0.000023 |
| cleaning up                    | 0.000024 |
+--------------------------------+----------+
17 rows in set, 1 warning (0.01 sec)
           
  • 檢視執行SQL語句CPU 的耗費情況
mysql> show profile cpu for query 6;
+--------------------------------+----------+----------+------------+
| Status                         | Duration | CPU_user | CPU_system |
+--------------------------------+----------+----------+------------+
| starting                       | 0.000127 | 0.000084 |   0.000032 |
| Executing hook on transaction  | 0.000009 | 0.000006 |   0.000002 |
| starting                       | 0.000017 | 0.000012 |   0.000005 |
| checking permissions           | 0.000013 | 0.000009 |   0.000004 |
| Opening tables                 | 0.000055 | 0.000040 |   0.000015 |
| init                           | 0.000011 | 0.000008 |   0.000003 |
| System lock                    | 0.000015 | 0.000011 |   0.000004 |
| optimizing                     | 0.000030 | 0.000021 |   0.000008 |
| statistics                     | 0.000030 | 0.000022 |   0.000009 |
| preparing                      | 0.000034 | 0.000024 |   0.000009 |
| executing                      | 0.000137 | 0.000100 |   0.000038 |
| end                            | 0.000010 | 0.000006 |   0.000003 |
| query end                      | 0.000007 | 0.000005 |   0.000002 |
| waiting for handler commit     | 0.000015 | 0.000011 |   0.000004 |
| closing tables                 | 0.000014 | 0.000010 |   0.000004 |
| freeing items                  | 0.000023 | 0.000017 |   0.000006 |
| cleaning up                    | 0.000024 | 0.000017 |   0.000007 |
+--------------------------------+-----------------------------------
           
  1. explain執行計劃
  • 含義:擷取MySQL如何執行SELECT語句的資訊,包括在SELECT語句執行過程表中如何連接配接和連接配接的順序
  • 文法:在任何select 語句中加上關鍵字explain/desc
explain select 字段清單 from 表名 where 條件;

mysql> explain select * from tb_user where id  = 1;
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+------
| id | select_type | table   | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+------
|  1 | SIMPLE      | tb_user | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+------
           
春華秋實之MySQL進階-02 索引2 索引

type連接配接的類型,possi- ble_keys可能用到的索引,key實際用到的索引,key_len索引的長度,rows掃描記錄數

  • 參數說明
    • id相同,執行順序從上而下,id不同,值越大,越先執行 (如圖3先執行,依次是c,sc,子查詢,s)
    • 春華秋實之MySQL進階-02 索引2 索引
    • select_type,表示select的類型,常見取值simple,primary,union,subquery(子查詢)
    • type,表示連接配接類型,性能由好到差的連接配接類型為null(一般是不引用表,例如select 'A'),sysytem,const(根據主鍵和唯一索引一般會出現),eq_ref,ref(使用非唯一索引會出現),range,index,all(全表掃描),實操中盡量往前優化
    • possibel key,顯示可能應用在這張表上的索引,一個或者多個
    • key,實際用到的索引
    • key_len,表示索引中使用的位元組數
    • rows,認為必須要執行的行數
    • filtered,表示傳回結果的行數占需讀取行數的百分比,filtered的值越大越好
    • extra,額外資訊

2.7 使用規則

  1. 驗證索引效率
  • 沒用索引
春華秋實之MySQL進階-02 索引2 索引
  • 用到索引
春華秋實之MySQL進階-02 索引2 索引
  1. 最左字首使用法則
  • 含義:主要針對聯合索引的,指的是從索引的最左列開始,并且不跳過索引中的列。如果跳過了,索引将部分失效。
  • 案例
    • 看下表中索引,發現profession,age,status形成了聯合索引
    • mysql> show index from tb_user;
      +---------+------------+-------------------------+--------------+-------------+-----------+----------
      | Table   | Non_unique | Key_name                | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
      +---------+------------+-------------------------+--------------+-------------+-----------+----------
      | tb_user |          0 | PRIMARY                 |            1 | id          | A         |          24 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
      | tb_user |          0 | idx_user_phone          |            1 | phone       | A         |          24 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
      | tb_user |          1 | idx_user_name           |            1 | name        | A         |          24 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
      | tb_user |          1 | idx_user_pro_age_status |            1 | profession  | A         |          16 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
      | tb_user |          1 | idx_user_pro_age_status |            2 | age         | A         |          22 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
      | tb_user |          1 | idx_user_pro_age_status |            3 | status      | A         |          24 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
      +---------+------------+-------------------------+--------------+-------------+-----------+----------
      
                 
    • 查詢條件寫聯合索引涉及的3個字段(字段順序可以變),并explain看下索引執行情況
    • mysql> explain select * from tb_user where profession = '軟體工程' and age = 31 and status = '0';
      +----+-------------+---------+------------+------+-------------------------+-------------------------
      | id | select_type | table   | partitions | type | possible_keys           | key                     
      +----+-------------+---------+------------+------+-------------------------+-------------------------
      |  1 | SIMPLE      | tb_user | NULL       | ref  | idx_user_pro_age_status | idx_user_pro_age_status 
      +----+-------------+---------+------------+------+-------------------------+-------------------------
      1 row in set, 1 warning (0.00 sec)
                 
    • 如果兩個字段呢,也是走的聯合索引,前提是最左字首,并且沒有跳過字段
    • mysql> explain select * from tb_user where profession = '軟體工程' and age = 31;
      +----+-------------+---------+------------+------+-------------------------+-------------------------
      | id | select_type | table   | partitions | type | possible_keys           | key                     
      +----+-------------+---------+------------+------+-------------------------+-------------------------
      |  1 | SIMPLE      | tb_user | NULL       | ref  | idx_user_pro_age_status | idx_user_pro_age_status 
      +----+-------------+---------+------------+------+-------------------------+-------------------------
      1 row in set, 1 warning (0.00 sec)
                 
    • 跳過最左列profession來查,就不走索引了,索引全部失效,ref=null走的全表掃描
    • mysql> explain select * from tb_user where age = 31 and status = '0';
      +----+-------------+---------+------------+------+---------------+------+---------+------+------+----
      | id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | 
      +----+-------------+---------+------------+------+---------------+------+---------+------+------+----
      |  1 | SIMPLE      | tb_user | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   24 |     
      +----+-------------+---------+------------+------+---------------+------+---------+------+------+----
      1 row in set, 1 warning (0.00 sec)
                 
    • 如果缺失中間字段age的索引呢,也走的聯合索引,因為最左列在,但是隻走了profession的索引,status沒走,出現了索引的部分失效
    • mysql> explain select * from tb_user where profession = '軟體工程' and status = '0';
      +----+-------------+---------+------------+------+-------------------------+-------------------------+---------+-------+------+----------+-----------------------+
      | id | select_type | table   | partitions | type | possible_keys           | key                     | key_len | ref   | rows | filtered | Extra                 |
      +----+-------------+---------+------------+------+-------------------------+-------------------------+---------+-------+------+----------+-----------------------+
      |  1 | SIMPLE      | tb_user | NULL       | ref  | idx_user_pro_age_status | idx_user_pro_age_status | 47      | const |    4 |    10.00 | Using index condition |
      +----+-------------+---------+------------+------+-------------------------+-------------------------+---------+-------+------+----------+-----------------------+
      1 row in set, 1 warning (0.00 sec)
                 
  1. 範圍查詢
  • 含義:聯合索引中,出現範圍查詢(>,<),範圍查詢右側的列索引失效
  • 案例:例如status就失效了,因為在範圍查詢的右側
mysql> explain select * from tb_user where profession = '軟體工程' and age > 30 and status = '0';
+----+-------------+---------+------------+-------+-------------------------+-------------------------+-----
| id | select_type | table   | partitions | type  | possible_keys           | key                     | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+---------+------------+-------+-------------------------+-------------------------+-----
|  1 | SIMPLE      | tb_user | NULL       | range | idx_user_pro_age_status | idx_user_pro_age_status | 49   
+----+-------------+---------+------------+-------+-------------------------+-------------------------+-----
1 row in set, 1 warning (0.01 sec)
           
  • 規避方法:業務允許的範圍下,盡量使用>=,就可以走聯合索引
mysql> explain select * from tb_user where profession = '軟體工程' and age >= 30 and status = '0';
+----+-------------+---------+------------+-------+-------------------------+-------------------------+-----
| id | select_type | table   | partitions | type  | possible_keys           | key                     |     
+----+-------------+---------+------------+-------+-------------------------+-------------------------+-----
|  1 | SIMPLE      | tb_user | NULL       | range | idx_user_pro_age_status | idx_user_pro_age_status | 54      | NULL |    2 |    10.00 | Using index condition |
+----+-------------+---------+------------+-------+-------------------------+-------------------------+-----
1 row in set, 1 warning (0.00 sec)

           
  1. 索引失效
  • 索引列在where中進行了運算,索引失效
mysql> explain select * from tb_user where substring(phone,10,2) = '15';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | 
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------
|  1 | SIMPLE      | tb_user | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   24 |   100.00 | 
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------
1 row in set, 1 warning (0.00 sec)
           
  • 字元串不加引号,索引失效
mysql> explain select * from tb_user where phone = 17799990015;
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------
| id | select_type | table   | partitions | type | possible_keys  | key  | key_len | ref  | rows | filtered 
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------
|  1 | SIMPLE      | tb_user | NULL       | ALL  | idx_user_phone | NULL | NULL    | NULL |   24 |    10.00 
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------
1 row in set, 3 warnings (0.00 sec)
           
0沒有引号,索引長度為49,status失效了
mysql> explain select * from tb_user where profession = '軟體工程' and age >= 30 and status = 0;
+----+-------------+---------+------------+-------+-------------------------+-------------------------+-----
| id | select_type | table   | partitions | type  | possible_keys           | key                     | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+---------+------------+-------+-------------------------+-------------------------+-----
|  1 | SIMPLE      | tb_user | NULL       | range | idx_user_pro_age_status | idx_user_pro_age_status | 49      | NULL |    2 |    10.00 | Using index condition |
+----+-------------+---------+------------+-------+-------------------------+-------------------------+-----
           
  • 僅僅是尾部模糊比對,索引不會失效,頭部的模糊比對會失效
這個走
mysql> explain select * from tb_user where profession like '軟體%';
+----+-------------+---------+------------+-------+-------------------------+-------------------------+-----
| id | select_type | table   | partitions | type  | possible_keys           | key                     | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+---------+------------+-------+-------------------------+-------------------------+-----
|  1 | SIMPLE      | tb_user | NULL       | range | idx_user_pro_age_status | idx_user_pro_age_status | 47      | NULL |    4 |   100.00 | Using index condition |
+----+-------------+---------+------------+-------+-------------------------+-------------------------+-----
1 row in set, 1 warning (0.00 sec)
           
這個不走
mysql> explain select * from tb_user where profession like '%工程';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | 
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------
|  1 | SIMPLE      | tb_user | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   24 |    11.11 | 
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------
1 row in set, 1 warning (0.00 sec)
           
  • or 前後字段都有索引才會走
age沒有索引
mysql> explain select * from tb_user where id = 10 or age = 23;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | 
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------
|  1 | SIMPLE      | tb_user | NULL       | ALL  | PRIMARY       | NULL | NULL    | NULL |   24 |    13.75 | 
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------
1 row in set, 1 warning (0.00 sec)
           
  • 資料分布影響
    • 如果MySQL檢測使用索引比全表更慢,則不适用索引,包含null
mysql> explain select * from tb_user where phone >= '17799990011';
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------
| id | select_type | table   | partitions | type | possible_keys  | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------
|  1 | SIMPLE      | tb_user | NULL       | ALL  | idx_user_phone | NULL | NULL    | NULL |   24 |    54.17 | Using where |
+----+-------------+---------+------------+------+----------------+------+---------+------+------+----------
           
  1. SQL提示
  • 含義:就是在SQL語句中g加入一些人為提示來達到優化操作的目的
  • use index :選擇給profession新增加的索引進行檢索
mysql> explain select * from tb_user use index(idx_user_profession) where profession = '軟體工程';
+----+-------------+---------+------------+------+----------------+------+---------+------+------+--
| id | select_type | table   | partitions | type | possible_keys       | key        | key_len | ref 
+----+-------------+---------+------------+------+---------------------+---------------------+-------
|  1 | SIMPLE      | tb_user | NULL       | ref  | idx_user_profession | idx_user_profession | 47     
+----+-------------+---------+------------+------+---------------------+---------------------+-------
1 row in set, 1 warning (0.00 sec)
           
  • ignore index:忽略這個索引
mysql> explain select * from tb_user ignore index(idx_user_profession) where profession = '軟體工程';
+----+-------------+---------+------------+------+-------------------------+-------------------------
| id | select_type | table   | partitions | type | possible_keys           | key                     
+----+-------------+---------+------------+------+-------------------------+-------------------------
|  1 | SIMPLE      | tb_user | NULL       | ref  | idx_user_pro_age_status | idx_user_pro_age_status 
+----+-------------+---------+------------+------+-------------------------+-------------------------
           
  • force index :強制
mysql> explain select * from tb_user force index(idx_user_profession) where profession = '軟體工程';
+----+-------------+---------+------------+------+---------------------+---------------------+---------+----
| id | select_type | table   | partitions | type | possible_keys       | key                 | key_len | ref 
+----+-------------+---------+------------+------+---------------------+---------------------+---------+----
|  1 | SIMPLE      | tb_user | NULL       | ref  | idx_user_profession | idx_user_profession | 47      | 
+----+-------------+---------+------------+------+---------------------+---------------------+---------+----
           
  1. 覆寫索引
  • 含義:盡量使用覆寫索引,減少select *
春華秋實之MySQL進階-02 索引2 索引
  • extra兩者差異
    • using index condition :查找使用了索引,但是需要回表查詢資料
    • using where;using index:查找使用了索引,不需要回表查詢
    • 具體原因:第一條資料聯合索引資料屬于二級索引葉子節點挂的就是id,是以需要的id就不要,第二條資料有一個name字段,需要根據id再去到聚集索引中回表查詢出來
  • 思考:一張表,有四個字段(id,username,password,status),由于資料量大,需要對SQL語句:select id,username,password from tb_user where usename = 'itcast';進行優化,該如何進行才是最優方案
    • 答案:對username和password建立聯合索引,這樣查詢這條SQL的時候就不需要進行回表查詢了
  1. 字首索引
  • 含義:當字段類型為字元串(varchar,test等)時,使用這樣的字段當作索引會讓索引變得很大,浪費磁盤io。此時隻将字元串的一部分字首建立索引
  • 文法
create index idx_xxxx on table_name(column(n));
           
  • 字首長度
    • 根據索引的選擇性來決定,選擇性是指不重複的索引值(基數)和資料表的記錄總值的比值。
mysql> select count(distinct email)/count(*) from tb_user;
+--------------------------------+
| count(distinct email)/count(*) |
+--------------------------------+
|                         1.0000 |
+--------------------------------+

截取郵箱前9個就發現有重複的了
mysql> select count(distinct substring(email,1,9))/count(*) from tb_user;
+-----------------------------------------------+
| count(distinct substring(email,1,9))/count(*) |
+-----------------------------------------------+
|                                        0.9583 |
+-----------------------------------------------+
           
  • 案例
mysql> create index idx_email_5 on tb_user(email(5));
           
春華秋實之MySQL進階-02 索引2 索引
春華秋實之MySQL進階-02 索引2 索引
  • 資料結構分析
春華秋實之MySQL進階-02 索引2 索引

前面5位去輔助索引查詢到對用id再去聚集索引那邊找資料,由于采用的是前5位,是以會去看下email是不是全部等于where後面的條件,下一步再去看輔助索引的連結清單下一個節點中email是不是where的條件,如果是就傳回,如果不是就不要

  1. 單列索引和聯合索引選取的規則
  • 案例
mysql> explain select id,phone,name from tb_user where phone = '17799990010' and name = '韓信';
           
春華秋實之MySQL進階-02 索引2 索引

phone和name都有單列索引,但是隻走了phone的索引,那也會産生回表查詢的,因為phone的索引是沒有包含name的字段的值

建立一個phone,name的聯合索引
mysql> create unique index idx_user_phone_name on tb_user(phone,name);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0
           
再次查詢走的也不是走聯合的
mysql> explain select id,phone,name from tb_user where phone = '17799990010' and name = '韓信';
           
春華秋實之MySQL進階-02 索引2 索引
是以這邊訓示一下SQL提示,指定聯合索引檢索,最後的extra中using index不需要回表查詢
mysql> explain select id,phone,name from tb_user use index(idx_user_phone_name) where phone = '17799990010' and name = '韓信';
           
春華秋實之MySQL進階-02 索引2 索引
  • 結論
    • 如在業務場景中,如果存在多個查詢條件,考慮針對于查詢字段建立索引時,建議建立聯合索引。
  1. 設計原則

繼續閱讀