(1)越小的數據類型一般更好:越小的數據類型一般在磁盤、內存和CPU緩存中都須要更少的空間,處理起來更快。
(2)簡單的數據類型更好:整型數據比起字符,處理開銷更小,由於字符串的比較更復雜。在MySQL中,應該用內置的日期和時間數據類型,而不是用字符串來存儲時間;以及用整型數據類型存儲IP地址。
(3)儘可能避免NULL:應該指定列爲NOT NULL,除非你想存儲NULL。在MySQL中,含有空值的列很難進行查詢優化,由於它們使得索引、索引的統計信息以及比較運算更加複雜。你應該用0、一個特殊的值或者一個空串代替空值。
(4)索引不會包含有NULL值的列。html
可是若是是一樣的sql若是在以前可以使用到索引,那麼如今使用不到索引,如下幾種主要狀況:mysql
1. 隨着表的增加,where條件出來的數據太多,大於15%,使得索引失效(會致使CBO計算走索引花費大於走全表)sql
2. 統計信息失效 須要從新蒐集統計信息數據庫
3. 索引自己失效 須要重建索引緩存
下面是一些不會使用到索引的緣由
索引失效
1) 沒有查詢條件,或者查詢條件沒有創建索引;
2) 在查詢條件上沒有使用引導列
3) 查詢的數量是大表的大部分,應該是30%以上。
4) 索引自己失效
5) 查詢條件使用函數在索引列上(見12)
6) 對小表查詢
7) 提示不使用索引
8) 統計數據不真實
9) CBO計算走索引花費過大的狀況。其實也包含了上面的狀況,這裏指的是表佔有的block要比索引小。
10)隱式轉換致使索引失效.這一點應當引發重視.也是開發中常常會犯的錯誤. 因爲表的字段tu_mdn定義爲varchar2(20),
但在查詢時把該字段做爲number類型以where條件傳給mysql,這樣會致使索引失效.
錯誤的例子:select * from test where tu_mdn=13333333333;
正確的例子:select * from test where tu_mdn='13333333333';
11)對索引列進行運算致使索引失效,我所指的對索引列進行運算包括(+,-,*,/,! 等)
錯誤的例子:select * from test where id-1=9;
正確的例子:select * from test where id=10;
12)使用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; 這時函數索引發做用了bash
13)若是MySQL估計使用索引比全表掃描更慢,則不使用索引。例如若是列key_part1均勻分佈在1到100之間,查詢時使用索引就不是很好數據庫設計
mysql>select * from table_name where key_part1>1 and key_part<90;
14)若是使用MEMORY/HEAP表而且where條件中不使用「=」進行索引列,那麼不會用到索引。Heap表只有在「=」的條件下會使用索引。由於用的是哈希索引。函數
15)用or分割開的條件,若是or前的條件中的列有索引,然後面的列中沒有索引,那麼涉及的索引都不會被用到。mysql索引
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>
16)若是將要使用的索引列不是複合索引列表中的第一部分,則不會使用索引
以下例子:可見雖然在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>
17)若是like是以%開始,可見雖然在name上面建有索引,可是因爲where 條件中like的值的「%」在第一位了,那麼MySQL也會採用這個索引。
若是WHERE子句的查詢條件裏使用了比較操做符LIKE和REGEXP,MYSQL只有在搜索模板的第一個字符不是通配符的狀況下才能使用索引。好比說,若是查詢條件是LIKE 'abc%',MYSQL將使用索引;若是條件是LIKE '%abc',MYSQL將不使用索引。
18)獨立的列(對列變量須要計算(聚合運算、類型轉換等))
獨立的列是指索引列不能是表達式的一部分,也不是是函數的參數。例如如下兩個查詢沒法使用索引:
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)
19).在JOIN操做中(須要從多個數據表提取數據時),MYSQL只有在主鍵和外鍵的數據類型相同時才能使用索引,不然即便創建了 索引也不會使用
20).在ORDER BY操做中,MYSQL只有在排序條件不是一個查詢條件表達式的狀況下才使用索引。儘管如此,在涉及多個數據表的查詢裏,即便有索引可用,那些索引在加快ORDER BY操做方面也沒什麼做用。
21).不要給「性別」增長索引。若是某個數據列裏包含着許多重複的值,就算爲它創建了索引也不會有很好的效果。好比說,若是某個數據列裏包含了淨是些諸如「0/1」或「Y/N」等值,就沒有必要爲它建立一個索引。
簡單的說吧,不須要,由於性別,就兩個值男與女(人妖不算,呵)。爲這兩個值創建索引是不值得的,由於不管多少條記錄,創建性別的索引,最多讓你的語句少檢索一半。但與創建索引帶來的損失比,撿芝麻丟西瓜。(可能不許確,但大意如些)。
打個比方,數據庫就比如一本新華字典,咱們查數據時,能夠根據拼音來查,字在字典的排序是根據拼音來排序的,咱們要查一個字,能夠根據拼音很快就能查到咱們要查的字,這就叫做彙集索引!換句話說,彙集索引就是按照物理排序的,也由於是按物理排序的,因此一張表只能有一個彙集索引,也是最快的索引。固然,咱們也能夠根據部首來查,可是這種查詢必須先查找到部首,而後再到檢索表查到那麼字,最後才能查到咱們須要的字,你沒辦法像拼音查法同樣翻翻字典就能夠查到,這就叫做普通索引。普通索引能夠有多個。
假如一本字典裏全是"男"和"女"兩個字,那麼在檢索表裏也有不少個"男"和"女",這對查詢幫助不大。
22).若是對大的文本進行搜索,使用全文索引而不使用like「%...%」.
23).若是列名是索引,使用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)
24).不使用NOT IN和<>操做
NOT IN和<>操做都不會使用索引將進行全表掃描。NOT IN能夠NOT EXISTS代替,id<>3則可以使用id>3 or id<3來代替。
25).排序的索引問題
mysql查詢只使用一個索引,所以若是where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。所以數據庫默認排序能夠符合要求的狀況下不要使用排序操做;儘可能不要包含多個列的排序,若是須要最好給這些列建立複合索引。
26).使用短索引
對串列進行索引,若是可能應該指定一個前綴長度。例如,若是有一個CHAR(255)的列,若是在前10個或20個字符內,多數值是唯一的,那麼就不要對整個列進行索引。短索引不只能夠提升查詢速度並且能夠節省磁盤空間和I/O操做。
27).索引不會包含有NULL值的列
只要列中包含有NULL值都將不會被包含在索引中,複合索引中只要有一列含有NULL值,那麼這一列對於此複合索引就是無效的。因此咱們在數據庫設計時不要讓字段的默認值爲NULL。
28).使用ENUM而不是字符串
ENUM保存的是TINYINT,別在枚舉中搞一些「中國」「北京」「技術部」這樣的字符串,字符串空間又大,效率又低。
若是索引正在工做,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>
分析表的語法以下:(檢查一個或多個表是否有錯誤)
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表起做用。
mysql> optimize table sales; +--------------+----------+----------+----------+ | Table | Op | Msg_type | Msg_text | +--------------+----------+----------+----------+ | sakila.sales | optimize | status | OK | +--------------+----------+----------+----------+ 1 row in set (0.05 sec)