MySQL-索引管理及執行計劃

1、索引介紹

1.一、索引做用

提供了相似於書中目錄的做用,目的是爲了優化查詢html

1.二、索引算法上分類

大的分類:mysql

B樹索引 
Hash索引 
R樹 
Full text 
GIS  #地圖類索引
-------------------------------

#B樹基於不一樣的查找算法分類:
B-tree 
如下兩種類型在範圍查詢方面提供了更好的性能(> < >= <=)
B+Tree   
B*Tree

1.三、索引功能上的分類

1.3.一、輔助索引

1)輔助索引(S)怎麼構建B樹結構的?linux

  • (1)索引是基於表中列(索引鍵)的值生成的B樹結構
  • (2)首先提取此列全部的值,進行自動排序
  • (3)將排好序的值,均勻的分佈到索引樹的葉子節點中(16K)
  • (4)而後生成此索引鍵值所對應得後端數據頁的指針
  • (5)生成枝節點和根節點,根據數據量級和索引鍵長度,生成合適的索引樹高度

image

2)輔助索引細分面試

  • 1)普通的單列輔助索引(普通索引
  • 2)覆蓋索引(聯合索引):多個列做爲索引條件,生成索引樹,理論上設計的好的,能夠減小大量的回表查詢
  • 3)惟一索引:索引列的值都是惟一的.

1.3.二、彙集索引

1)前提redis

  • (1)表中設置了主鍵,主鍵列就會自動被做爲彙集索引.
  • (2)若是沒有主鍵,會選擇惟一鍵做爲彙集索引.
  • (3)彙集索引必須在建表時纔有意義,通常是表的無關列(ID)

2)彙集索引(C)怎麼構建B樹結構的?算法

  • (1) 在建表時,設置了主鍵列(ID)
  • (2) 在未來錄入數據時,就會按照ID列的順序存儲到磁盤上.(咱們又稱之爲彙集索引組織表)
  • (3) 將排好序的整行數據,生成葉子節點.能夠理解爲,磁盤的數據頁就是葉子節點

1.3.三、索引高度影響因數

1)數據量級, 解決方法:分表,分庫,分佈式sql

2)索引列值過長 , 解決方法:前綴索引mongodb

3)數據類型: 數據庫

變長長度字符串,使用了char,解決方案:變長字符串使用varcharvim

enum類型的使用enum ('山東','河北','黑龍江','吉林','遼寧','陝西'......)

1.3.四、B樹查找算法

image

image

image

2、索引基本管理

2.一、查看索引

mysql> desc city;
+-------------+----------+------+-----+---------+----------------+
| Field       | Type     | Null | Key | Default | Extra          |
+-------------+----------+------+-----+---------+----------------+
| ID          | int(11)  | NO   | PRI | NULL    | auto_increment |
| Name        | char(35) | NO   |     |         |                |
| CountryCode | char(3)  | NO   | MUL |         |                |
| District    | char(20) | NO   |     |         |                |
| Population  | int(11)  | NO   |     | 0       |                |
+-------------+----------+------+-----+---------+----------------+
Field :列名字
key   :有沒有索引,索引類型
	  PRI: 主鍵索引
	  UNI: 惟一索引 
	  MUL: 輔助索引(單列,聯和,前綴)

mysql> show index from city;
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name    | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| city  |          0 | PRIMARY     |            1 | ID          | A         |        4188 |     NULL | NULL   |      | BTREE      |         |               |
| city  |          1 | CountryCode |            1 | CountryCode | A         |        4188 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

2.二、索引建立修改刪除

1)建立索引

alter table city add index idx_name(name);		#方法一
create index idx_name1 on city(name);			#方法二
show index from city;

2)刪除索引

alter table city drop index idx_name1;		#直接刪除索引名稱便可

3)覆蓋索引(聯合索引)

alter table city add key idx_co_po(countrycode,population);	#多個字段上創建索引
alter table city add index idx_co_po(countrycode,population);	#多個字段上創建索引

4)前綴索引

alter table city add index idx_di(district(5));	#在前5個字符上創建索引

5)惟一索引

alter table city add unique index idx_uni1(name);

ERROR 1062 (23000): Duplicate entry 'San Jose' for key 'idx_uni1'	#惟一鍵衝突

3、執行計劃

3.一、執行計劃介紹

獲取到的是優化器選擇完成的,他認爲代價最小的執行計劃.

做用: 語句執行前,先看執行計劃信息,能夠有效的防止性能較差的語句帶來的性能問題.

3.二、select 獲取數據的方法

1. 全表掃描(應當儘可能避免,由於性能低)

2. 索引掃描

3. 獲取不到數據

3.三、執行計劃獲取及分析

獲取優化器選擇後的執行計劃:explain或者desc

explain select SQL_NO_CACHE * from test where name='AAA'\G     #SQL_NO_CACHE的做用是禁止緩存查詢結果。

mysql> desc select * from city where countrycode='CHN'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: city
   partitions: NULL
         type: ref
possible_keys: CountryCode
          key: CountryCode
      key_len: 3
          ref: const
         rows: 363
     filtered: 100.00
        Extra: NULL
---------------------------------------------------------------

#執行計劃相關信息分析:
table: city                               #查詢操做的表  
possible_keys: CountryCode,idx_co_po      #可能會走的索引
key: CountryCode						  #真正走的索引  
type: ref								  #索引類型      
Extra: Using index condition              #額外信息 

4、索引類型詳解 *****

以下爲索引類型,從左到右性能依次變好.

ALL			#全表掃描
index			#全索引掃描
range			#索引範圍查詢
ref				#輔助索引的等值查詢
eq_ref			#多表鏈接的表,On的條件是主鍵或惟一鍵
system(const)	#主鍵或惟一鍵的等值查詢
NULL			#索引中掃描不到這個數據

在索引掃描類型方面,至少保證在range以上級別

4.一、all: 全表掃描

desc select * from city;
desc select * from city where name like '%C%';
desc select * from city where name != 'CHN';	#或者<>
desc select * from city where countrycode not in ('CHN','USA');
#注意:生產中幾乎是沒有這種需求的。儘可能避免

4.二、index: 全索引掃描

須要掃描整個索引樹,獲取到想要數據,比ALL性能好,順序IO,能夠減小回表查詢

mysql> desc city;
+-------------+----------+------+-----+---------+----------------+
| Field       | Type     | Null | Key | Default | Extra          |
+-------------+----------+------+-----+---------+----------------+
| ID          | int(11)  | NO   | PRI | NULL    | auto_increment |
| Name        | char(35) | NO   |     |         |                |
| CountryCode | char(3)  | NO   | MUL |         |                |
| District    | char(20) | NO   |     |         |                |
| Population  | int(11)  | NO   |     | 0       |                |
+-------------+----------+------+-----+---------+----------------+

mysql> desc select CountryCode from city;
+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key         | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | city  | NULL       | index | NULL          | CountryCode | 3       | NULL | 4188 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-------------+

4.三、 range : 索引範圍查詢

4.3.一、狀況種類

>  <  >= <= 
in 
or 
like 'CH%'
between and
---------------------------------------------------
注意:
B+樹額外優化了
> < >= <= 
between and 
like 'CH%'
in or沒法享受B+樹的額外優化,能夠用union all來替代

4.3.二、示例

mysql> desc select * from city where id<10;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | city  | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |    9 |   100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+

mysql> desc select * from city where countrycode in ('CHN','USA');
+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type  | possible_keys | key         | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | city  | NULL       | range | CountryCode   | CountryCode | 3       | NULL |  637 |   100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-----------------------+

mysql> desc select * from city where countrycode like 'CH%';
+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type  | possible_keys | key         | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | city  | NULL       | range | CountryCode   | CountryCode | 3       | NULL |  397 |   100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-----------------------+

4.3.三、優化案例:in=>union all

mysql> desc select * from city where countrycode in ('CHN','USA');
+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type  | possible_keys | key         | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | city  | NULL       | range | CountryCode   | CountryCode | 3       | NULL |  637 |   100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-----------------------+

mysql> desc select * from city where countrycode='CHN' union all select * from city where countrycode='USA';
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key         | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
|  1 | PRIMARY     | city  | NULL       | ref  | CountryCode   | CountryCode | 3       | const |  363 |   100.00 | NULL  |
|  2 | UNION       | city  | NULL       | ref  | CountryCode   | CountryCode | 3       | const |  274 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+

4.四、 ref: 輔助索引的等值查詢

mysql> desc select * from city where countrycode = 'CHN';
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key         | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | city  | NULL       | ref  | CountryCode   | CountryCode | 3       | const |  363 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+

4.五、eq_ref

多表鏈接的表,On的條件是主鍵或惟一鍵

4.六、system 或 const

主鍵或惟一鍵的等值查詢

mysql> desc select * from city where id=10;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | city  | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+

4.七、NULL

索引中掃描不到這個數據

mysql> desc select * from city where id=5000;  #id=5000不存在
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                          |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------+
|  1 | SIMPLE      | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | no matching row in const table |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------+

4.八、Extra字段:Using filesort問題

Extra字段:Using filesort	#出現說明有問題,要優化
desc select * from city where countrycode='CHN' order by population desc limit 10;   #contrycode上有索引,但population上是沒有索引的
mysql> desc select * from city where countrycode='CHN' order by population desc limit 10;
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+---------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key         | key_len | ref   | rows | filtered | Extra                                 |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+---------------------------------------+
|  1 | SIMPLE      | city  | NULL       | ref  | CountryCode   | CountryCode | 3       | const |  363 |   100.00 | Using index condition; Using filesort |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+---------------------------------------+

#解決思路:
索引能夠減小排序,能夠很大程度減小CPU時間
輔助索引 應用順序(優化器選擇的)
若是查詢條件:符合覆蓋索引的順序時,優先選擇覆蓋索引
不符合順序,優先會走where條件的索引

#解決方法:能夠在countrycode和population上創建聯合索引
mysql> alter table city add index idx_po(countrycode,population);
mysql> desc select * from city where countrycode='CHN' order by population limit 10;
+----+-------------+-------+------------+------+--------------------+--------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys      | key    | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+-------+------------+------+--------------------+--------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | city  | NULL       | ref  | CountryCode,idx_po | idx_po | 3       | const |  363 |   100.00 | Using index condition |
+----+-------------+-------+------------+------+--------------------+--------+---------+-------+------+----------+-----------------------+

4.九、explain使用場景

面試:咱們公司業務慢,請你從數據庫的角度分析緣由?
mysql出現性能問題,總結有兩種狀況:
(1)應急性的慢:忽然夯住
 應急狀況:數據庫hang(卡了,資源耗盡)
	處理過程:
	    (1)show processlist; #獲取到致使數據庫hang住的語句
	    (2)explain   #分析SQL的執行計劃,有沒有走索引,索引的類型狀況
	    (3)建索引,改語句	
(2)一段時間慢(持續性的):
	    (1)記錄慢日誌slowlog,分析slowlog
	    (2)explain 分析SQL的執行計劃,有沒有走索引,索引的類型狀況
	    (3)建索引,改語句

5、索引壓力測試

5.一、mysqlslap測試示例

mysqlslap工具介紹
​ mysqlslap來自於mariadb包,測試的過程默認生成一個mysqlslap的schema,生成測試表t1,查詢和插入測試數據,mysqlslap庫自動生成,若是已經存在則先刪除。用--only-print來打印實際的測試過程,整個測試完成後不會在數據庫中留下痕跡。

經常使用選項:

--auto-generate-sql, -a 自動生成測試表和數據,表示用mysqlslap工具本身生成的SQL腳原本測試併發壓力
--auto-generate-sql-load-type=type 測試語句的類型。表明要測試的環境是讀操做仍是寫操做仍是二者混合的。取值包括:read,key,write,update和mixed(默認)
--auto-generate-sql-add-auto-increment 表明對生成的表自動添加auto_increment列,從5.1.18版本開始支持
--number-char-cols=N, -x N 自動生成的測試表中包含多少個字符類型的列,默認1
--number-int-cols=N, -y N 自動生成的測試表中包含多少個數字類型的列,默認1
--number-of-queries=N 總的測試查詢次數(併發客戶數×每客戶查詢次數)
--query=name,-q 使用自定義腳本執行測試,例如能夠調用自定義的存儲過程或者sql語句來執行測試
--create-schema 表明自定義的測試庫名稱,測試的schema,MySQL中schema也就是database
--commint=N 多少條DML後提交一次
--compress, -C 如服務器和客戶端都支持壓縮,則壓縮信息
--concurrency=N, -c N 表示併發量,即模擬多少個客戶端同時執行select;可指定多個值,以逗號或者--delimiter參數指定值作爲分隔符
--engine=engine_name, -e engine_name 表明要測試的引擎,能夠有多個,用分隔符隔開
--iterations=N, -i N 測試執行的迭代次數,表明要在不一樣併發環境下,各自運行測試多少次
--only-print 只打印測試語句而不實際執行
--detach=N 執行N條語句後斷開重連
--debug-info, -T 打印內存和CPU的相關信息
測試示例:

1)單線程測試

[root@centos7 ~]# mysqlslap -a -uroot -p
Enter password: 
Benchmark
        Average number of seconds to run all queries: 0.004 seconds
        Minimum number of seconds to run all queries: 0.004 seconds
        Maximum number of seconds to run all queries: 0.004 seconds
        Number of clients running queries: 1
        Average number of queries per client: 0
2)多線程測試,使用--concurrency來模擬併發鏈接

[root@centos7 ~]# mysqlslap -uroot -p -a -c 500
Enter password: 
Benchmark
        Average number of seconds to run all queries: 3.384 seconds
        Minimum number of seconds to run all queries: 3.384 seconds
        Maximum number of seconds to run all queries: 3.384 seconds
        Number of clients running queries: 500
        Average number of queries per client: 0
3)同時測試不一樣的存儲引擎的性能進行對比

[root@centos7 ~]# mysqlslap -uroot -p -a --concurrency=500 --number-of-queries 1000 --iterations=5 --engine=myisam,innodb --debug-info
Enter password: 
Benchmark
        Running for engine myisam
        Average number of seconds to run all queries: 0.192 seconds
        Minimum number of seconds to run all queries: 0.187 seconds
        Maximum number of seconds to run all queries: 0.202 seconds
        Number of clients running queries: 500
        Average number of queries per client: 2

Benchmark
        Running for engine innodb
        Average number of seconds to run all queries: 0.355 seconds
        Minimum number of seconds to run all queries: 0.350 seconds
        Maximum number of seconds to run all queries: 0.364 seconds
        Number of clients running queries: 500
        Average number of queries per client: 2


User time 0.33, System time 0.58
Maximum resident set size 22892, Integral resident set size 0
Non-physical pagefaults 46012, Physical pagefaults 0, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 31896, Involuntary context switches 0
4)執行一次測試,分別500和1000個併發,執行5000次總查詢

[root@centos7 ~]# mysqlslap -uroot -p -a --concurrency=500,1000 --number-of-queries 5000 --debug-info
Enter password: 
Benchmark
        Average number of seconds to run all queries: 3.378 seconds
        Minimum number of seconds to run all queries: 3.378 seconds
        Maximum number of seconds to run all queries: 3.378 seconds
        Number of clients running queries: 500
        Average number of queries per client: 10

Benchmark
        Average number of seconds to run all queries: 3.101 seconds
        Minimum number of seconds to run all queries: 3.101 seconds
        Maximum number of seconds to run all queries: 3.101 seconds
        Number of clients running queries: 1000
        Average number of queries per client: 5


User time 0.84, System time 0.64
Maximum resident set size 83068, Integral resident set size 0
Non-physical pagefaults 139977, Physical pagefaults 0, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 31524, Involuntary context switches 3
5)迭代測試

[root@centos7 ~]# mysqlslap -uroot -p -a --concurrency=500 --number-of-queries 5000 --iterations=5 --debug-info
Enter password: 
Benchmark
        Average number of seconds to run all queries: 3.307 seconds
        Minimum number of seconds to run all queries: 3.184 seconds
        Maximum number of seconds to run all queries: 3.421 seconds
        Number of clients running queries: 500
        Average number of queries per client: 10


User time 2.18, System time 1.58
Maximum resident set size 74872, Integral resident set size 0
Non-physical pagefaults 327732, Physical pagefaults 0, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 73904, Involuntary context switches 3	

1)模擬數據庫數據

drop database oldboy;
create database oldboy charset utf8;

2)建立一個t1的表,而後導入50萬行數據

[root@db01 ~]# vim slap.sh
#!/bin/bash  
HOSTNAME="localhost" 
PORT="3306" 
USERNAME="root" 
PASSWORD="1" 
DBNAME="oldboy" 
TABLENAME="t1" 
#create database 
mysql -h ${HOSTNAME} -P${PORT} -u${USERNAME} -p${PASSWORD} -e "drop database if exists ${DBNAME}" 
create_db_sql="create database if not exists ${DBNAME}" 
mysql -h ${HOSTNAME} -P${PORT} -u${USERNAME} -p${PASSWORD} -e "${create_db_sql}" 
#create table 
create_table_sql="create table if not exists ${TABLENAME}(stuid int not null primary key,stuname varchar(20) not null,stusex char(1)   
not null,cardid varchar(20) not null,birthday datetime,entertime datetime,address varchar(100)default null)" 
mysql -h ${HOSTNAME} -P${PORT} -u${USERNAME} -p${PASSWORD} ${DBNAME} -e "${create_table_sql}" 
#insert data to table 
i="1" 
while [ $i -le 500000 ]  
do  
insert_sql="insert into ${TABLENAME}  values($i,'alexsb_$i','1','110011198809163418','1990-05-16','2017-09-13','oldboyedu')" 
mysql -h ${HOSTNAME} -P${PORT} -u${USERNAME} -p${PASSWORD} ${DBNAME} -e "${insert_sql}" 
let i++  
done  
#select data  
select_sql="select count(*) from ${TABLENAME}" 
mysql -h ${HOSTNAME} -P${PORT} -u${USERNAME} -p${PASSWORD} ${DBNAME} -e "${select_sql}"

執行腳本:
sh slap.sh

或者直接source			
drop database oldboy;
source /root/oldboy.sql

3)檢查數據可用性

mysql -uroot -p
select count(*) from oldboy.t1;

4)使用mysqlslap來進行壓力測試

mysqlslap --defaults-file=/etc/my.cnf \
 --concurrency=100 --iterations=1 --create-schema='oldboy' \
--query="select * from oldboy.t1 where stuname='alexsb_100'" engine=innodb \
--number-of-queries=2000 -uroot -pmysql -verbose

沒創建索引以前:

image

在查詢條件列上創建索引:alter table t1 add index idx_name(stuname);

image

5.二、其餘測試工具(待測試)

1)tpcc

2)sysbench

6、索引應用規範

主要根據公司的業務來創建合適的索引

  • 產品的功能
  • 用戶的行爲:"熱"查詢語句,"熱"數據

6.一、創建索引的原則

爲了使索引的使用效率更高,在建立索引時,必須考慮在哪些字段上建立索引建立什麼類型的索引

1)建表時必定要有主鍵,通常是個無關列(必須)

2)選擇惟一鍵索引

惟一性索引的值是惟一的,能夠更快速的經過該索引來肯定某條記錄。	

#優化方案:
    (1) 若是非得使用重複值較多的列做爲查詢條件(例如:男女),能夠將表邏輯拆分
    (2) 能夠將此列和其餘的查詢類,作聯和索引

#如何判斷索引列有多少是惟一值?
select count(*) from world.city;
select count(distinct countrycode) from world.city;
select count(distinct countrycode,population ) from world.city;

3)爲常常須要where 、ORDER BY、GROUP BY,join on等操做的字段創建索引,排序操做會浪費不少時間。注:若是常常做爲條件的列,重複值特別多,能夠創建聯合索引

4)使用前綴索引。若是索引字段的值很長,最好使用值的前綴來索引。

5)限制索引的數目

索引的數目不是越多越好。可能會產生的問題:
	(1) 每一個索引都須要佔用磁盤空間,索引越多,須要的磁盤空間就越大。
	(2) 修改表時,對索引的重構和更新很麻煩。越多的索引,會使更新表變得很浪費時間。
	(3) 優化器的負擔會很重,有可能會影響到優化器的選擇.

6)刪除再也不使用或者不多使用的索引(使用percona toolkit)

表中的數據被大量更新,或者數據的使用方式被改變後,原有的一些索引可能再也不須要。數據庫管理
員應當按期找出這些索引,將它們刪除,從而減小索引對更新操做的影響。

7)大表加索引,要在業務不繁忙期間操做

8)儘可能少在常常更新值的列上建索引

創建索引原則總結

(1) 必需要有主鍵,若是沒有能夠作爲主鍵條件的列,建立無關列
(2) 常常作爲where條件列 order by group by join on, distinct 的條件(業務:產品功能+用戶行爲)
(3) 最好使用惟一值多的列做爲索引,若是索引列重複值較多,能夠考慮使用聯合索引
(4) 列值長度較長的索引列,咱們建議使用前綴索引.
(5) 下降索引條目,一方面不要建立沒用索引,不常使用的索引清理,使用percona toolkit工具
(6) 索引維護要避開業務繁忙期

6.二、 不走索引的狀況

1)沒有查詢條件,或者查詢條件沒有創建索引

select * from tab;              #全表掃描
select  * from tab where 1=1;

在業務數據庫中,特別是數據量比較大的表,是沒有全表掃描這種需求。

  • 對用戶查看是很是痛苦的。
  • 對服務器來說毀滅性的。
(1)select * from tab;
SQL改寫成如下語句:
selec  * from  tab  order by  price  limit 10 ;     #須要在price列上創建索引

(2)select  * from  tab where name='zhangsan'          #name列沒有索引
改爲以下語句:
	一、換成有索引的列做爲查詢條件
	二、將name列創建索引

2)查詢的結果集,超過了總數行數25%,優化器默認沒有必要走索引

解決方法:
一、若是業務容許,可使用limit控制。
二、儘可能不要在mysql存放這個數據了,能夠放到redis裏面

3)索引自己失效,統計數據不真實

索引有自我維護的能力。對於表內容變化比較頻繁的狀況下,有可能會出現索引失效。通常是刪除重建

4)查詢條件使用函數在索引列上,或者對索引列進行運算,運算包括(+,-,*,/,! 等)

錯誤的例子:select * from test where id-1=9; 
正確的例子:select * from test where id=10;
在索引列上使用算術運算,函數運算,子查詢時,可能不會走索引

5)隱式轉換致使索引失效

mysql> alter table tab add index inx_tel(telnum);	
mysql> desc tab;
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| id     | int(11)     | YES  |     | NULL    |       |
| name   | varchar(20) | YES  |     | NULL    |       |
| telnum | varchar(20) | YES  | MUL | NULL    |       |	#字段類型是varchar,同時創建了索引
+--------+-------------+------+-----+---------+-------+

mysql> select * from tab where telnum='1333333';	#查詢條件是字符串,走索引
+------+------+---------+
| id   | name | telnum  |
+------+------+---------+
|    1 | a    | 1333333 |
+------+------+---------+

mysql> select * from tab where telnum=1333333;	#查詢條件是數字,隱式轉換爲字符串
+------+------+---------+
| id   | name | telnum  |
+------+------+---------+
|    1 | a    | 1333333 |
+------+------+---------+

mysql> explain  select * from tab where telnum='1333333';
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key     | key_len | ref   | rows | Extra                 |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | tab   | ref  | inx_tel       | inx_tel | 63      | const |    1 | Using index condition |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+

mysql> explain  select * from tab where telnum=1333333;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | tab   | ALL  | inx_tel       | NULL | NULL    | NULL |    2 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

6)<> ,not in 不走索引

EXPLAIN  SELECT * FROM teltab WHERE telnum   <> '110';
EXPLAIN  SELECT * FROM teltab WHERE telnum  NOT IN ('110','119');

注意:
1. 單獨的>,<,in 有可能走,也有可能不走,和結果集有關,儘可能結合業務添加limit
2. or或in  儘可能改爲union
EXPLAIN  SELECT * FROM teltab WHERE telnum   IN ('110','119');
改寫成:
EXPLAIN SELECT * FROM teltab WHERE telnum='110'
UNION ALL
SELECT * FROM teltab WHERE telnum='119'

7)like "%_" 百分號在最前面不走

EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%'   #走range索引掃描
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110'  #不走索引
%linux%類的搜索需求,可使用elasticsearch+mongodb 專門作搜索服務的數據庫產品

8)單獨引用聯合索引裏非第一位置的索引列.做爲條件查詢時不走索引

idx_a_b_c(a,b,c)	#創建的聯合索引

走索引的狀況:
where  a  b  c
where  a b 
where  a
============
部分走索引
where a c
where a c b
============
不走索引
where c 
where b 
where bc 
where cb
where ca 
where cba
相關文章
相關標籤/搜索