本文主要講關於SELECT語句的優化問題. 會涉及到一些關於表索引的知識.

性能

很簡單, 你每建立一個索引, 數據庫就會根據索引類型,幫你建立一個索引數據結構(B+樹非常常用).

NOTE:B+樹

B+樹對範圍查詢和直接查詢都很在行. 直接查詢的時間複雜度均為O(log), 也就是用的二分法, 具體為什麼就去看B+樹的數據結構, 在看B+樹之前最好先看B樹, 不然東西太多消化不了.

不過每次插入數據和刪除數據就需要重構索引樹的結構, 濫用索引反而會降低寫入效率.

設計

索引有好幾種, 最常用的有:

  • 主鍵索引: 比如用戶表的ID字段
  • 唯一索引: 比如不可重複的用戶名
  • 組合索引: 需要經常被一起查詢的字段, 比如用戶名, 密碼
  • 普通索引: 這個怎麼用就仁者見仁智者見智了.

索引一經創建不能修改,如果要修改索引,只能刪除重建. 具體怎麼創建就baidu吧。

如果要查看一張表的索引, 如下:

 1mysql> SHOW INDEX FROM users;
 2
 3+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
 4| Table | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
 5+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
 6| users |          0 | PRIMARY   |            1 | id          | A         |        9847 |     NULL | NULL   |      | BTREE      |         |               |
 7| users |          0 | id_UNIQUE |            1 | id          | A         |        9847 |     NULL | NULL   |      | BTREE      |         |               |
 8| users |          0 | username  |            1 | username    | A         |        9847 |     NULL | NULL   |      | BTREE      |         |               |
 9+-------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
10
113 rows in set (0.00 sec)

不重要的就不說了。

  • Non_unique: 能不能重複,不能為0,可以為1.
  • Seq_in_index: 組合索引的一個位置。等會就知道。
  • Sub_part: 前置索引長度,不使用前置索引則為NULL。
  • Cardinality: 基數,是否做為索引的最重要的因素,基數越大說明重複性越低,越有可能查詢時觸發索引。
  • Index_type: 索引類型,常用的有FULLTEXT(用與搜索技術,主要解決模糊查詢效率低下的問題),和BTREE(就是樹形數據結構存儲),還有Hash(由於Hash的特點,只能判斷值相等,不能判斷值範圍)。

QUESION: 什麼是前置索引?

對於一些特殊字段比如TEXT,或者VARCHAR(255)這種很大的字段,建立的索引樹會非常肥,這樣的索引非常慢,我們對與這種字段會採用它們的前幾個字符做為索引。選擇幾個字符的訣竅就是要保證要極高的非重複性,和儘量短的字符,來節省空間。

NOTE: 關於基數

通常我們在選擇索引時會儘可能選擇基數大的列做為索引。 因為MySQL在執行查詢時,基數對比總數是是否觸發索引的一個判斷條件,這個值大概在30%左右。

調優

查看一次SELECT在MySQL使用了哪些策略,我們可以通過在SQL語句前加上EXPLAIN來做到。

1mysql> EXPLAIN SELECT id, username FROM users WHERE username = 'sdttttt';
2+----+-------------+-------+------------+-------+---------------+----------+---------+-------+------+----------+-------------+
3| id | select_type | table | partitions | type  | possible_keys | key      | key_len | ref   | rows | filtered | Extra       |
4+----+-------------+-------+------------+-------+---------------+----------+---------+-------+------+----------+-------------+
5|  1 | SIMPLE      | users | NULL       | const | username      | username | 50      | const |    1 |   100.00 | Using index |
6+----+-------------+-------+------------+-------+---------------+----------+---------+-------+------+----------+-------------+
71 row in set, 1 warning (0.00 sec)
  • possible_keys: 可能觸發的索引。
  • key: 本次SQL執行觸發的索引。
  • filtered: 過濾了多少數據,單位:百分比。
  • Extra: 執行策略。

下面說一下Extra,這個比較重要:

Using index

查詢的列被索引覆蓋,並且where篩選條件是索引的是前導列,Extra中為Using index

Using where Using index

  1. 查詢的列被索引覆蓋,並且where篩選條件是索引列之一, 但是不是索引的不是前導列Extra中為Using where; Using index,意味著無法直接通過索引查找來查詢到符合條件的數據。
  2. 查詢的列被索引覆蓋,並且where篩選條件是索引列前導列的一個範圍,同樣意味著無法直接通過索引查找查詢到符合條件的數據。

(查詢的列被索引覆蓋,這種情況是不會發生回表的,只是進行索引掃描罷了)

NULL

(既沒有Using index,也沒有Using where Using index,也沒有using where

1,查詢的列未被索引覆蓋,並且where篩選條件是索引的前導列,意味著用到了索引,但是部分字段未被索引覆蓋,必須通過“回表”來實現,不是純粹地用到了索引,也不是完全沒用到索引,Extra中為NULL(沒有信息)。

Using where

1,查詢的列未被索引覆蓋,where篩選條件非索引的前導列,Extra中為Using where.

using where 意味著通過索引或者表掃描的方式進程where條件的過濾, 反過來說,也就是沒有可用的索引查找,這裡的type都是all,說明MySQL認為全表掃描是一種比較低的代價。)

EXT: ICP(Index Condition Pushdown)

這個是MySQL5.6新加入的特新,MySQL的設計分為兩層,服務層和存儲引擎。

在沒有的ICP的時候,你對索引執行過濾:首先SE(Storage Engine)將一條條索引數據取出,並且一條條給服務層看,由服務層來對照where條件。

在使用ICP後,你對索引執行過濾:首先SE(Storage Engine)將一條條索引數據取出,並且對照下推的索引條件,如果滿足條件,就返回給服務層,服務層下推到沒有被SE執行的where條件。

你是不是覺得很繞?其實就是索引的where條件SE幫服務層做了,服務層不>用去管索引的Where條件了。

說實話我對這個功能還是挺迷的,服務層的執行速度是不如SE麼?為什麼ICP能提高速度?MySQL中的秘密還挺多。

All in All

在設計數據表的時候需要考慮這個表的讀寫情況,根據字段來適當的增加索引。

在編寫SQL語句時,儘可能使用索引。能調優就儘量調優。