【Mysql】索引簡介

本文口味:番茄炒蛋,預計閱讀:10分鐘。java

博客又停更了兩個月,在這期間,對人生和世界多了許多思考。在人生的不一樣階段,會對生活和世界有着不同的認知,而認知的改變也會直接反應在行爲模式之中。mysql

對於生活的思考心得也會在以後的時間裏,慢慢分享給你們,一方面是對本身心路歷程的記錄和總結,另外一方面也但願能給遇到一樣問題或疑惑的朋友以幫助。目前生活已經慢慢調整到我想要的樣子,博客寫做也該繼續起航了。sql

1、說明

Mysql是最經常使用的關係型數據庫,而索引則是Mysql調優中最關心的部分,設計一個好的索引並寫出合適的sql,就能將查詢速度大大提高。從本篇開始,將會對Mysql中的索引進行深刻淺出的介紹,從索引的簡介、類別、使用姿式到索引的原理,最後到索引實戰。但願經過本系列的文章,能讓你對mysql中的索引有一個更深刻的認識。數據庫

如下是本文大綱:數據結構

2、什麼是索引

索引是存儲引擎用於快速查找記錄的一種數據結構。app

emm,用人話說,若是把Mysql比做一本書的話,索引就是書的目錄,根據目錄便能很快找到須要的信息所在的頁面,若是沒有目錄的話,想要查找想要的信息就只能一頁一頁翻了。
好比下面這樣一條簡單的sql:cors

SELECT id,name,course,grade FROM t_grade WHERE name = 'Frank';

若是沒有添加索引的話,只能從最小記錄開始依次遍歷mysql中的記錄,而後對比每條記錄是否符合搜索條件。若是表中的數據量不大(十萬級別如下),耗時其實也還好,畢竟目前來講,CPU效率已經很高了。但這樣實際上是對CPU的一種浪費,就比如開着跑車在泥濘的鄉村小路上駕駛,徹底沒法發揮它應有的性能。而索引即是這樣一條康莊大道,有了索引,才能充分發揮mysql引擎的性能,讓你的sql跑車風馳電掣。dom

3、索引的優缺點

對於大部分事物而言,一般存在其對立面的,有好的一面,就會有壞的一面,就像質量好的東西一般價格高,便宜的東西一般質量差,索引也是如此。性能

使用索引的優勢顯而易見:測試

  1. 能夠大大加快數據檢索效率。
  2. 能夠加速表與表之間的鏈接。
  3. 能夠經過惟一索引的建立,保證數據的惟一性。
  4. 能夠顯著減小分組與排序的時間。

總而言之,用一個字來總結,就是快。

使用索引的缺點也是須要考慮的:

  1. 索引的建立和維護須要時間成本。表中的數據量越大,插入或刪除數據時,調整索引所須要的時間就越長。
  2. 索引須要單獨存儲,佔用磁盤空間,若是設置了大量的索引,佔用的空間甚至比記錄自己更大。
  3. 在對數據進行增、刪、改時,須要同時更新索引中的數據,所以會影響增刪改的速度。

因此使用索引並非百利而無一害,使用不當甚至可能形成刪庫跑路的慘劇【手動滑稽】。但當你瞭解它的原理,掌握了索引的真諦,它就會成爲你的神兵利器,讓你在mysql開發中所向披靡。

4、索引的分類以及建立姿式

索引可分爲普通索引、惟一索引、主鍵索引、組合索引、全文索引。看起來好像不少很複雜,但其實並不是如此,且聽我慢慢道來。

普通索引,名字中就透露出它普通的氣質,也就是最多見的索引。

如何建立一個普通索引呢?其實很簡單,若是是在DDL中建立索引,能夠這樣使用:

CREATE TABLE `t_grade` (
    id BIGINT(20) COMMENT '主鍵id',
    name VARCHAR(30) COMMENT '姓名',
    course INT COMMENT '課程,1-語文,2-數學,3-英語,4-物理',
    grade DECIMAL(5,2) COMMENT '成績',
    KEY idx_name(`name`)
)ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;

這樣就爲"name"列建立了一個名爲"idx_name"的普通索引。通用的建立方式爲:

KEY 索引名 (`列名`)

若是是爲一張已經建立好的表添加一個普通索引,那麼能夠這樣:

ALTER TABLE `t_grade` ADD KEY idx_name(`name`);

你可能會說,「不是用index關鍵字來建立索引的嗎」,別急別急,其實它們的效果是同樣的。

主鍵索引,一看就是很關鍵的角色,沒錯,每張表都會有且只有一個主鍵索引,即便沒有顯式的建立主鍵索引的話,也會自動建立一個隱藏的主鍵索引。

這麼重要的索引,用的關鍵字確定也得不同才行,建立主鍵索引的關鍵字是PRIMARY KEY,在DDL中添加主鍵索引的姿式爲:

CREATE TABLE `t_grade` (
    id BIGINT(20) COMMENT '主鍵id',
    name VARCHAR(30) COMMENT '姓名',
    course INT COMMENT '課程,1-語文,2-數學,3-英語,4-物理',
    grade DECIMAL(5,2) COMMENT '成績',
    PRIMARY KEY (`id`)
)ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;

由於主鍵只能有一個,因此不須要添加主鍵名。通用的添加方式爲:

PRIMARY KEY (`列名`)

若是是爲已建立好的表添加主鍵索引,那麼能夠這樣:

ALTER TABLE `t_grade` ADD PRIMARY KEY (`id`);

惟一索引,顧名思義,就是「惟一」的索引,被添加到索引中的列的值必須是惟一的,若是向數據表中插入一條已存在的惟一索引字段記錄,就會報錯。

定義惟一索引的關鍵字爲 UNIQUE KEY。在DDL中添加惟一索引的姿式爲:

CREATE TABLE `t_grade` (
    id BIGINT(20) COMMENT '主鍵id',
    name VARCHAR(30) COMMENT '姓名',
    course INT COMMENT '課程,1-語文,2-數學,3-英語,4-物理',
    grade DECIMAL(5,2) COMMENT '成績',
    UNIQUE KEY uk_name (`name`)
)ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;

惟一索引的通用添加方式爲:

UNIQUE KEY 索引名 (`列名`)

爲已建立好的表添加惟一索引:

ALTER TABLE `t_grade` ADD UNIQUE KEY uk_name (`name`);

組合索引,又叫聯合索引,即是將兩個或者多個字段組合在一塊兒的索引,好像跟沒說同樣= =
看一個栗子就知道了。

CREATE TABLE `t_grade` (
    id BIGINT(20) COMMENT '主鍵id',
    name VARCHAR(30) COMMENT '姓名',
    course INT COMMENT '課程,1-語文,2-數學,3-英語,4-物理',
    grade DECIMAL(5,2) COMMENT '成績',
    KEY idx_name_corse (`name`,`course`)
)ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;

一樣是使用key關鍵字,在索引名後添加多個字段名便可。這裏有一點須要注意的是,字段排列是有順序的。舉例說明,下面這兩個索引是不同的:

ALTER TABLE `t_grade` ADD KEY idx_name_course (`name`,`course`);
ALTER TABLE `t_grade` ADD KEY idx_name_course (`course`,`name`);

索引的匹配遵循「左綴匹配原則」,舉個栗子說明,若是建立的組合索引是

ALTER TABLE `t_grade` ADD KEY idx_name_course (`name`,`course`);

那麼下面語句將能命中這個組合索引。

SELECT * FROM `t_grade` WHERE name = 'Frank';

而下面這個語句將沒法命中索引:

SELECT * FROM `t_grade` WHERE course = 1;

由於在組合索引中,索引中的記錄是先按照前一個字段排序,而後再根據後一個字段排序的,因此若是直接使用組合索引中的第二個字段查詢時,查詢索引對索引記錄進行遍歷,遍歷完成以後還須要回溯到聚簇索引中獲取完整記錄,這樣反而更耗時間,因此sql優化器會選擇直接對記錄進行遍歷。

若是你還不清楚索引的結構以及聚簇索引是什麼,不要着急,後面的文章裏會有詳細的介紹。

聯合惟一索引,即是將多個字段組合起來造成一個惟一鍵,舉個栗子:

先刪除全部索引,而後添加兩條記錄:

INSERT INTO `t_grade` (`id`, `name`, `course`, `grade`) VALUES(1, 'Frank', 1, 100);
INSERT INTO `t_grade` (`id`, `name`, `course`, `grade`) VALUES(2, 'Frank', 1, 95);

這樣就能插入兩條記錄了。

而後刪掉這兩條記錄,建立一個聯合惟一索引:

ALTER TABLE `t_grade` ADD UNIQUE KEY idx_name_course (`name`,`course`);

而後再來執行一下上面的sql:

這時候,就會獲得一個錯誤提示,由於將字段namecourse建立了聯合惟一索引,因此這兩個字段的組合值必須是惟一的,若是要插入的記錄的這兩個字段組合值已經存在,那麼就會拋出異常。

最後一個是比較複雜的索引:全文索引,因爲其複雜性,這裏只簡單的介紹它的建立姿式。

CREATE TABLE `t_article`(
        id BIGINT COMMENT '文章id',
        title VARCHAR(200) COMMENT '文章標題',
        content TEXT COMMENT '文章內容',
        FULLTEXT (title, content)
)ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;

或者給現有表添加全文索引:

ALTER TABLE `t_article` ADD FULLTEXT KEY fidx_title_content (title,content) WITH PARSER ngram;

想要使用全文索引查詢,則須要使用MATCH關鍵字。

SELECT * FROM `t_article` WHERE MATCH(title, content) AGAINST('查詢字符串');

固然,若是想要使用全文索引,須要確認mysql的版本號在5.7以上,不然沒法在innodb引擎上使用全文索引的中文檢索插件ngram。

5、索引使用先後對比

爲了更直觀的看出索引的優缺點,咱們能夠來對數據表添加索引先後執行相同sql的耗時來看出對比,這裏僅進行簡單的比較,沒有使用性能測試。

先來建立一個數據表:

CREATE TABLE `t_grade` (
    id BIGINT(20) COMMENT '主鍵id',
    name VARCHAR(30) COMMENT '姓名',
    course INT COMMENT '課程,0-化學,1-語文,2-數學,3-英語,4-物理',
    grade DECIMAL(5,2) COMMENT '成績'
)ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;

而後插入一百萬條數據:

public void batchInsert(){
    long timeMillis = System.currentTimeMillis();
    System.out.println("開始插入數據");
    for (int i = 1; i < 1000000; i++) {
        GradeDO gradeDO = new GradeDO((long) i, randomName(), random.nextInt(5), BigDecimal.valueOf(random.nextDouble() * 100));
        gradeMapper.insert(gradeDO);
    }
    System.out.println("插入一百萬條記錄耗時:" + ( System.currentTimeMillis() - timeMillis) / 1000.0 );
}

輸出以下:

開始插入數據
插入一百萬條記錄耗時:1507.102

如今是沒有索引的狀態,開始進行插入測試:

public void batchInsert(){
    long timeMillis = System.currentTimeMillis();
    System.out.println("開始插入數據");
    for (int i = 1000000; i < 1010000; i++) {
        GradeDO gradeDO = new GradeDO((long) i, randomName(), random.nextInt(5), BigDecimal.valueOf(random.nextDouble() * 100));
        gradeMapper.insert(gradeDO);
    }
    System.out.println("插入一萬條記錄耗時:" + ( System.currentTimeMillis() - timeMillis) / 1000.0 );
}

輸出以下:

開始插入數據
插入一萬條記錄耗時:15.681

而後進行查詢測試。

@Test
void testQuery() {
    long timeMillis = System.currentTimeMillis();
    System.out.println("開始查詢");
    for (int i = 0; i < 100; i++) {
        Integer id = random.nextInt(1000000);
        GradeDO gradeDO = gradeMapper.selectById(id);
    }
    System.out.println("一百次查詢耗時:" + ( System.currentTimeMillis() - timeMillis) / 1000.0 );
}

輸出以下:

開始查詢
一百次查詢耗時:51.658

接下來,爲id列建立一個主鍵,併爲name字段建立一個普通索引。
再插入一萬條記錄:

開始插入數據
插入一萬條記錄耗時:17.465

而後進行查詢測試。

開始查詢
一百次查詢耗時:0.191

能夠看出,在有單個索引的狀況下,建立記錄耗時略長於無索引的狀況,當字段數量和索引數量增長時,這種差距將會增大。查詢效率能夠清晰的看出,這裏添加了索引以後,大大的縮減了查詢的耗時,固然,這裏主要是聚簇索引的功勞。

6、總結

索引是mysql中十分重要的一個特性,使用好它就能讓你的sql如虎添翼。簡單來講,索引一方面能夠大大提高查詢性能,另外一方面也會佔用時間和空間成本,所以索引的選擇也是一門學問。索引有不少種類型,不一樣類型的索引有着不一樣的特性,所以只有瞭解了它們各自的特性才能正確使用它們。
關於索引的簡介就先介紹到這裏了,後面會對索引的原理進行進一步深刻的介紹,讓你不只知道怎麼使用索引,並且還能知道爲何要這樣使用索引。
若是本文對你有幫助,不要吝嗇你的點贊哦。也歡迎關注個人公衆號進行留言交流。

相關文章
相關標籤/搜索