mysql索引使用總結
1 使用索引注意
(1)越小的數據類型通常更好:越小的數據類型通常在磁盤、內存和CPU緩存中都需要更少的空間,處理起來更快。
(2)簡單的數據類型更好:整型數據比起字符,處理開銷更小,因為字符串的比較更復雜。在MySQL中,應該用內置的日期和時間數據類型,而不是用字符串來存儲時間;以及用整型數據類型存儲IP地址。
(3)盡量避免NULL:應該指定列為NOT NULL,除非你想存儲NULL。在MySQL中,含有空值的列很難進行查詢優化,因為它們使得索引、索引的統計信息以及比較運算更加復雜。你應該用0、一個特殊的值或者一個空串代替空值。
(4)索引不會包含有NULL值的列。
2 索引不能使用排查
但是如果是同樣的sql如果在之前能夠使用到索引,那么現在使用不到索引,以下幾種主要情況:
1. 隨着表的增長,where條件出來的數據太多,大於15%,使得索引失效(會導致CBO計算走索引花費大於走全表)
2. 統計信息失效,需要重新搜集統計信息
3. 索引本身失效,需要重建索引
3 不會用到索引
下面是一些不會使用到索引的原因
1) 沒有查詢條件,或者查詢條件沒有建立索引;
2) 在查詢條件上沒有使用引導列
3) 查詢的數量是大表的大部分,應該是30%以上。
4) 索引本身失效
5) 在索引列上使用mysql的內置函數
6) 對小表查詢,數據量小
7) CBO計算走索引花費過大的情況。其實也包含了上面的情況,這里指的是表占有的block要比索引小。
8)隱式轉換導致索引失效.這一點應當引起重視.也是開發中經常會犯的錯誤. 由於表的字段tu_mdn定義為varchar2(20),
但在查詢時把該字段作為number類型以where條件傳給mysql,這樣會導致索引失效.
錯誤的例子:select * from test where tu_mdn=13333333333;
正確的例子:select * from test where tu_mdn='13333333333';
9)對索引列進行運算導致索引失效,我所指的對索引列進行運算包括(+,-,*,/,! 等)
錯誤的例子:select * from test where id-1=9;
正確的例子:select * from test where id=10;
10)使用mysql內部函數導致索引失效.對於這樣情況應當創建基於函數的索引.
錯誤的例子:select * from test where round(id)=10;
說明,此時id的索引已經不起作用了 正確的例子:首先建立函數索引,
create index test_id_fbi_idx on test(round(id));
然后 select * from test where round(id)=10; 這時函數索引起作用了
11)如果MySQL估計使用索引比全表掃描更慢,則不使用索引。例如如果列key_part1均勻分布在1到100之間,查詢時使用索引就不是很好
mysql>select * from table_name where key_part1>1 and key_part<90;
12)如果使用MEMORY/HEAP表並且where條件中不使用“=”進行索引列,那么不會用到索引。Heap表只有在“=”的條件下會使用索引。因為用的是哈希索引。
13)用or分割開的條件,如果or前的條件中的列有索引,而后面的列中沒有索引,那么涉及的索引都不會被用到。
mysql> show index from test1\G; *************************** 1. row *************************** Table: test1 Non_unique: 1 Key_name: inx_id_name Seq_in_index: 1 Column_name: name Collation: A Cardinality: 552589 Sub_part: NULL Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: *************************** 2. row *************************** Table: test1 Non_unique: 1 Key_name: inx_id_name Seq_in_index: 2 Column_name: id Collation: A Cardinality: 567855 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: 2 rows in set (0.00 sec) ERROR: No query specified mysql>
從上面可以發現只有name和id列上面有索引。來看如下的執行計划。
mysql> explain extended select * from test1 where name='name100' or dept='dept100'; +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+ | 1 | SIMPLE | test1 | NULL | ALL | inx_id_name | NULL | NULL | NULL | 769014 | 19.00 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+ 1 row in set, 2 warnings (0.00 sec) mysql>
14)如果將要使用的索引列不是復合索引列表中的第一部分,則不會使用索引
如下例子:可見雖然在id上面建有復合索引,但是由於id不是索引的第一列,那么在查詢中這個索引也不會被MySQL采用。(索引的最左匹配原則)
mysql> explain select * from test1 where id=1; +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+ | 1 | SIMPLE | test1 | NULL | ALL | NULL | NULL | NULL | NULL | 787947 | 10.00 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) mysql>
15)如果like是以%開始,可見雖然在name上面建有索引,但是由於where 條件中like的值的“%”在第一位了,那么MySQL也會采用這個索引。
如果WHERE子句的查詢條件里使用了比較操作符LIKE和REGEXP,MYSQL只有在搜索模板的第一個字符不是通配符的情況下才能使用索引。比如說,如果查詢條件是LIKE 'abc%',MYSQL將使用索引;如果條件是LIKE '%abc',MYSQL將不使用索引。
17)獨立的列(對列變量需要計算(聚合運算、類型轉換等))
獨立的列是指索引列不能是表達式的一部分,也不是是函數的參數。例如以下兩個查詢無法使用索引:
1)表達式: select actor_id from sakila.actor where actor_id+1=5;
2)函數參數:select ... where TO_DAYS(CURRENT_DATE) - TO_DAYS(date_col)<=10;應該把列計算轉換成常量計算。
示例:
如果列類型是字符串,但在查詢時把一個數值型常量賦值給了一個字符型的列名name,那么雖然在name列上有索引,但是也沒有用到。
mysql> explain select * from company2 where name=294\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: company2 type: ALL possible_keys: ind_company2_name key: NULL key_len: NULL ref: NULL rows: 1000 Extra: Using where 1 row in set (0.00 sec)
而下面的sql語句就可以正確使用索引。
mysql> explain select * from company2 where name name=‘294'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: company2 type: ref possible_keys: ind_company2_name key: ind_company2_name key_len: 23 ref: const rows: 1 Extra: Using where 1 row in set (0.00 sec)
18).在JOIN操作中(需要從多個數據表提取數據時),MYSQL只有在主鍵和外鍵的數據類型相同時才能使用索引,否則即使建立了 索引也不會使用
19).在ORDER BY操作中,MYSQL只有在排序條件不是一個查詢條件表達式的情況下才使用索引。盡管如此,在涉及多個數據表的查詢里,即使有索引可用,那些索引在加快ORDER BY操作方面也沒什么作用。
20).不要給“性別”增加索引。如果某個數據列里包含着許多重復的值,就算為它建立了索引也不會有很好的效果。比如說,如果某個數據列里包含了凈是些諸如“0/1”或“Y/N”等值,就沒有必要為它創建一個索引。
簡單的說吧,不需要,因為性別,就兩個值男與女(人妖不算,呵)。為這兩個值建立索引是不值得的,因為無論多少條記錄,建立性別的索引,最多讓你的語句少檢索一半。但與建立索引帶來的損失比,撿芝麻丟西瓜。(可能不准確,但大意如些)。
打個比方,數據庫就好比一本新華字典,我們查數據時,可以根據拼音來查,字在字典的排序是根據拼音來排序的,我們要查一個字,可以根據拼音很快就能查到我們要查的字,這就叫作聚集索引!換句話說,聚集索引就是按照物理排序的,也因為是按物理排序的,所以一張表只能有一個聚集索引,也是最快的索引。當然,我們也可以根據部首來查,但是這種查詢必須先查找到部首,然后再到檢索表查到那么字,最后才能查到我們需要的字,你沒辦法像拼音查法一樣翻翻字典就可以查到,這就叫作普通索引。普通索引可以有多個。
假如一本字典里全是"男"和"女"兩個字,那么在檢索表里也有很多個"男"和"女",這對查詢幫助不大。
21).如果對大的文本進行搜索,使用全文索引而不使用like“%...%”.
22).如果列名是索引,使用column_name is null將使用索引。
如下
mysql> explain select * from company2 where name is null\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: company2 type: ref possible_keys: ind_company2_name key: ind_company2_name key_len: 11 ref: const rows: 1 Extra: Using where 1 row in set (0.00 sec)
23).不使用NOT IN和<>操作
NOT IN和<>操作都不會使用索引將進行全表掃描。NOT IN可以NOT EXISTS代替,id<>3則可使用id>3 or id<3來代替。
24).排序的索引問題
mysql查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那么order by中的列是不會使用索引的。因此數據庫默認排序可以符合要求的情況下不要使用排序操作;盡量不要包含多個列的排序,如果需要最好給這些列創建復合索引。
25).使用短索引
對串列進行索引,如果可能應該指定一個前綴長度。例如,如果有一個CHAR(255)的列,如果在前10個或20個字符內,多數值是惟一的,那么就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁盤空間和I/O操作。
26).索引不會包含有NULL值的列
只要列中包含有NULL值都將不會被包含在索引中,復合索引中只要有一列含有NULL值,那么這一列對於此復合索引就是無效的。所以我們在數據庫設計時不要讓字段的默認值為NULL。
27).使用ENUM而不是字符串
ENUM保存的是TINYINT,別在枚舉中搞一些“中國”“北京”“技術部”這樣的字符串,字符串空間又大,效率又低。
三、索引分析方法
3.1查看索引使用情況
如果索引正在工作,Handler_read_key的值將很高,這個值代表了一個行被索引值讀的次數。
Handler_read_rnd_next的值高則意味着查詢運行低效,並且應該建立索引補救。
mysql> show status like 'Handler_read%'; +-----------------------+--------+ | Variable_name | Value | +-----------------------+--------+ | Handler_read_first | 9 | | Handler_read_key | 16 | | Handler_read_last | 0 | | Handler_read_next | 680908 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 935519 | +-----------------------+--------+ 7 rows in set (0.00 sec) mysql>
3.2兩個簡單實用的優化方法:
分析表的語法如下:(檢查一個或多個表是否有錯誤)
1
2
3
4
5
6
7
8
9
|
mysql> CHECK TABLE tbl_name[,tbl_name] …[option] …option =
{ QUICK | FAST | MEDIUM| EXTENDED | CHANGED}
mysql> check table sales;
+--------------+-------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+--------------+-------+----------+----------+
| sakila.sales | check | status | OK |
+--------------+-------+----------+----------+
1 row
in
set
(0.01 sec)
|
優化表的語法格式:
OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [,tbl_name]
如果已經刪除了表的一大部分,或者如果已經對含有可變長度行的表進行了很多的改動,則需要做定期優化。這個命令可以將表中的空間碎片進行合並,但是此命令只對MyISAM、BDB和InnoDB表起作用。
1
2
3
4
5
6
7
|
mysql> optimize table sales;
+--------------+----------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+--------------+----------+----------+----------+
| sakila.sales | optimize | status | OK |
+--------------+----------+----------+----------+
1 row
in
set
(0.05 sec)
|