#方法一:建立表時 CREATE TABLE 表名 ( 字段名1 數據類型 [完整性約束條件…], 字段名2 數據類型 [完整性約束條件…], [UNIQUE | FULLTEXT | SPATIAL ] INDEX | KEY [索引名] (字段名[(長度)] [ASC |DESC]) ); #方法二:CREATE在已存在的表上建立索引 CREATE [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名 ON 表名 (字段名[(長度)] [ASC |DESC]) ; #方法三:ALTER TABLE在已存在的表上建立索引 ALTER TABLE 表名 ADD [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名 (字段名[(長度)] [ASC |DESC]) ; #刪除索引:DROP INDEX 索引名 ON 表名字;
#方式一 create table t1( id int, name char, age int, sex enum('male','female'), unique key uni_id(id), index ix_name(name) #index沒有key ); create table t1( id int, name char, age int, sex enum('male','female'), unique key uni_id(id), index(name) #index沒有key ); #方式二 create index ix_age on t1(age); #方式三 alter table t1 add index ix_sex(sex); alter table t1 add index(sex); #查看 mysql> show create table t1; | t1 | CREATE TABLE `t1` ( `id` int(11) DEFAULT NULL, `name` char(1) DEFAULT NULL, `age` int(11) DEFAULT NULL, `sex` enum('male','female') DEFAULT NULL, UNIQUE KEY `uni_id` (`id`), KEY `ix_name` (`name`), KEY `ix_age` (`age`), KEY `ix_sex` (`sex`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1
#1. 準備表 create table s1( id int, name varchar(20), gender char(6), email varchar(50) ); #2. 建立存儲過程,實現批量插入記錄 delimiter $$ #聲明存儲過程的結束符號爲$$ create procedure auto_insert1() BEGIN declare i int default 1; while(i<3000000)do insert into s1 values(i,'eva','female',concat('eva',i,'@oldboy')); set i=i+1; end while; END$$ #$$結束 delimiter ; #從新聲明分號爲結束符號 #3. 查看存儲過程 show create procedure auto_insert1\G #4. 調用存儲過程 call auto_insert1();
#無索引:mysql根本就不知道究竟是否存在id等於333333333的記錄,只能把數據表從頭至尾掃描一遍,此時有多少個磁盤塊就須要進行多少IO操做,因此查詢速度很慢 mysql> select * from s1 where id=333333333; Empty set (0.33 sec)
PS:mysql
1. mysql先去索引表裏根據b+樹的搜索原理很快搜索到id等於333333333的記錄不存在,IO大大下降,於是速度明顯提高sql
2. 咱們能夠去mysql的data目錄下找到該表,能夠看到佔用的硬盤空間多了ide
3. 須要注意,以下圖函數
#1. 必定是爲搜索條件的字段建立索引,好比select * from s1 where id = 333;就須要爲id加上索引 #2. 在表中已經有大量數據的狀況下,建索引會很慢,且佔用硬盤空間,建完後查詢速度加快 好比create index idx on s1(id);會掃描表中全部的數據,而後以id爲數據項,建立索引結構,存放於硬盤的表中。 建完之後,再查詢就會很快了。 #3. 須要注意的是:innodb表的索引會存放於s1.ibd文件中,而myisam表的索引則會有單獨的索引文件table1.MYI MySAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。而在innodb中,表數據文件自己就是按照B+Tree(BTree即Balance True)組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,所以innodb表數據文件自己就是主索引。 由於inndob的數據文件要按照主鍵彙集,因此innodb要求表必需要有主鍵(Myisam能夠沒有),若是沒有顯式定義,則mysql系統會自動選擇一個能夠惟一標識數據記錄的列做爲主鍵,若是不存在這種列,則mysql會自動爲innodb表生成一個隱含字段做爲主鍵,這字段的長度爲6個字節,類型爲長整型.
並非說咱們建立了索引就必定會加快查詢速度,若想利用索引達到預想的提升查詢速度的效果,咱們在添加索引時,必須遵循如下問題
測試
條件中出現這些符號或關鍵字:>、>=、<、<=、!= 、between...and...、like、大數據
大於號、小於號spa
儘可能選擇區分度高的列做爲索引,區分度的公式是count(distinct col)/count(*),表示字段不重複的比例,比例越大咱們掃描的記錄數越少,惟一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0,那可能有人會問,這個比例有什麼經驗值嗎?使用場景不一樣,這個值也很難肯定,通常須要join的字段咱們都要求是0.1以上,即平均1條掃描10條記錄3d
mysql> desc s1; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | YES | MUL | NULL | | | name | varchar(20) | YES | | NULL | | | gender | char(5) | YES | | NULL | | | email | varchar(50) | YES | MUL | NULL | | +--------+-------------+------+-----+---------+-------+ rows in set (0.00 sec) mysql> drop index a on s1; Query OK, 0 rows affected (0.20 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> drop index d on s1; Query OK, 0 rows affected (0.18 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> desc s1; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | YES | | NULL | | | name | varchar(20) | YES | | NULL | | | gender | char(5) | YES | | NULL | | | email | varchar(50) | YES | | NULL | | +--------+-------------+------+-----+---------+-------+ rows in set (0.00 sec)
咱們編寫存儲過程爲表s1批量添加記錄,name字段的值均爲egon,也就是說name這個字段的區分度很低(gender字段也是同樣的,咱們稍後再搭理它) 回憶b+樹的結構,查詢的速度與樹的高度成反比,要想將樹的高低控制的很低,須要保證:在某一層內數據項均是按照從左到右,從小到大的順序依次排開,即左1<左2<左3<... 而對於區分度低的字段,沒法找到大小關係,由於值都是相等的,毫無疑問,還想要用b+樹存放這些等值的數據,只能增長樹的高度,字段的區分度越低,則樹的高度越高。極端的狀況,索引字段的值都同樣,那麼b+樹幾乎成了一根棍。本例中就是這種極端的狀況,name字段全部的值均爲'egon' #如今咱們得出一個結論:爲區分度低的字段創建索引,索引樹的高度會很高,然而這具體會帶來什麼影響呢??? #1:若是條件是name='xxxx',那麼確定是能夠第一時間判斷出'xxxx'是不在索引樹中的(由於樹中全部的值均爲'egon’),因此查詢速度很快 #2:若是條件正好是name='egon',查詢時,咱們永遠無
索引列不能在條件中參與計算,保持列「乾淨」,好比from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,緣由很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,須要把全部元素都應用函數才能比較,顯然成本太大。因此語句應該寫成create_time = unix_timestamp(’2014-05-29’)unix
#一、and與or的邏輯 條件1 and 條件2:全部條件都成立纔算成立,但凡要有一個條件不成立則最終結果不成立 條件1 or 條件2:只要有一個條件成立則最終結果就成立 #二、and的工做原理 條件: a = 10 and b = 'xxx' and c > 3 and d =4 索引: 製做聯合索引(d,a,b,c) 工做原理: 對於連續多個and:mysql會按照聯合索引,從左到右的順序找一個區分度高的索引字段(這樣即可以快速鎖定很小的範圍),加速查詢,即按照d—>a->b->c的順序 #三、or的工做原理 條件: a = 10 or b = 'xxx' or c > 3 or d =4 索引: 製做聯合索引(d,a,b,c) 工做原理: 對於連續多個or:mysql會按照條件的順序,從左到右依次判斷,即a->b->c->d
在左邊條件成立可是索引字段的區分度低的狀況下(name與gender均屬於這種狀況),會依次往右找到一個區分度高的索引字段,加速查詢code
通過分析,在條件爲name='egon' and gender='male' and id>333 and email='xxx'的狀況下,咱們徹底不必爲前三個條件的字段加索引,由於只能用上email字段的索引,前三個字段的索引反而會下降咱們的查詢效率
很是重要的原則,對於組合索引mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就中止匹配(指的是範圍大了,有索引速度也慢),好比a = 1 and b = 2 and c > 3 and d = 4 若是創建(a,b,c,d)順序的索引,d是用不到索引的,若是創建(a,b,d,c)的索引則均可以用到,a,b,d的順序能夠任意調整。
使用函數 select * from tb1 where reverse(email) = 'egon'; - 類型不一致 若是列是字符串類型,傳入條件是必須用引號引發來,否則... select * from tb1 where email = 999; #排序條件爲索引,則select字段必須也是索引字段,不然沒法命中 - order by select name from s1 order by email desc; 當根據索引排序時候,select查詢的字段若是不是索引,則速度仍然很慢 select email from s1 order by email desc; 特別的:若是對主鍵排序,則仍是速度很快: select * from tb1 order by nid desc; - 組合索引最左前綴 若是組合索引爲:(name,email) name and email -- 命中索引 name -- 命中索引 email -- 未命中索引 - count(1)或count(列)代替count(*)在mysql中沒有差異了 - create index xxxx on tb(title(19)) #text類型,必須制定長度
- 避免使用select * - 使用count(*) - 建立表時儘可能使用 char 代替 varchar - 表的字段順序固定長度的字段優先 - 組合索引代替多個單列索引(因爲mysql中每次只能使用一個索引,因此常用多個條件查詢時更適合使用組合索引) - 儘可能使用短索引 - 使用鏈接(JOIN)來代替子查詢(Sub-Queries) - 連表時注意條件類型需一致 - 索引散列值(重複少)不適合建索引,例:性別不適合
聯合索引是指對錶上的多個列合起來作一個索引。聯合索引的建立方法與單個索引的建立方法同樣,不一樣之處僅在於有多個索引列,如:
mysql> create table t( -> a int, -> b int, -> primary key(a), -> key idx_a_b(a,b) -> ); Query OK, 0 rows affected (0.11 sec)
InnoDB存儲引擎支持覆蓋索引(covering index,或稱索引覆蓋),即從輔助索引中就能夠獲得查詢記錄,而不須要查詢彙集索引中的記錄。
覆蓋索引 查一個數據不須要回表 select name from 表 where age = 20 不是覆蓋索引 select age from 表 where age =20 是覆蓋索引 select count(age) from 表 where age =20 是覆蓋索引
當咱們爲單獨的一列建立索引的時候 若是條件是這一列,且使用正確就能夠命中索引 當咱們爲兩列分別建立單獨的索引的時候 若是這兩列都是條件,那麼可能只能命中期中一個條件 若是這兩列都是條件,那麼可能會命中兩個索引 - 合併索引 咱們爲多列直接建立聯合因此 條件命中聯合索引
1.條件必定是創建了索引的字段,若是條件使用的字段根本就沒有建立索引,那麼索引不生效 2.若是條件是一個範圍,隨着範圍的值逐漸增大,那麼索引能發揮的做用也越小 3.若是使用like進行模糊查詢,那麼使用a%的形式能命中索引,%a形式不能命中索引 4.儘可能選擇區分度高的字段做爲索引列 5.索引列不能在條件中參與計算,也不能使用函數 6.在多個條件以and相連的時候,會優勢選擇區分度高的索引列來進行查詢 在多個條件以or相連的時候,就是從左到右依次判斷 7.製做聯合索引 1.最左前綴原則 a,b,c,d 條件是a的能命中索引,條件是a,b能命中索引,a,b,c能命中,a,c.... 只要沒有a就不能命中索引 若是在聯合查詢中,老是涉及到同一個字段,那麼就在創建聯合索引的時候將這個字段放在最左側 2.聯合索引 若是按照定義順序,從左到右遇到的第一個在條件中以範圍爲條件的字段,索引失效 儘可能將帶着範圍查詢的字段,定義在聯合索引的最後面 drop index 若是咱們查詢的條件老是多個列合在一塊兒查,那麼就創建聯合索引 create index ind_mix on s1(id,email) select * from s1 where id = 1000000 命中索引 select * from s1 where email = 'eva1000000@oldboy' 未命中索引 但凡是建立了聯合索引,那麼在查詢的時候,再建立順序中從左到右的第一列必須出如今條件中 select count(*) from s1 where id = 1000000 and email = 'eva10%'; 命中索引 select count(*) from s1 where id = 1000000 and email like 'eva10%'; 能夠命中索引 範圍 : select * from s1 where id >3000 and email = 'eva300000@oldboy'; 不能命中索引 8.條件中涉及的字段的值必須和定義表中字段的數據類型一致,不然不能命中索引
執行計劃 看看mysql準備怎麼執行這條語句 能夠看到是否命中索引,計劃能命中哪些,實際命中了哪些,執行的順序,是否發生了索引合併,覆蓋索引 explain select * from s1;