mysql索引失效


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)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM