MySQL 表與索引設計攻略

歡迎關注富途web開發團隊,php , 前端都缺。缺人從衆php

首先,跟你們說句抱歉。好久沒有更新了。html

上個月去了趟上海,參加了FDC2018前端千人峯會以後。就一直沒有來得及更新。前端

參會體驗分享點擊這裏mysql

感觸挺多的。有機會之後會多參加。git

最近想着本身也學習一點好玩的東西吧。github

原文連接web

第一部分 MySQL概述

MySQL 是什麼

1970 年,Edgar Frank "Ted" Codd(關係數據庫之父)發表了題爲"A Relational Model of Data for Large Shared Data Banks"(大型共享數據庫的關係數據模型)的論文,文中首次提出並證實了,可使用關係模型來描述數據。sql

關係模型是指使用二維表的形式來表示實體和實體間的聯繫。MySQL 是基於這個理論而實現的許多關係型數據庫之一。數據庫

關係模型圖編程

MySQL 的歷史與現狀

  • 1990 Michael Widenius 寫了 MySQL 的第一個版本。
  • 1995 Michael Widenius 成立了 MySQL AB 公司。
  • 2000 Michael Widenius 公佈了 MySQL 源碼,採用 GPL 許可協議。MySQL 進入開源時代。
  • 2008 Sun 收購了 MySQL AB 公司。MySQL 數據庫進入 Sun 時代。
  • 2009 Oracle 收購 Sun 公司。MySQL 數據庫進入 Oracle 時代。Oracle 同時維護社區版的 MySQL,以及一個企業版本的 MySQL。
  • 2009 Michael Widenius 從開源的 MySQL 分支上從新拉分支,創立 MariaDB

MySQL 版本

MySQL 的社區版開源免費的,企業版是閉源收費的。

下面是主要版本特性的對比圖。

騰訊雲 CDB 5.6 的版本是:5.6.28-cdb20160902-logonline ddl的時候,須要注意:給某個表增長列的時候仍是會形成堵塞。

5.7版本的mysql 在性能和併發鏈接數上都有很大幅度的提高。

MySQL 與 MariaDB 的兼容性和差別

隨着Oracle買下Sun,MySQL也落入了關係型數據庫王者之手。而早在2009年,考慮到Oracle的名聲以及其入手以後閉源的可能性,MySQL之父的Michael便先行一步,以他女兒Maria的名字開始了MySQL的另一個衍生版本:MariaDB。

As MariaDB is a full replacement of MySQL, the MySQL manual at dev.mysql.com/doc is generally applicable.--來源

咱們每個月都會將社區版的 MySQL 基本代碼編譯入 MariaDB,從而保證 MariaDB 與 Oracle 添加的任何補丁和更新的 MySQL 相兼容。

MariaDB版本與Mysql版本相匹配——好比MariaDB 5.1,與MySQL 5.1使用相同的代碼。因爲更新和修復是針對MySQL源碼樹的,這樣的話MariaDB能夠採納這些補丁,指的是原有代碼的補丁,不是各自的新特性(理論上,MariaDB每個月都與MySQL源碼合併)。--來源

Upgrading from MySQL to MariaDB

兼容性和差別

MySQL 的邏輯架構

Mysql是由SQL接口,解析器,優化器,緩存,存儲引擎組成的。

  • Connectors 調用方
  • Management Serveices & Utilities 系統管理和控制工具
  • Connection Pool 鏈接池
  • SQL Interface
  • Parser 解析器
  • Optimizer 優化器
  • Cache & Buffer 各類緩存
  • Engine 存儲引擎

線程池是Mysql5.6的一個核心功能,對於服務器應用而言,不管是web應用服務仍是DB服務,高併發請求始終是一個繞不開的話題。當有大量請求併發訪問時,必定伴隨着資源的不斷建立和釋放,致使資源利用率低,下降了服務質量。線程池是一種通用的技術,經過預先建立必定數量的線程,當有請求達到時,線程池分配一個線程提供服務,請求結束後,該線程又去服務其餘請求。 經過這種方式,避免了線程和內存對象的頻繁建立和釋放,下降了服務端的併發度,減小了上下文切換和資源的競爭,提升資源利用效率。全部服務的線程池本質都是位了提升資源利用效率,而且實現方式也大致相同。本文主要說明Mysql線程池的實現原理。

MySQL 的物理文件

日誌文件會記錄mysql運行期間發生的變化。當mysql遭到意外的損壞時,能夠經過日誌文件進行數據恢復。

日誌記錄的信息不少。好比:mysql鏈接情況、SQL語句的執行狀況和錯誤信息等都會被記錄下來。

mysql的日誌文件主要包含如下的幾種:

  • 錯誤日誌
  • 查詢日誌
  • 慢查詢日誌
  • 事務日誌
  • 二進制日誌

錯誤日誌主要記錄mysql服務器相關的數據;

慢查詢日誌記錄一些執行時間較長的查詢query;

事務日誌是InnoDB存儲引擎特有的日誌;

二進制日誌主要記錄修改數據或有可能引發數據改變的mysql語句;

mysql存儲數據文件會根據各個存儲引擎不一樣而使用不一樣的存儲文件。

SQL 執行過程

全部的咱們想查詢或者修改,刪除數據,都是經過執行sql語句來完成的。mysql經過分析咱們傳入的sql語句來進行相關操做。

sql語句傳入mysql後,會首選查看緩存中有木有匹配的數據,有的話直接返回數據,結束sql。若是沒有,則須要將當前sql語句傳入解析器進行語法解析。而後進行預處理檢查語法是否符合語義,最後優化器將其轉化爲執行計劃,獲得mysql最合適的查詢語句,最後交給查詢執行引擎。獲取咱們最後想要的數據。

緩存池、順序讀取與隨機讀取

緩存池是存儲引擎實現的(與查詢緩存是兩個不一樣層次的緩存)。在 MySQL InnoDB 中,能夠經過innodb_buffer_pool_size參數來定義緩存池的大小。

緩存池經過 LRU 策略進行維護。若數據庫中的數據能夠徹底存放於緩存池中,則能夠認爲,此時數據庫的性能是最佳的了。除了同步或異步的寫磁盤操做外,全部其餘操做均可以在內存中完成。

下面是 18G 的數據,隨着緩存池的變大,TPS 的變化狀況。18G 數據,存到內存要比 18G 大一點,由於還有其餘的開銷。

由於有了緩存池,一些熱點的數據,就能夠自動躺在緩存池中了。

磁盤與硬盤的隨機讀寫和順序讀寫:順序讀取是指順序地讀取磁盤上的頁。隨機讀取是指訪問的頁不是連續的,須要磁盤的磁頭不停地移動。

======================第一部分 完=====================

第二部分 數據庫設計

範式定義

  • 第一範式:屬性不可分割。數據表中的每一列(每一個字段)必須是不可拆分的最小單元,也就是確保每一列的原子性;
  • 第二範式:要有主鍵,要求其餘字段都依賴於主鍵。知足1NF後,要求表中的全部列,都必須依賴於主鍵,而不能有任何一列與主鍵沒有關係,也就是說一個表只描述一件事情;
  • 第三範式:消除傳遞依賴(消除冗餘)。必須先知足第二範式(2NF),要求:表中的每一列只與主鍵直接相關而不是間接相關,(表中的每一列只能依賴於主鍵)
  • 巴斯-科德範式(BCNF): 每一個表中只有一個候選鍵
  • 第四範式: 消除表中的多值依賴。(當一個表中的非主屬性互相獨立時(3NF),這些非主屬性不該該有多值)

注意:一般咱們到第三範式就夠了,後面的都太嚴格了,不符合實際使用。按照領域模型方式來建數據庫,通常都能很好地知足到第三範式。

先按照範式的規範來設計表。而後根據實際的查詢查詢需求,使用反範式加速查詢。

字段類型選擇

基本準則:

  • 更小的,簡單地,夠用的,一般更好
  • 通常狀況下,應該儘可能使用能夠正確存儲數據最小數據類型
  • 更小的數據類型一般更快,由於它們佔用更少的磁盤、內存和CPU緩存,並在處理的時候需藥的CPU週期也更少。
  • 固然,後期更換字段類型是很耗時和痛苦的事情。因此,在一開始設計的時候,最好根據業務規模,在「更小原則」與「後期維護」間進行權衡。在夠用的狀況下,選擇最小的。
  • 根據字段的屬性,選擇簡單地數據類型。如:年齡就應該用整型存,不要用字符串存。時間就應該用內置的時間類型來存。IP就應該用int來存。(inet_aton('127.0.0.1') inet_ntoa(4294967295) MySQL都已經內置了轉化函數了 )
  • 儘可能避免 NULL
  • 儘可能避免使用 set 和 enum 類型

整型

  • TINYINT: 8位
  • SMALLINT: 16位
  • MEDIUMINT: 24位
  • INT: 32位
  • BIGINT: 64位

範圍:-2^(n-1) ~ (2^(n-1))-1

使用 UNSIGNED,可使正數的範圍提升正式的一倍+1,如-128~127 -> 0~255

  • UNSIGNED INT 40億
  • UNSIGNED BIGINT 18446744萬億

INT(11),其中的11並無什麼做用。只是規定了一些MYSQL客戶端在顯示數據時的顯示格式而已。

題外話:上面的5種整數類型,只是規定了 MYSQL 怎麼在內存和磁盤中保存數據。而,在整數計算時,MYSQL 通常將其所有轉成64位的 BIGINT 進行運算。

實數類型

小數字段推薦使用 decimal 類型,float 和 double 精度不夠,特別是涉及金錢的業務,必須使用 decimal。--騰訊雲 CDB for MySQL 使用規範指南

咱們的實際業務中,更喜歡用整型來存(擴大 1000、10000等)

字符串類型

5.7 後默認使用utf8mb4

  • CHAR 定長。括號中寫多少就固定開好了指定個數的存儲,只能存多少個字符,不夠則系統會默認補上些東西。
  • VARCHAR 變長。括號中填的是最大的字符個數。實際存數據的時候,不是開固定長度的空間。而是根據寫入的數據來開,可是不能超過最大的字符數。

注意,括號中定義的是字符數不是字節數

CHAR 會去掉最右邊的空格(若是有的話)。而 VARCHAR 則會保留。

VARCHAR 和 CHAR 的括號中存的都是最大字符數。

存 'hello' 時,使用 VARCHAR(5) 比使用 VARCHAR(200) 要好。雖然兩者佔用的空間是同樣的,可是仍是 VARCHAR(5) 會好些,在後續的某些操做者更有效率。(更少原則)

存儲很大的數據:

  • BLOB系列(二進制):TINYBLOB、SMALLBLOB、BLOB、MEDIUMBLOB、LONGBLOB
  • TEXT系列(字符串):TINYTEXT、SMALLTEXT、TEXT、MEDIUMTEXT、LONGTEXT

附一個通用的實踐:整數字段的查找效率是最好的。因此,若是某些字段要查找、排序、關聯等,使用整型的效率最好。

日期和時間類型

  • DATETIME

使用64位來存儲,時間跨度爲1001年~9999年。這個類型沒有時區的概念,好比我在東八區存了個"2018-01-01 00:00:00"進去,而後在西八區取出來,取到的仍是"2018-01-01 00:00:00"。那這就不正確了。

因此比較好的作法是,數據庫中使用DATETIME,而後存時間的時候一概用程序生成UTC時間(而不是local時區的時間)存進去,取出來的時候無論想顯示服務器時間仍是顯示用戶的時間均可以處理。

  • TIMESTAMP 使用32位來存儲,實際上存的是時間戳(因此範圍是1970~2038

MYSQL提供了FROM_UNIXTIME在輸出時格式化顯示時間(同時也幫你把時區加上去了)。同時,MySQL也提供了CURRENT_TIMESTAMP來自動維護created_at

具體用 int 仍是 TIMESTAMP 仍是 DATETIME ,看下面文章,本身考慮下。

www.jianshu.com/p/edfdaacc3…

blog.csdn.net/ppvqq/artic…

標識列的數據類型選擇

標識列就是相似自增ID這種,用於標識某一行數據的一個關鍵值。由於這些標識列常常會用於關聯操做,或者和其餘數據進行比較等,因此標識列的數據類型在選擇時必定要特別注意,以達到最佳的查詢效率。

下面是一些小技巧

  • 一旦選定了類型,與其比較或關聯的相關列的數據類型最好和標識列徹底同樣,包括unsigned這些屬性也最好同樣。
  • 選用int等整形永遠是最好的選擇。
  • 必定避免使用enum和set類型
  • 儘可能不要使用字符串類型。
  • 儘可能避免 NULL

一些技巧

使用緩存表和彙總表

爲了更快地讀,只能更慢地寫。有時提高性能最好的方法是在同一張表中保存衍生的冗餘數據。然而,有時也須要建立一張徹底獨立的彙總表或緩存表(特別是爲知足檢索的需求時)

  • 緩存表:通常用於「冗餘數據」,能夠從某個表中,花費必定的時間來生成。
  • 彙總表:通常是 group 操做的結果(如:收藏數量表就是一個彙總表)。

彙總表、計數表,可能會遇到寫瓶頸。此時,可使用「槽(slot)」,把每次新增的「+1」隨機地分配到某一行中,這樣就能夠將每次寫都鎖一行,變成每次寫,會從 N 個槽中選一個來寫。儘可能地避免了寫鎖等待。

其餘的一些技巧

約束性與併發控制

惟一索引

利用數據庫的機制,來幫咱們實現惟一性。能夠經過組合字段的惟一性,來達到惟 N 性等。

樂觀鎖

更新數據時,更新條件中帶上以前讀取的記錄的版本號(或重要字段的值)。

YII 的樂觀鎖支持

increase 與 decrease

當前取到count=4,要加 1 後存會數據庫 。count = count + 1count = 5 的區別

使用槽(slot)來減小鎖資源等待

若是要對一行數據進行頻繁的修改,可能會出現對這行數據的寫鎖等待。此時,考慮下可否把改一行,從業務邏輯上變成改多行。把寫操做分配到多行,減小單行的鎖等待。

存儲原始數據而非結果

好比,要獲取用戶的剩餘抽獎次數。此時,數據庫中,存已經用了的次數和總共有多少次抽獎次數 要優於只存一個 剩餘抽獎次數

===================第二部分 完=======================

第三部分 索引設計

這裏討論的是:MySQL 5.6 InnoDB BTREE 索引。只要把 MySQL InnoDB 中 BTREE 的樹結構理清了,就能本身推導出許多索引的設計準則。

索引簡介

索引其實就是一種數據結構。(哈希表、樹等等)不一樣類型的索引有着不一樣的數據結構和功能。

例如:語文課本,每篇文章都對應一個起始頁碼,全部的文章按照頁碼順序進行整理排版。此時,「文章按頁碼順序進行整理排版」就是索引的數據結構,書本的目錄就是索引文件。

索引類型

Mysql 5.6 InnoDB 提供了兩個類型的索引(Index_type)

  • BTREE
  • FULLTEXT

(Mysql 5.6 InnoDB 不支持手動使用 hash index(InnoDB 內部支持自適應哈希索引),也不支持 Geospatial index(5.7 後支持))

Clustered indexes 指彙集索引

可使用 BTREE 索引,來實現

  • 主鍵索引PRIMARY KEY
  • 惟一索引UNIQUE KEY
  • 普通索引KEY

(外鍵FOREIGN KEY的實現,也有部分依賴於 BTREE索引,建外鍵的時候,必需要求當前指定的列最少有個普通索引,否則的話,系統會自動幫你建一個。)

可使用 FULLTEXT 索引,來實現

  • 全文索引FULLTEXT KEY

單列索引與聯合索引

  • 單列索引,只使用一列來建索引
  • 聯合索引,使用多列來建索引
CREATE DATABASE `test_db` DEFAULT CHARSET utf8 COLLATE utf8_general_ci;

use test_db;

CREATE TABLE  `table_b`(
   `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
   `title` VARCHAR(100) NOT NULL,
   `name` VARCHAR(100) NOT NULL,
   `f_id` INT UNSIGNED NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY uk_name (`name`),
    KEY title (`title`),
    KEY title_name_fid (`title`,`name`,`f_id`),
    FULLTEXT KEY (`name`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;



SHOW INDEX FROM table_b;

+---------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name       | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| table_b |          0 | PRIMARY        |            1 | id          | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| table_b |          0 | uk_name        |            1 | name        | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| table_b |          1 | title          |            1 | title       | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| table_b |          1 | title_name_fid |            1 | title       | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| table_b |          1 | title_name_fid |            2 | name        | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| table_b |          1 | title_name_fid |            3 | f_id        | A         |           0 |     NULL | NULL   |      | BTREE      |         |               |
| table_b |          1 | name           |            1 | name        | NULL      |           0 |     NULL | NULL   |      | FULLTEXT   |         |               |
+---------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

複製代碼

索引的做用

  • 加速查詢速度
  • 維護數據的約束性(完整性、一致性)

對於加速查詢,使用索引不必定是最好的選擇。小表就直接全表掃描,中到大表就建索引,超大表就分區分表。其實主要就要索引帶來的好處和維護索引的成本之間的權衡。

BTREE 索引

在 MySQL 5.6 InnoDB 中,咱們平時建索引,只有 BTREE 這個選擇了。全文索引,通常咱們的業務場景不會用到。

B+ 樹簡介

B+樹和二叉樹、平衡二叉樹同樣,都是經典的數據結構。B+樹由B樹和索引順序訪問方法(ISAM,是否是很熟悉?對,這也是MyISAM引擎最初參考的數據結構)演化而來,可是在實際使用過程當中幾乎已經沒有使用B樹的狀況了。

B+樹的定義十分複雜,所以只簡要地介紹B+樹:B+樹是爲磁盤或其餘直接存取輔助設備而設計的一種平衡查找樹,在B+樹中,全部記錄節點都是按鍵值的大小順序存放在同一層的葉節點中,各葉節點指針進行鏈接。

特色:

  • 查找樹
  • 平衡
  • 數據都在葉子節點
  • 節點能夠包含多個數據
  • 葉子節點間有指針相連
  • 查找、插入、刪除近似於 O(lgn)

相關:

  • 局部性原理
  • I/O 是很慢的

爲何使用 B+樹 這種數據結構來做爲索引呢?

  • 查找樹,查找效率接近 O(lgn)
  • 平衡,每次查詢查找的次數基本相等
  • 索引數據在非葉子節點,能夠把索引數據都 load 進內存,加快查詢
  • 葉子節點點有指針相連,便於遍歷

在線 B+樹 生成器

Mysql InnoDB 的邏輯存儲結構

劃分邏輯結構的目的是便於管理,就像學校要劃分年級、班級、小組同樣。

  • 表空間(tablespace)
  • 段(segment)
  • 區(extent)
  • 頁(page)
  • 行(row)

  1. 頁是 B+樹 中一個一個節點(葉子節點或非葉子節點)。

  2. 頁有分索引頁和數據頁。索引頁,其中存放的就是非葉子節點的數據,數據頁存放的就是葉子節點的數據。每一個頁中,包含 1 個以上的行,行間經過指針按順序相連。咱們在搜索數據時,先定位到某個頁,再在頁內尋找想要的行。每頁默認 16KB。

  3. 一個區包括 64 個頁,區在申請磁盤時,是整塊整塊申請的,因此,一個區中的數據,在物理上是連續的。 (64 個頁 × 一個頁 16kb = 一個區 1M)。通常內存都有能力把一個 B+樹 索引中的因此非葉子節點所有 load 進內存中進行管理。因此,在內存中維護這個 B+樹的 非葉子節點時,通常開銷都相對較小。可是,維護葉子節點的話,通常都要磁盤 io 了,所以整個葉子數據通常不能所有 load 進內存。

  4. 表空間就像是一個文件夾,段就是文件夾中的一個一個文件。新建一個索引時,就會新建兩個段數據,一個只存索引(索引段),一個只存數據(數據段)。

  5. 表空間就是一個表的數據了。

Mysql InnoDB 的邏輯存儲結構有 3 個有意思的地方:

  • 段,是爲了區分非葉子數據和葉子數據。便於把整個非葉子數據 load 進內存。
  • 區,是一個連續的磁盤空間。
  • 頁,頁內也是一個連續的空間。

類比:

  • 中心,劃分中心,是爲了更好地管理有相同職能技能的員工。
  • 業務線,業務線中包含好多小組,各個小組坐在附近,能便於交流。
  • 小組,小組中包含員工,同一小組的員工坐在一塊兒,能便於交流。

從邏輯結構,咱們能夠知道:

  • 連續性
  • 葉子節點與非葉子節點的分開管理

MySQL InnoDB BTREE 總覽

經過上面圖片,咱們能推導出下面這些結論

  • 全值匹配
  • 匹配最左前綴
  • 匹配列前綴
  • 匹配範圍值
  • 精確匹配某一列並範圍匹配另一列
  • 第一個範圍查詢字段後面的段索引字都只能用於過濾。

索引中的字段能夠有兩個做用:

  • 肯定索引片起始和終止位置
  • 過濾,比較篩選

彙集索引與輔助索引

彙集索引

在 MySQL InnoDB 中,每行記錄,必有一個主鍵。官方推薦使用業務無關的整形無符號自增非空類型做爲主鍵。 若是沒有自定義主鍵,系統會根據如下規則來選取主鍵

  • 看有沒有單個的非空的惟一索引,有的話,就用這個做爲主鍵。有多個的話,就選擇建表語句中,符合條件的第一個字段。
  • 若無的話,就本身維護一個6個字節的空間做爲主鍵。詳情請見

擴展:爲啥要本身定義自增非空int

  • 6字節。本身可能用不到那麼大
  • 順序。減小頁分裂。

輔助索引

其餘的索引就是輔助索引(惟一索引、普通索引等)。輔助索引中,葉子節點存的是記錄的主鍵值。經過主鍵值再去彙集索引查實際的值。

建議在業務要求的輔助索引字段後面補上主鍵字段?

這裏加和不加,從實驗上看不出區別。應該是優化器幫咱們處理好了(優化器是很強大的,優化器的代碼更新地很快,特性不少,要相信優化器)。

爲何輔助索引的葉子節點不直接存錶行記錄實際的物理指針(頁號等),而是要去彙集索引那邊再查一次?

若是這樣的話,若是彙集索引中,頁的分裂,將會致使數據的物理結構發生變化。若是輔助索引存的仍是物理信息,那麼就還要去更新輔助索引中的數據,那就會產生許多的額外操做。而目前這種結構,咱們要查兩次,可是這個負擔不是很嚴重,由於非葉子節點都在內存中,查起來很快的。

三星索引設計規範

如何爲一條 SQL 語句設計索引。

覆蓋索引

無需訪問彙集索引,經過輔助索引就能完成需求。

  • 要select的字段都在索引中。
  • select count(*) 由於咱們只要一個數字就行了,因此也是隻須要訪問輔助索引就行了,因此也算是覆蓋索引。

由於一般輔助索引都比彙集索引要小(輔助索引的葉子節點頁中,一個頁能包含更多的列記錄)。

如何優化count(*)

特別是帶條件的計數時,確定要掃描的,沒有系統的統計信息能夠直接拿。因此,要不就使用覆蓋索引,若是仍是慢的話,就用以前設計表時說的彙總表,設置多個槽防止鎖表。

咱們說的全表掃描,就是指遍歷彙集索引的葉子節點

三星索引的定義:

  • 第一顆星:儘可能縮短將要被掃描的葉子節點範圍(起始位和終止位之間的間隔儘可能小)。
  • 第二顆星:避免把葉子節點的數據 load 進內存中的排序操做。使用 B+樹 索引幫咱們提早排好。
  • 第三顆星:避免減小回彙集索引查詢,經過輔助索引就解決戰鬥。

設計步驟

一般,第三顆星咱們都能達到,即,使用覆蓋索引來避免回彙集索引查找的過程,能夠減小不少回表的 I/O。

若是,查詢的 where 條件中都是等值查詢(或沒有排序的話),那麼咱們能完成知足 3 顆星的要求。

若是,查詢的 where 條件中存在範圍查詢,且有排序的須要,那麼咱們就只要在(第一顆星 + 第三顆星)和(第二顆星 + 第三顆星)這二者間選擇了。

候選A

(第一顆星 + 第三顆星)追求窄,掃描最少的索引片

  1. 取出對於等值條件的列,將這些列做爲索引的前導列,任意順序皆可。(選擇性高的靠前會好點,便於其餘查詢複用這個是索引)
  2. 將選擇性最好的範圍條件做爲索引的下一列。
  3. 以正確的順序添加 order by列。忽略上面兩步已經添加過的列。
  4. 以任意順序將select語句中的其餘列添加到索引中,已不易變列開始。

候選B

(第二顆星 + 第三顆星)追求不用排序

  1. 取出對於等值條件的列,將這些列做爲索引的前導列,任意順序皆可。(選擇性高的靠前會好點,便於其餘查詢複用這個是索引)
  2. 以正確的順序添加 order by列。忽略上面兩步已經添加過的列。
  3. 以任意順序將 select 語句中的其餘列添加到索引中,以不易變列開始。

有排序且有範圍查詢時,才考慮選擇 候選A 或 候選B,其餘狀況,都能知足三星索引。

排序指:order by

選擇 候選A 仍是 候選B,就是判斷:load 進內存排序的成本大,仍是一個一個從頭遍歷的篩選的成本大。這種沒有定性的答案,須要根據數據的特性以及要去取怎樣的數據決定。

select A,B from user where A > a order by B;

假設總共有 n 條記錄,知足條件 A > a 的有 m 條,每條數據一次i/o

  • 候選A :n->m, 而後 i/o m 條數據進入內存進行排序,耗時O(mlgm),即,耗時爲:mi/o+ mlgm次比較
  • 候選B :i/o n 條數據,進入內存中比較,比較次數爲1~n,即,耗時爲:1~n次[比較+i/o時間]。(由於若是隻需取 1 條數據,能夠提早退出,因此 1~n

假如 m 很大,大到接近 n 的話,那麼 候選B 好。 假如 m 很小,小到接近 1 的話,那麼 候選A 好。

==================第三部分 完====================

第四部分 實踐測試

硬件配置

  • 騰訊雲 CDB
  • 5.6.28-cdb2016-log 20180122
  • 高IO版,內存1000MB,硬盤25GB,1000次/秒

MySQL 主要配置信息

show variables like '%query_cache%';
+------------------------------+---------+
| Variable_name                | Value   |
+------------------------------+---------+
| have_query_cache             | YES     |
| query_cache_limit            | 1048576 |
| query_cache_min_res_unit     | 4096    |
| query_cache_size             | 0       |
| query_cache_type             | OFF     |
| query_cache_wlock_invalidate | OFF     |
+------------------------------+---------+
6 rows in set (0.01 sec)
複製代碼
  • 每頁大小 16KB
show variables like '%innodb_page%';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| innodb_page_size | 16384 |
+------------------+-------+
複製代碼
show variables like '%innodb_buffer_pool%';
+-------------------------------------+----------------+
| Variable_name                       | Value          |
+-------------------------------------+----------------+
| innodb_buffer_pool_dump_at_shutdown | OFF            |
| innodb_buffer_pool_dump_now         | OFF            |
| innodb_buffer_pool_filename         | ib_buffer_pool |
| innodb_buffer_pool_instances        | 8              |
| innodb_buffer_pool_load_abort       | OFF            |
| innodb_buffer_pool_load_at_startup  | OFF            |
| innodb_buffer_pool_load_now         | OFF            |
| innodb_buffer_pool_size             | 936378368      |
+-------------------------------------+----------------+
複製代碼

測試數據庫初始狀態

  • 數量:500W
  • 主鍵:int 型自增主鍵
CREATE TABLE `user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `sex` tinyint(3) unsigned NOT NULL,
  `age` tinyint(3) unsigned NOT NULL,
  `email` varchar(255) NOT NULL,
  `address` varchar(350) NOT NULL,
  `company` varchar(255) NOT NULL,
  `city` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8


select count(*) from user;
+----------+
| count(*) |
+----------+
|  5037343 |
+----------+
1 row in set (1.81 sec)


select * from user limit 1 \G
*************************** 1. row ***************************
     id: 1
   name: Prof. Osborne Waelchi I
    sex: 0
    age: 60
  email: dach.angela@yahoo.com
address: 35712 Quigley Mountains North Alysonville, CO 53682-2718
company: McGlynn Ltd
   city: Port Maziebury
1 row in set (0.01 sec)
複製代碼

實驗案例

索引相關

運算後的列,不能使用索引
MySQL [test_db_for_index]> show index from user;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY  |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.01 sec)

MySQL [test_db_for_index]> explain select * from user where id+1=2;
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | user  | ALL  | NULL          | NULL | NULL    | NULL | 4870574 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
1 row in set (0.04 sec)

MySQL [test_db_for_index]> select * from user where id+1=2;
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
| id | name                    | sex | age | email                 | address                                                   | company     | city           |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
|  1 | Prof. Osborne Waelchi I |   0 |  60 | dach.angela@yahoo.com | 35712 Quigley Mountains
North Alysonville, CO 53682-2718 | McGlynn Ltd | Port Maziebury |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
1 row in set (3.04 sec)

MySQL [test_db_for_index]> select * from user where id=1;
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
| id | name                    | sex | age | email                 | address                                                   | company     | city           |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
|  1 | Prof. Osborne Waelchi I |   0 |  60 | dach.angela@yahoo.com | 35712 Quigley Mountains
North Alysonville, CO 53682-2718 | McGlynn Ltd | Port Maziebury |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
1 row in set (0.01 sec)

複製代碼
列字段的前綴性與 IN 繞過技巧
MySQL [test_db_for_index]> show index from user;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY  |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | sex_name |            1 | sex         | A         |           2 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | sex_name |            2 | name        | A         |     2435287 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.02 sec)

MySQL [test_db_for_index]> desc select * from user where name='Prof. Osborne Waelchi I';
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | user  | ALL  | NULL          | NULL | NULL    | NULL | 4870574 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
1 row in set (0.01 sec)

MySQL [test_db_for_index]> select * from user where name='Prof. Osborne Waelchi I';
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
| id | name                    | sex | age | email                 | address                                                   | company     | city           |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
|  1 | Prof. Osborne Waelchi I |   0 |  60 | dach.angela@yahoo.com | 35712 Quigley Mountains
North Alysonville, CO 53682-2718 | McGlynn Ltd | Port Maziebury |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
1 row in set (3.11 sec)

MySQL [test_db_for_index]> explain select * from user where name='Prof. Osborne Waelchi I' and sex in (0,1);
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------+
|  1 | SIMPLE      | user  | range | sex_name      | sex_name | 768     | NULL |    2 | Using index condition |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------+
1 row in set (0.01 sec)

MySQL [test_db_for_index]> select * from user where name='Prof. Osborne Waelchi I' and sex in (0,1);
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
| id | name                    | sex | age | email                 | address                                                   | company     | city           |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
|  1 | Prof. Osborne Waelchi I |   0 |  60 | dach.angela@yahoo.com | 35712 Quigley Mountains
North Alysonville, CO 53682-2718 | McGlynn Ltd | Port Maziebury |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
1 row in set (0.01 sec)
複製代碼
多列索引的部分使用
MySQL [test_db_for_index]> show index from user;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY  |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.01 sec)


MySQL [test_db_for_index]> desc select *  from user where name='Prof. Osborne Waelchi I' and age=60;
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | user  | ALL  | NULL          | NULL | NULL    | NULL | 4870574 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
1 row in set (0.03 sec)

MySQL [test_db_for_index]> desc select *  from user where name='Prof. Osborne Waelchi I';
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | user  | ALL  | NULL          | NULL | NULL    | NULL | 4870574 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
1 row in set (0.02 sec)

MySQL [test_db_for_index]> select *  from user where name='Prof. Osborne Waelchi I' and age=60;;
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
| id | name                    | sex | age | email                 | address                                                   | company     | city           |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
|  1 | Prof. Osborne Waelchi I |   0 |  60 | dach.angela@yahoo.com | 35712 Quigley Mountains
North Alysonville, CO 53682-2718 | McGlynn Ltd | Port Maziebury |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
1 row in set (3.03 sec)

ERROR: No query specified

MySQL [test_db_for_index]> select *  from user where name='Prof. Osborne Waelchi I';
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
| id | name                    | sex | age | email                 | address                                                   | company     | city           |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
|  1 | Prof. Osborne Waelchi I |   0 |  60 | dach.angela@yahoo.com | 35712 Quigley Mountains
North Alysonville, CO 53682-2718 | McGlynn Ltd | Port Maziebury |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
1 row in set (3.03 sec)


MySQL [test_db_for_index]> show index from user;
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name       | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY        |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name_age_email |            1 | name        | A         |     1623524 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name_age_email |            2 | age         | A         |     2435287 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name_age_email |            3 | email       | A         |     2435287 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.02 sec)


MySQL [test_db_for_index]> desc select *  from user where name='Prof. Osborne Waelchi I';
+----+-------------+-------+------+----------------+----------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys  | key            | key_len | ref   | rows | Extra                 |
+----+-------------+-------+------+----------------+----------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | user  | ref  | name_age_email | name_age_email | 767     | const |    1 | Using index condition |
+----+-------------+-------+------+----------------+----------------+---------+-------+------+-----------------------+
1 row in set (0.01 sec)

MySQL [test_db_for_index]> select *  from user where name='Prof. Osborne Waelchi I';
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
| id | name                    | sex | age | email                 | address                                                   | company     | city           |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
|  1 | Prof. Osborne Waelchi I |   0 |  60 | dach.angela@yahoo.com | 35712 Quigley Mountains
North Alysonville, CO 53682-2718 | McGlynn Ltd | Port Maziebury |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
1 row in set (0.01 sec)

MySQL [test_db_for_index]> select *  from user where name='Prof. Osborne Waelchi I' and age=60;;
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
| id | name                    | sex | age | email                 | address                                                   | company     | city           |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
|  1 | Prof. Osborne Waelchi I |   0 |  60 | dach.angela@yahoo.com | 35712 Quigley Mountains
North Alysonville, CO 53682-2718 | McGlynn Ltd | Port Maziebury |
+----+-------------------------+-----+-----+-----------------------+-----------------------------------------------------------+-------------+----------------+
1 row in set (0.01 sec)

ERROR: No query specified

MySQL [test_db_for_index]> desc select *  from user where name='Prof. Osborne Waelchi I' and age=60;
+----+-------------+-------+------+----------------+----------------+---------+-------------+------+-----------------------+
| id | select_type | table | type | possible_keys  | key            | key_len | ref         | rows | Extra                 |
+----+-------------+-------+------+----------------+----------------+---------+-------------+------+-----------------------+
|  1 | SIMPLE      | user  | ref  | name_age_email | name_age_email | 768     | const,const |    1 | Using index condition |
+----+-------------+-------+------+----------------+----------------+---------+-------------+------+-----------------------+
1 row in set (0.04 sec)

複製代碼
單索引的部分使用
MySQL [test_db_for_index]> show index from user;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY  |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.02 sec)


MySQL [test_db_for_index]> desc select *  from user where name like 'Prof. Osborne W%';
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | user  | ALL  | NULL          | NULL | NULL    | NULL | 4870574 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
1 row in set (0.02 sec)


MySQL [test_db_for_index]> select *  from user where name like 'Prof. Osborne W%';
+---------+--------------------------+-----+-----+--------------------------+-----------------------------------------------------------+----------------+------------------+
| id      | name                     | sex | age | email                    | address                                                   | company        | city             |
+---------+--------------------------+-----+-----+--------------------------+-----------------------------------------------------------+----------------+------------------+
|       1 | Prof. Osborne Waelchi I  |   0 |  60 | dach.angela@yahoo.com    | 35712 Quigley Mountains
North Alysonville, CO 53682-2718 | McGlynn Ltd    | Port Maziebury   |
|  798465 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 1167101 | Prof. Osborne Weissnat V |   0 |  74 | monserrat36@shanahan.com | 77818 Rohan Throughway
Koelpinmouth, VA 66568-0775        | Stark-Anderson | Oberbrunnershire |
| 1660173 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 2160173 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 2660173 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 3160173 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 3528809 | Prof. Osborne Weissnat V |   0 |  74 | monserrat36@shanahan.com | 77818 Rohan Throughway
Koelpinmouth, VA 66568-0775        | Stark-Anderson | Oberbrunnershire |
| 4021968 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 4521968 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 5021968 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
+---------+--------------------------+-----+-----+--------------------------+-----------------------------------------------------------+----------------+------------------+
11 rows in set (3.30 sec)



MySQL [test_db_for_index]> show index from user;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY  |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name     |            1 | name        | A         |     1623524 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.02 sec)

MySQL [test_db_for_index]> desc select *  from user where name like 'Prof. Osborne W%';
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
|  1 | SIMPLE      | user  | range | name          | name | 767     | NULL |   11 | Using index condition |
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
1 row in set (0.02 sec)

MySQL [test_db_for_index]> select *  from user where name like 'Prof. Osborne W%';
+---------+--------------------------+-----+-----+--------------------------+-----------------------------------------------------------+----------------+------------------+
| id      | name                     | sex | age | email                    | address                                                   | company        | city             |
+---------+--------------------------+-----+-----+--------------------------+-----------------------------------------------------------+----------------+------------------+
|       1 | Prof. Osborne Waelchi I  |   0 |  60 | dach.angela@yahoo.com    | 35712 Quigley Mountains
North Alysonville, CO 53682-2718 | McGlynn Ltd    | Port Maziebury   |
|  798465 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 1660173 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 2160173 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 2660173 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 3160173 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 4021968 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 4521968 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 5021968 | Prof. Osborne Weimann I  |   0 |  79 | santino92@spinka.com     | 64416 Lia Mills Apt. 784
Kleinburgh, MI 09030-1298        | Green PLC      | Hesselhaven      |
| 1167101 | Prof. Osborne Weissnat V |   0 |  74 | monserrat36@shanahan.com | 77818 Rohan Throughway
Koelpinmouth, VA 66568-0775        | Stark-Anderson | Oberbrunnershire |
| 3528809 | Prof. Osborne Weissnat V |   0 |  74 | monserrat36@shanahan.com | 77818 Rohan Throughway
Koelpinmouth, VA 66568-0775        | Stark-Anderson | Oberbrunnershire |
+---------+--------------------------+-----+-----+--------------------------+-----------------------------------------------------------+----------------+------------------+
11 rows in set (0.04 sec)
複製代碼
同字段的等值 or,新版優化器會優化成 in,可使用索引
MySQL [test_db_for_index]> show index from user;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY  |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name     |            1 | name        | A         |     1623524 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.01 sec)


MySQL [test_db_for_index]> desc select * from user where name='Prof. Osborne Waelchi I' or name='Zaria Quigley';
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
|  1 | SIMPLE      | user  | range | name          | name | 767     | NULL |    2 | Using index condition |
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
1 row in set (0.02 sec)

MySQL [test_db_for_index]> desc select * from user where name in('Prof. Osborne Waelchi I','Zaria Quigley');
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
|  1 | SIMPLE      | user  | range | name          | name | 767     | NULL |    2 | Using index condition |
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
1 row in set (0.01 sec)


MySQL [test_db_for_index]> select * from user where name in('Prof. Osborne Waelchi I','Zaria Quigley');
+----+-------------------------+-----+-----+------------------------+-----------------------------------------------------------+------------------+----------------+
| id | name                    | sex | age | email                  | address                                                   | company          | city           |
+----+-------------------------+-----+-----+------------------------+-----------------------------------------------------------+------------------+----------------+
|  1 | Prof. Osborne Waelchi I |   0 |  60 | dach.angela@yahoo.com  | 35712 Quigley Mountains
North Alysonville, CO 53682-2718 | McGlynn Ltd      | Port Maziebury |
|  4 | Zaria Quigley           |   0 |  41 | ryan.anissa@cronin.com | 799 Barney Cove
Princessland, VA 34382                    | Farrell-Hartmann | DuBuqueport    |
+----+-------------------------+-----+-----+------------------------+-----------------------------------------------------------+------------------+----------------+
2 rows in set (0.01 sec)

MySQL [test_db_for_index]> select * from user where name='Prof. Osborne Waelchi I' or name='Zaria Quigley';
+----+-------------------------+-----+-----+------------------------+-----------------------------------------------------------+------------------+----------------+
| id | name                    | sex | age | email                  | address                                                   | company          | city           |
+----+-------------------------+-----+-----+------------------------+-----------------------------------------------------------+------------------+----------------+
|  1 | Prof. Osborne Waelchi I |   0 |  60 | dach.angela@yahoo.com  | 35712 Quigley Mountains
North Alysonville, CO 53682-2718 | McGlynn Ltd      | Port Maziebury |
|  4 | Zaria Quigley           |   0 |  41 | ryan.anissa@cronin.com | 799 Barney Cove
Princessland, VA 34382                    | Farrell-Hartmann | DuBuqueport    |
+----+-------------------------+-----+-----+------------------------+-----------------------------------------------------------+------------------+----------------+
2 rows in set (0.01 sec)

複製代碼
不一樣字段的or的優化,使用兩個單列索引組合,使用 union 也是一樣效果,並不會優化。
MySQL [test_db_for_index]> show index from user;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY  |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name     |            1 | name        | A         |     1623524 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | email    |            1 | email       | A         |     1623524 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.01 sec)

MySQL [test_db_for_index]> desc select * from user where name='1' or email='d';
+----+-------------+-------+-------------+---------------+------------+---------+------+------+--------------------------------------+
| id | select_type | table | type        | possible_keys | key        | key_len | ref  | rows | Extra                                |
+----+-------------+-------+-------------+---------------+------------+---------+------+------+--------------------------------------+
|  1 | SIMPLE      | user  | index_merge | name,email    | name,email | 767,767 | NULL |    2 | Using union(name,email); Using where |
+----+-------------+-------+-------------+---------------+------------+---------+------+------+--------------------------------------+
1 row in set (0.01 sec)

MySQL [test_db_for_index]> select * from user where name='1' or email='d';
Empty set (0.02 sec)


MySQL [test_db_for_index]> select * from user where name='1' union select * from user where email='d';
Empty set (0.01 sec)

MySQL [test_db_for_index]> desc select * from user where name='1' union select * from user where email='d';
+----+--------------+------------+------+---------------+-------+---------+-------+------+-----------------------+
| id | select_type  | table      | type | possible_keys | key   | key_len | ref   | rows | Extra                 |
+----+--------------+------------+------+---------------+-------+---------+-------+------+-----------------------+
|  1 | PRIMARY      | user       | ref  | name          | name  | 767     | const |    1 | Using index condition |
|  2 | UNION        | user       | ref  | email         | email | 767     | const |    1 | Using index condition |
| NULL | UNION RESULT | <union1,2> | ALL  | NULL          | NULL  | NULL    | NULL  | NULL | Using temporary       |
+----+--------------+------------+------+---------------+-------+---------+-------+------+-----------------------+
3 rows in set (0.01 sec)
複製代碼
儘可能減小範圍條件。(>、<、!=、not in、between、not between),能轉成已知的,都轉已知的
## sex字段只有 0 1 兩個取值
MySQL [test_db_for_index]> show index from user;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY  |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | sex_name |            1 | sex         | A         |           2 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | sex_name |            2 | name        | A         |     2435287 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.01 sec)

MySQL [test_db_for_index]> desc select * from user where sex!=1 and name='payton';
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | user  | ALL  | sex_name      | NULL | NULL    | NULL | 4870574 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
1 row in set (0.01 sec)

MySQL [test_db_for_index]> select * from user where sex!=1 and name='payton';
Empty set (3.18 sec)

MySQL [test_db_for_index]> desc select * from user where sex=0 and name='payton';
+----+-------------+-------+------+---------------+----------+---------+-------------+------+-----------------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref         | rows | Extra                 |
+----+-------------+-------+------+---------------+----------+---------+-------------+------+-----------------------+
|  1 | SIMPLE      | user  | ref  | sex_name      | sex_name | 768     | const,const |    1 | Using index condition |
+----+-------------+-------+------+---------------+----------+---------+-------------+------+-----------------------+
1 row in set (0.02 sec)

MySQL [test_db_for_index]> select * from user where sex=0 and name='payton';
Empty set (0.02 sec)

複製代碼
聯合索引中,範圍搜索字段後面的字段不能再決定索引片寬度,只能用於篩選。使用 IN 優化
MySQL [test_db_for_index]> show index from user;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY  |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | age_name |            1 | age         | A         |         136 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | age_name |            2 | name        | A         |     2435287 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.01 sec)

MySQL [test_db_for_index]> desc select * from user where age >= 10 and age <= 15 and name='payton';
+----+-------------+-------+-------+---------------+----------+---------+------+--------+-----------------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows   | Extra                 |
+----+-------------+-------+-------+---------------+----------+---------+------+--------+-----------------------+
|  1 | SIMPLE      | user  | range | age_name      | age_name | 768     | NULL | 626654 | Using index condition |
+----+-------------+-------+-------+---------------+----------+---------+------+--------+-----------------------+
1 row in set (0.01 sec)

MySQL [test_db_for_index]> select * from user where age >= 10 and age <= 15 and name='payton';
Empty set (0.09 sec)

MySQL [test_db_for_index]> desc select * from user where age in (10,11,12,13,14,15) and name='payton';
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------+
|  1 | SIMPLE      | user  | range | age_name      | age_name | 768     | NULL |    6 | Using index condition |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------+
1 row in set (0.01 sec)

MySQL [test_db_for_index]> select * from user where age in (10,11,12,13,14,15) and name='payton';
Empty set (0.02 sec)

複製代碼
覆蓋索引更優

由於這裏的 InnoDB 的緩存池太大了,有接近 900M 的內存。徹底有能力把輔助索引的葉子和非葉子節點所有 load 進內存。同理,彙集索引也是。由於這裏沒有 io 的差距,因此差異就是一頁的記錄行數據多少了。因此這裏的差異不是很大。可是,若是不能徹底把彙集索引非葉子節點所有進內存的話,這裏的差距會更大。

MySQL [test_db_for_index]> desc select count(*) from user;
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows    | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+
|  1 | SIMPLE      | user  | index | NULL          | PRIMARY | 4       | NULL | 4870574 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+
1 row in set (0.02 sec)

MySQL [test_db_for_index]> desc select count(*) from user;
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows    | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+
|  1 | SIMPLE      | user  | index | NULL          | PRIMARY | 4       | NULL | 4870574 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+
1 row in set (0.01 sec)

MySQL [test_db_for_index]> select count(*) from user;
+----------+
| count(*) |
+----------+
|  5037343 |
+----------+
1 row in set (1.69 sec)


MySQL [test_db_for_index]> show index from user;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY  |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | sex      |            1 | sex         | A         |           2 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.01 sec)

MySQL [test_db_for_index]> desc select count(*) from user;
+----+-------------+-------+-------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-------+-------+---------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | user  | index | NULL          | sex  | 1       | NULL | 4870574 | Using index |
+----+-------------+-------+-------+---------------+------+---------+------+---------+-------------+
1 row in set (0.01 sec)

MySQL [test_db_for_index]> select count(*) from user;
+----------+
| count(*) |
+----------+
|  5037343 |
+----------+
1 row in set (0.67 sec)

複製代碼
InnoDB 緩存池的大小,對查詢的影響

這個實驗的環境和其餘的不同。

緩存池是存儲引擎實現的。在 MySQL InnoDB 中,能夠經過innodb_buffer_pool_size參數來定義緩存池的大小。

緩存池經過LRU策略進行維護。若數據庫中的數據能夠徹底存放於緩存池中,則能夠認爲,此時數據庫的性能是最佳的了。除了同步或異步的寫磁盤操做外,全部其餘操做均可以在內存中完成。

下面是18G的數據,隨着緩存池的變大,TPS的變化狀況。18G數據,存到內存要比18G大一點,由於還有其餘的開銷。

由於有了緩存池,一些熱點的數據,就能夠自動躺在緩存池中了,這樣,就快了。

磁盤與硬盤的隨機讀寫和順序讀寫

順序讀取是指順序地讀取磁盤上的頁。隨機讀取是指訪問的頁不是連續的,須要磁盤的磁頭不停地移動。

注意,這裏指的順序,指的是大塊內部是順序地,大塊與大塊間能夠是不連續的。由於很難保證能申請到一塊幾十G的連續空間。

在 MySQL InnoDB 中,頁是經過區來進行管理的,每次申請存儲時,會申請一塊連續的區,其中包括64個頁。因此,能夠保證這64個頁是連續的,可是區與區間就不保證連續了。

固態硬盤雖然物理結構和磁盤不同,可是也是準守上面的原則的。順序讀仍是會比隨機讀快。

緩存的做用,系統把random_digit索引的全部葉子節點都緩存到內存中了

MariaDB [big_tables]> show index from custom;
+--------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table  | Non_unique | Key_name     | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| custom |          0 | PRIMARY      |            1 | id           | A         |     1240315 |     NULL | NULL   |      | BTREE      |         |               |
| custom |          1 | email        |            1 | email        | A         |     1240315 |      255 | NULL   | YES  | BTREE      |         |               |
| custom |          1 | name         |            1 | name         | A         |     1240315 |      255 | NULL   | YES  | BTREE      |         |               |
| custom |          1 | random_digit |            1 | random_digit | A         |          20 |     NULL | NULL   | YES  | BTREE      |         |               |
+--------+------------+--------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.00 sec)

MariaDB [big_tables]> select count(*) from custom;
+----------+
| count(*) |
+----------+
|  1158255 |
+----------+
1 row in set (1.39 sec)

MariaDB [big_tables]> select count(*) from custom;
+----------+
| count(*) |
+----------+
|  1158255 |
+----------+
1 row in set (0.23 sec)

MariaDB [big_tables]> select count(*) from custom;
+----------+
| count(*) |
+----------+
|  1158255 |
+----------+
1 row in set (0.23 sec)

MariaDB [big_tables]> select count(*) from custom;
+----------+
| count(*) |
+----------+
|  1158255 |
+----------+
1 row in set (0.25 sec)

MariaDB [big_tables]> select count(*) from custom;
+----------+
| count(*) |
+----------+
|  1158255 |
+----------+
1 row in set (0.22 sec)

MariaDB [big_tables]> desc select count(*) from custom;
+------+-------------+--------+-------+---------------+--------------+---------+------+---------+-------------+
| id   | select_type | table  | type  | possible_keys | key          | key_len | ref  | rows    | Extra       |
+------+-------------+--------+-------+---------------+--------------+---------+------+---------+-------------+
|    1 | SIMPLE      | custom | index | NULL          | random_digit | 7       | NULL | 1240315 | Using index |
+------+-------------+--------+-------+---------------+--------------+---------+------+---------+-------------+
1 row in set (0.00 sec)


複製代碼
優化器是很厲害的
MySQL [test_db_for_index]> show index from user;
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name       | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY        |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | age_name_email |            1 | age         | A         |       29698 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | age_name_email |            2 | name        | A         |     2435287 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | age_name_email |            3 | email       | A         |     2435287 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name_age_email |            1 | name        | A         |     1623524 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name_age_email |            2 | age         | A         |     1623524 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name_age_email |            3 | email       | A         |     2435287 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
7 rows in set (0.01 sec)


MySQL [test_db_for_index]> desc select age,name,email from user where age>1 order by name limit 1;
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys  | key            | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | user  | index | age_name_email | name_age_email | 1535    | NULL |    2 | Using where; Using index |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
1 row in set (0.01 sec)

MySQL [test_db_for_index]> desc select age,name,email from user where age>50 order by name limit 1;
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys  | key            | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | user  | index | age_name_email | name_age_email | 1535    | NULL |    2 | Using where; Using index |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
1 row in set (0.01 sec)


MySQL [test_db_for_index]> desc select age,name,email from user where age>70 order by name limit 1;
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys  | key            | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | user  | index | age_name_email | name_age_email | 1535    | NULL |    3 | Using where; Using index |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+


MySQL [test_db_for_index]> desc select age,name,email from user where age>80 order by name limit 1;
+----+-------------+-------+-------+----------------+----------------+---------+------+------+------------------------------------------+
| id | select_type | table | type  | possible_keys  | key            | key_len | ref  | rows | Extra                                    |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+------------------------------------------+
|  1 | SIMPLE      | user  | range | age_name_email | age_name_email | 1       | NULL |    1 | Using where; Using index; Using filesort |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+------------------------------------------+
1 row in set (0.02 sec)


MySQL [test_db_for_index]> desc select age,name,email from user where age>580 order by name limit 1;
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                                               |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
|  1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible WHERE noticed after reading const tables |
+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+
1 row in set (0.02 sec)
複製代碼
Cardinality 與字段選擇性
MySQL [test_db_for_index]> show index from user;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY  |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | sex      |            1 | sex         | A         |           2 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.01 sec)


MySQL [test_db_for_index]> select count(distinct sex)/count(*) from user;
+------------------------------+
| count(distinct sex)/count(*) |
+------------------------------+
|                       0.0000 |  # 其實不是0,不夠很接近
+------------------------------+
1 row in set (1.88 sec)


MySQL [test_db_for_index]> select count(distinct name)/count(*) from user;
+-------------------------------+
| count(distinct name)/count(*) |
+-------------------------------+
|                        0.1592 |
+-------------------------------+
1 row in set (6.13 sec)

MySQL [test_db_for_index]> select count(distinct left(email,5))/count(*) from user;
+----------------------------------------+
| count(distinct left(email,5))/count(*) |
+----------------------------------------+
|                                 0.0049 |
+----------------------------------------+
1 row in set (4.12 sec)


MySQL [test_db_for_index]> select count(distinct left(email,15))/count(*) from user;
+-----------------------------------------+
| count(distinct left(email,15))/count(*) |
+-----------------------------------------+
|                                  0.1545 | # 這個最省空間。不過要注意,截斷後,就不能使用覆蓋索引了,必需要回彙集索引才能拿到當前列完整的內容
+-----------------------------------------+
1 row in set (5.74 sec)


MySQL [test_db_for_index]> select count(distinct email)/count(*) from user;
+--------------------------------+
| count(distinct email)/count(*) |
+--------------------------------+
|                         0.1586 |
+--------------------------------+
1 row in set (5.66 sec)

複製代碼
候選A 仍是 候選B
MySQL [test_db_for_index]> show index from user;
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name       | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY        |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | age_name_email |            1 | age         | A         |       29698 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | age_name_email |            2 | name        | A         |     2435287 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | age_name_email |            3 | email       | A         |     2435287 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name_age_email |            1 | name        | A         |     1623524 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name_age_email |            2 | age         | A         |     1623524 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name_age_email |            3 | email       | A         |     2435287 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
7 rows in set (0.01 sec)

MySQL [test_db_for_index]> select age,name,email from user where age > 18 order by name limit 1;
+-----+--------------------+-----------------+
| age | name               | email           |
+-----+--------------------+-----------------+
|  60 | Aaliyah Altenwerth | grice@yahoo.com |
+-----+--------------------+-----------------+
1 row in set (0.01 sec)

MySQL [test_db_for_index]> select age,name,email from user force index(age_name_email) where age > 18 order by name limit 1;
+-----+--------------------+-----------------+
| age | name               | email           |
+-----+--------------------+-----------------+
|  60 | Aaliyah Altenwerth | grice@yahoo.com |
+-----+--------------------+-----------------+
1 row in set (3.11 sec)

MySQL [test_db_for_index]> select age,name,email from user force index(name_age_email) where age > 18 order by name limit 1;
+-----+--------------------+-----------------+
| age | name               | email           |
+-----+--------------------+-----------------+
|  60 | Aaliyah Altenwerth | grice@yahoo.com |
+-----+--------------------+-----------------+
1 row in set (0.00 sec)


MySQL [test_db_for_index]> select age,name,email from user force index(name_age_email) where age > 18 order by name limit 100000,10;
+-----+--------------------+-------------------------------+
| age | name               | email                         |
+-----+--------------------+-------------------------------+
|  20 | Alexandrea Deckow  | lueilwitz.barry@hermiston.com |
|  20 | Alexandrea Deckow  | lueilwitz.barry@hermiston.com |
|  20 | Alexandrea Deckow  | lueilwitz.barry@hermiston.com |
|  20 | Alexandrea Deckow  | lueilwitz.barry@hermiston.com |
|  20 | Alexandrea Deckow  | lueilwitz.barry@hermiston.com |
|  20 | Alexandrea Deckow  | lueilwitz.barry@hermiston.com |
|  47 | Alexandrea Denesik | loy.larkin@durgan.com         |
|  47 | Alexandrea Denesik | loy.larkin@durgan.com         |
|  28 | Alexandrea Dibbert | rae61@gerhold.info            |
|  28 | Alexandrea Dibbert | rae61@gerhold.info            |
+-----+--------------------+-------------------------------+
10 rows in set (0.06 sec)


MySQL [test_db_for_index]> select age,name,email from user force index(age_name_email) where age > 18 order by name limit 100000,10;
+-----+--------------------+-------------------------------+
| age | name               | email                         |
+-----+--------------------+-------------------------------+
|  20 | Alexandrea Deckow  | lueilwitz.barry@hermiston.com |
|  20 | Alexandrea Deckow  | lueilwitz.barry@hermiston.com |
|  20 | Alexandrea Deckow  | lueilwitz.barry@hermiston.com |
|  20 | Alexandrea Deckow  | lueilwitz.barry@hermiston.com |
|  20 | Alexandrea Deckow  | lueilwitz.barry@hermiston.com |
|  20 | Alexandrea Deckow  | lueilwitz.barry@hermiston.com |
|  47 | Alexandrea Denesik | loy.larkin@durgan.com         |
|  47 | Alexandrea Denesik | loy.larkin@durgan.com         |
|  28 | Alexandrea Dibbert | rae61@gerhold.info            |
|  28 | Alexandrea Dibbert | rae61@gerhold.info            |
+-----+--------------------+-------------------------------+
10 rows in set (18.65 sec)


--------------------------


MySQL [test_db_for_index]> select age,name,email from user force index(name_age_email) where age > 89 order by name limit 1;
Empty set (1.61 sec)

MySQL [test_db_for_index]> select age,name,email from user force index(age_name_email) where age > 89 order by name limit 1;
Empty set (0.01 sec)


MySQL [test_db_for_index]> select age,name,email from user force index(name_age_email) where age > 18 order by name limit 1;
+-----+--------------------+-----------------+
| age | name               | email           |
+-----+--------------------+-----------------+
|  60 | Aaliyah Altenwerth | grice@yahoo.com |
+-----+--------------------+-----------------+
1 row in set (0.00 sec)

MySQL [test_db_for_index]> select age,name,email from user force index(age_name_email) where age > 18 order by name limit 1;
+-----+--------------------+-----------------+
| age | name               | email           |
+-----+--------------------+-----------------+
|  60 | Aaliyah Altenwerth | grice@yahoo.com |
+-----+--------------------+-----------------+
1 row in set (3.11 sec)

## 選擇候選A 仍是 候選B,咱們本身想的過程,其次優化器已經幫咱們都想好了。(若是同時存在候選A 和 候選B 的話)



MySQL [test_db_for_index]> show index from user;
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name       | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY        |            1 | id          | A         |     4870574 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | age_name_email |            1 | age         | A         |       29698 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | age_name_email |            2 | name        | A         |     2435287 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | age_name_email |            3 | email       | A         |     2435287 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name_age_email |            1 | name        | A         |     1623524 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name_age_email |            2 | age         | A         |     1623524 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | name_age_email |            3 | email       | A         |     2435287 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
7 rows in set (0.01 sec)


MySQL [test_db_for_index]> desc select age,name,email from user where age>1 order by name limit 1;
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys  | key            | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | user  | index | age_name_email | name_age_email | 1535    | NULL |    2 | Using where; Using index |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
1 row in set (0.01 sec)

MySQL [test_db_for_index]> desc select age,name,email from user where age>50 order by name limit 1;
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys  | key            | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | user  | index | age_name_email | name_age_email | 1535    | NULL |    2 | Using where; Using index |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
1 row in set (0.01 sec)


MySQL [test_db_for_index]> desc select age,name,email from user where age>70 order by name limit 1;
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys  | key            | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+
|  1 | SIMPLE      | user  | index | age_name_email | name_age_email | 1535    | NULL |    3 | Using where; Using index |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+--------------------------+


MySQL [test_db_for_index]> desc select age,name,email from user where age>80 order by name limit 1;
+----+-------------+-------+-------+----------------+----------------+---------+------+------+------------------------------------------+
| id | select_type | table | type  | possible_keys  | key            | key_len | ref  | rows | Extra                                    |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+------------------------------------------+
|  1 | SIMPLE      | user  | range | age_name_email | age_name_email | 1       | NULL |    1 | Using where; Using index; Using filesort |
+----+-------------+-------+-------+----------------+----------------+---------+------+------+------------------------------------------+
1 row in set (0.02 sec)

複製代碼

參考資料

博客

測試數據集

相關文章
相關標籤/搜索