天天看點

MySQL索引深入學習(四)

前面第三章,我們看了mysql的七種的join的查詢,那這次我們就看下怎麼看我們寫的sql語句有沒有使用索引?

第一個問題:索引是什麼?

索引是幫助Mysql高效擷取資料的資料結構(資料結構),可以了解為我們的字典(排好序的快速查找的資料結構)。
官網的定義:資料本身之外,資料庫還維護着一個滿足特定查找算法的資料結構,這些資料結構以某種方式指向資料,就可以在這些資料結構得基礎上面實作進階查找的算法,這種資料結構就是索引。切記查找+排序
           

這裡說到了查找+排序,請大家先把這個記住,後面我們在仔細說這個

我們先寫個代碼插入1000萬條資料吧,好久沒寫代碼了畢竟,不然都不好意思說自己是一個程式員了

# sql腳本
create table user
(
    id   int auto_increment
        primary key,
    name int null,
    pwd  int null
);
           
# java代碼
package com.ai.paul.mallsmanager.service;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * @ClassName : TestJD1  //類名
 * @Description :   //描述
 * @Author : paul
 * @Date: 2020/9/9  16:55
 */
public class TestJD1 {
    //static int count = 0;

    public static void main(String[] args) {

        long start = System.currentTimeMillis();
        conn();
        long end = System.currentTimeMillis();
        System.out.println("耗時:" + (end - start)/1000 + "秒");
    }

    public static void conn(){
        //1.導入驅動jar包
        //2.注冊驅動(mysql5之後的驅動jar包可以省略注冊驅動的步驟)
        //Class.forName("com.mysql.jdbc.Driver");
        //3.擷取資料庫連接配接對象
        Connection conn = null;
        PreparedStatement pstmt = null;
        {
            try {
                //"&rewriteBatchedStatements=true",一次插入多條資料,隻插入一次
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/malls?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8" + "&rewriteBatchedStatements=true","root","root");
                //4.定義sql語句
                String sql = "insert into user values(default,?,?)";
                //5.擷取執行sql的對象PreparedStatement
                pstmt = conn.prepareStatement(sql);
                //6.不斷産生sql
                for (int i = 0; i < 10000000; i++) {
                    pstmt.setString(1,(int)(Math.random()*1000000)+"");
                    pstmt.setString(2,(int)(Math.random()*1000000)+"");
                    pstmt.addBatch();
                }
                //7.往資料庫插入一次資料
                pstmt.executeBatch();
                System.out.println("添加10000000條資訊成功!");

            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                //8.釋放資源
                //避免空指針異常
                if(pstmt != null) {
                    try {
                        pstmt.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }

                if(conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }
}
           

我們剛開始的表結構是沒有索引的,如果現在我們執行下建立索引呢?

MySQL索引深入學習(四)

這裡感覺自己的控制台有點少女心啊。。。O(∩_∩)O哈哈~

我們可以看到建立索引的時候花了17秒,為什麼這麼慢呢?

這裡有個問題?為什麼查詢相對增删改速度較快呢?我們插入了1000w的資料

後續的時候我們在說,現在看下查詢和更新的速度?該問題未複現出來,隻能原理來說下了囧,有知道的小夥伴可以私信說下

當我們插入/删除一條資料庫的時候:

1.将該條資料插入/删除表中->同時需要維護新的索引數,因為插入的值導緻了索引值變化了,是以需要 重新建構。需要同時維護的。因為如果不維護的話可能會導緻根據索引查詢不到值得,導緻可能報錯。(這裡建議可以的話使用邏輯删除,不要使用實體删除,因為實體删除需要再次維護索引)

2.當我們查詢某一條資料的時候,隻需要查詢,但是不會涉及索引。

2.索引的優勢和劣勢

優勢 劣勢
提高了檢索的效率,降低了資料庫IO 索引也是一張表,索引也是需要占用空間的
降低了資料排序的成本,降低了CPU的消耗 提高了查詢速度,但是降低了更新表速度,因為索引位置也需要改變

索引的類型

類型 建立語句
單值索引 create index indexName on tableName(col);alter tableName add index indexName on (col)
唯一索引 create unique index indexName on tableName(col)
複合索引 create index indexName on tableName(col1,col2)
全文索引(用于全文檢索) create fulltext index indexName on tableName(col)
檢視索引 show index from tableName

4.mysql的索引結構

1.BTree索引
2.Hash索引
3.full-text全文索引
4.R-Tree索引
這裡着重介紹BTree索引

系統從磁盤讀取資料到記憶體時是以磁盤塊(block)為基本機關的,位于同一個磁盤塊中的資料會被一次性讀取出來,而不是需要什麼取什麼。

InnoDB存儲引擎中有頁(Page)的概念,頁是其磁盤管理的最小機關。InnoDB存儲引擎中預設每個頁的大小為16KB,可通過參數innodb_page_size将頁的大小設定為4K、8K、16K,在MySQL中可通過如下指令檢視頁的大小:
show variables like 'innodb_page_size';
           
MySQL索引深入學習(四)

上圖是一個BTree索引樹,那是怎麼查詢的資料的呢?(這裡以查找29為例)

首先将磁盤1讀取進記憶體,進行了【磁盤I/O操作】,在記憶體中利用二分查找,
發現29在17和35之間,然後就找到位址指針P2,然後根據P2讀取磁盤3到記憶體中,進行了【磁盤I/O操作】,
在記憶體中利用二分查找,發現29在26和30之間,然後就找到位址指針P2,
然後根據P2讀取磁盤8到記憶體中,利用二分查找找到了。
           

但是上面的BTree是否存在問題呢?

我們可以看到在每個磁盤都存了data,如果data非常大的話則一層也不能存儲很多的資料,當資料量較大的時候,會導緻數的層次會比較深,也會導緻磁盤的I/O會增多。這時就引入了一個炸天的B+Tree樹了。

B+Tree相對BTree樹主要是以下不同
非葉子節點隻存儲鍵值資訊。
所有葉子節點之間都有一個鍊指針。
資料記錄都存放在葉子節點中。
           

在說B+Tree樹之前,我們再說下下面幾種樹吧,實際心裡是不想說的,嘿嘿

二叉樹:

MySQL索引深入學習(四)

二叉樹具有以下性質:左子樹的鍵值小于根的鍵值,右子樹的鍵值大于根的鍵值。那這樣的二叉樹作為索引結構有啥問題呢?失衡,因為如果一直比根節點大下去則會I/O也會非常大。

那就需要引入平衡二叉樹(在符合二叉查找樹的條件下,還滿足任何節點的兩個子樹的高度最大差為1。)

如果在AVL樹中進行插入或删除節點,可能導緻AVL樹失去平衡,這種失去平衡的二叉樹可以概括為四種姿态:LL(左左)、RR(右右)、LR(左右)、RL(右左)。它們的示意圖如下:

MySQL索引深入學習(四)
姿态 原因 怎麼平衡
LL(左左) 當插入1的時候,我們從左子樹數可以看到有三層,但是右子樹隻有一層,兩者高度差為2 以失衡點4位中心點旋轉作為新的根節點,将4的右節點6作為原根節點8的左節點,然後作為新根節點4的右節點
RR(右右) 當插入13的時候,我們從右子樹數可以看到有三層,但是左子樹隻有一層,兩者高度差為2 以失衡點12位中心點旋轉作為新的根節點,将12的左節點10作為原根節點8的右節點,然後作為新根節點4的左節點
LR(左右) 當插入5的時候,我們從左子樹數可以看到有三層,但是右子樹隻有一層,兩者高度差為2 首先RR單轉在LL單轉:首先對4節點進行RR單轉,5變成4的右節點,然後4作為6的左節點,在進行LL單轉,将6作為新根節點,然後8和12連接配接
RL(右左) 當插入9的時候,我們從右子樹數可以看到有三層,但是左子樹隻有一層,兩者高度差為2 首先LL單轉在RR單轉:将10作為新失衡點的根節點,然後将10作為新的根節點
MySQL索引深入學習(四)
MySQL索引深入學習(四)
MySQL索引深入學習(四)

那B+Tree呢?

MySQL索引深入學習(四)
通常在B+Tree上有兩個頭指針,一個指向根節點,另一個指向關鍵字最小的葉子節點,
而且所有葉子節點(即資料節點)之間是一種鍊式環結構。
是以可以對B+Tree進行兩種查找運算:一種是對于主鍵的範圍查找和分頁查找,另一種是從根節點開始,進行随機查找。
           

都講到這裡我們就在說下紅黑樹

紅黑樹是一種含有紅黑結點并能自平衡的二叉查找樹。它必須滿足下面性質:
性質1:每個節點要麼是黑色,要麼是紅色。
性質2:根節點是黑色。
性質3:每個葉子節點(NIL)是黑色。
性質4:每個紅色結點的兩個子結點一定都是黑色。
性質5:任意一結點到每個葉子結點的路徑都包含數量相同的黑結點。
           

這裡不懂的千萬别問樓主,因為樓主知道也不會說的,哈哈

更多内容已上傳公衆号【那條轉彎的路】

MySQL索引深入學習(四)