索引的功能就是加速查找,MySQL中的primary key,unique,聯合惟一也都是索引,只是這些索引除了加速查找之外,還有約束功能。html
通常的應用系統,讀寫比例在10:1左右,並且插入操做和通常的更新操做不多出現性能問題,在生產環境中,咱們遇到最多的,也是最容易出問題的,仍是一些複雜的查詢操做,所以對查詢語句的優化顯然是重中之重。提及加速查詢,就不得不提到索引了。索引在MySQL中也叫作「鍵」,是存儲引擎用於快速找到記錄的一種數據結構。索引對於良好的性能
很是關鍵,尤爲是當表中的數據量愈來愈大時,索引對於性能的影響愈發重要。
索引優化應該是對查詢性能優化最有效的手段了。索引可以輕易將查詢性能提升好幾個數量級。mysql
索引是應用程序設計和開發的一個重要方面。若索引太多,應用程序的性能可能會受到影響。而索引太少,對查詢性能又會產生影響,要找到一個平衡點,這對應用程序的性能相當重要。sql
MySQL中經常使用的索引數據庫
索引原理及B+樹的內容看我另外一篇博客:https://www.cnblogs.com/kuxingseng95/articles/9559097.html緩存
#咱們能夠在建立上述索引的時候,爲其指定索引類型,分兩類 hash類型的索引:查詢單條快,範圍查詢慢 btree類型的索引:b+樹,層數越多,數據量指數級增加(咱們就用它,由於innodb默認支持它) #不一樣的存儲引擎支持的索引類型也不同 InnoDB 支持事務,支持行級別鎖定,支持 B-tree、Full-text 等索引,不支持 Hash 索引; MyISAM 不支持事務,支持表級別鎖定,支持 B-tree、Full-text 等索引,不支持 Hash 索引; Memory 不支持事務,支持表級別鎖定,支持 B-tree、Hash 等索引,不支持 Full-text 索引; NDB 支持事務,支持行級別鎖定,支持 Hash 索引,不支持 B-tree、Full-text 等索引; Archive 不支持事務,支持表級別鎖定,不支持 B-tree、Hash、Full-text 等索引;
關於兩種類型,推薦博客:http://www.javashuo.com/article/p-cevbwntc-bh.html性能優化
#方法一:建立表時 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 index ix_age on t1(age); #方式三 alter table t1 add index ix_sex(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 示範
關於查詢須要瞭解的數據結構
並非說咱們創建了索引就必定會加快查詢的速度,若是想要利用索引達到預想的提升查詢速度的效果,咱們在添加索引時,必須考慮如下問題。ide
1.範圍問題函數
當條件中出現>,>=,<,<=,!=,between...and..,like這樣的條件不許確的符號或者關鍵字性能
除了like的符號或者關鍵字要看查找的範圍大小了。
當like匹配的字符中有%,可是處於開頭的時候,速度慢。其餘狀況速度仍是很快的。
2.儘可能選擇區分度高的列做爲索引
區分度的公式是count(distinct col)/count(*),表示字段不重複的比例,比例越大咱們掃描的記錄數越少,惟一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0,那可能有人會問,這個比例有什麼經驗值嗎?使用場景不一樣,這個值也很難肯定,通常須要join的字段咱們都要求是0.1以上,
3. =和in能夠亂序,好比a = 1 and b = 2 and c = 3 創建(a,b,c)索引能夠任意順序,mysql的查詢優化器會幫你優化成索引能夠識別的形式
4.索引列不能參與計算,保持列「乾淨」,好比id*3 = 3000。緣由很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,須要把全部元素都應用函數才能比較,顯然成本太大。因此語句應該寫成create_time = unix_timestamp(’2014-05-29’)
5.and/or
#一、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='egon' and gender='male' and id>333 and email='xxx'的狀況下,咱們徹底不必爲前三個條件的字段加索引,由於只能用上email字段的索引,前三個字段的索引反而會下降咱們的查詢效率
6 最左前綴匹配原則(詳見第八小節),很是重要的原則,對於組合索引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 * - count(1)或count(列) 代替 count(*) - 建立表時儘可能時 char 代替 varchar - 表的字段順序固定長度的字段優先 - 組合索引代替多個單列索引(常用多個條件查詢時) - 儘可能使用短索引 - 使用鏈接(JOIN)來代替子查詢(Sub-Queries) - 連表時注意條件類型需一致 - 索引散列值(重複少)不適合建索引,例:性別不適合 - 使用函數 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類型,必須制定長度
補充:查詢優化之——explain:【轉】https://www.jianshu.com/p/ea3fc71fdc45
關於使用,看後面的例子。
若是是爲了更快查詢到數據,有單列索引不是ok了,爲何有‘聯合索引’的存在?
如今你們廣泛的說法是由於,查詢條件出現相似這類狀況時‘where xx=xx && xx=xx && xx>xx’使用聯合索引會比單列索引高效,因此要使用多列索引,可是通過測試,事情並不是如此,反而是單列索引處理時間比多列索引還快....
事實上建立多列索引的意義就是爲了‘減小io操做’
聯合索引的建立
聯合索引時指對錶上的多個列合起來作一個索引。聯合索引的建立方法與單個索引的建立方法同樣,不一樣之處在僅在於有多個索引列,以下
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)
那麼什麼時候須要使用聯合索引呢?在討論這個問題以前,先來看一下聯合索引內部的結果。從本質上來講,聯合索引就是一棵B+樹,不一樣的是聯合索引的鍵值得數量不是1,而是>=2。接着來討論兩個整型列組成的聯合索引,假定兩個鍵值得名稱分別爲a、b如圖
能夠看到這與咱們以前看到的單個鍵的B+樹並無什麼不一樣,鍵值都是排序的,經過葉子結點能夠邏輯上順序地讀出全部數據,就上面的例子來講,即(1,1),(1,2),(2,1),(2,4),(3,1),(3,2),數據按(a,b)的順序進行了存放。
所以,對於查詢select * from table where a=xxx and b=xxx, 顯然是可使用(a,b) 這個聯合索引的,對於單個列a的查詢select * from table where a=xxx,也是可使用(a,b)這個索引的。
但對於b列的查詢select * from table where b=xxx,則不可使用(a,b) 索引,其實你不難發現緣由,葉子節點上b的值爲一、二、一、四、一、2顯然不是排序的,所以對於b列的查詢使用不到(a,b) 索引
聯合索引的第二個好處是在第一個鍵相同的狀況下,已經對第二個鍵進行了排序處理,例如在不少狀況下應用程序都須要查詢某個用戶的購物狀況,並按照時間進行排序,最後取出最近三次的購買記錄,這時使用聯合索引能夠幫咱們避免多一次的排序操做,由於索引自己在葉子節點已經排序了,以下
例子:
#===========準備表============== create table buy_log( userid int unsigned not null, buy_date date ); insert into buy_log values (1,'2009-01-01'), (2,'2009-01-01'), (3,'2009-01-01'), (1,'2009-02-01'), (3,'2009-02-01'), (1,'2009-03-01'), (1,'2009-04-01'); alter table buy_log add key(userid); alter table buy_log add key(userid,buy_date); #===========驗證============== mysql> show create table buy_log; | buy_log | CREATE TABLE `buy_log` ( `userid` int(10) unsigned NOT NULL, `buy_date` date DEFAULT NULL, KEY `userid` (`userid`), KEY `userid_2` (`userid`,`buy_date`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 | #能夠看到possible_keys在這裏有兩個索引能夠用,分別是單個索引userid與聯合索引userid_2,可是優化器最終選擇了使用的key是userid由於該索引的葉子節點包含單個鍵值,因此理論上一個頁能存放的記錄應該更多 mysql> explain select * from buy_log where userid=2; +----+-------------+---------+------+-----------------+--------+---------+-------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+-----------------+--------+---------+-------+------+-------+ | 1 | SIMPLE | buy_log | ref | userid,userid_2 | userid | 4 | const | 1 | | +----+-------------+---------+------+-----------------+--------+---------+-------+------+-------+ row in set (0.00 sec) #接着假定要取出userid爲1的最近3次的購買記錄,用的就是聯合索引userid_2了,由於在這個索引中,在userid=1的狀況下,buy_date都已經排序好了 mysql> explain select * from buy_log where userid=1 order by buy_date desc limit 3; +----+-------------+---------+------+-----------------+----------+---------+-------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+------+-----------------+----------+---------+-------+------+--------------------------+ | 1 | SIMPLE | buy_log | ref | userid,userid_2 | userid_2 | 4 | const | 4 | Using where; Using index | +----+-------------+---------+------+-----------------+----------+---------+-------+------+--------------------------+ row in set (0.00 sec) #ps:若是extra的排序顯示是Using filesort,則意味着在查出數據後須要二次排序(以下查詢語句,沒有先用where userid=3先定位範圍,因而即使命中索引也沒用,須要二次排序) mysql> explain select * from buy_log order by buy_date desc limit 3; +----+-------------+---------+-------+---------------+----------+---------+------+------+-----------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------+---------------+----------+---------+------+------+-----------------------------+ | 1 | SIMPLE | buy_log | index | NULL | userid_2 | 8 | NULL | 7 | Using index; Using filesort | +----+-------------+---------+-------+---------------+----------+---------+------+------+-----------------------------+ #對於聯合索引(a,b),下述語句能夠直接使用該索引,無需二次排序 select ... from table where a=xxx order by b; #而後對於聯合索引(a,b,c)來首,下列語句一樣能夠直接經過索引獲得結果 select ... from table where a=xxx order by b; select ... from table where a=xxx and b=xxx order by c; #可是對於聯合索引(a,b,c),下列語句不能經過索引直接獲得結果,還須要本身執行一次filesort操做,由於索引(a,c)並未排序 select ... from table where a=xxx order by c;
InnoDB存儲引擎支持覆蓋索引(covering index,或稱索引覆蓋),即從輔助索引中就能夠獲得查詢記錄,而不須要查詢彙集索引中的記錄。
使用覆蓋索引的一個好處是:輔助索引不包含整行記錄的全部信息,故其大小要遠小於彙集索引,所以能夠減小大量的IO操做
注意:覆蓋索引技術最先是在InnoDB Plugin中完成並實現,這意味着對於InnoDB版本小於1.0的,或者MySQL數據庫版本爲5.0如下的,InnoDB存儲引擎不支持覆蓋索引特性
對於InnoDB存儲引擎的輔助索引而言,因爲其包含了主鍵信息,所以其葉子節點存放的數據爲(primary key1,priamey key2,...,key1,key2,...)。例如
例子:
select age from s1 where id=123 and name = 'liming'; #id字段有索引,可是name字段沒有索引,該sql命中了索引,但未覆蓋,須要去彙集索引中再查找詳細信息。 最牛逼的狀況是,索引字段覆蓋了全部,那全程經過索引來加速查詢以及獲取結果就ok了 mysql> desc s1; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | NO | | NULL | | | name | varchar(20) | YES | | NULL | | | gender | char(6) | YES | | NULL | | | email | varchar(50) | YES | | NULL | | +--------+-------------+------+-----+---------+-------+ rows in set (0.21 sec) mysql> explain select name from s1 where id=1000; #沒有任何索引 +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+ | 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 2688336 | 10.00 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+ row in set, 1 warning (0.00 sec) mysql> create index idx_id on s1(id); #建立索引 Query OK, 0 rows affected (4.16 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain select name from s1 where id=1000; #命中輔助索引,可是未覆蓋索引,還須要從彙集索引中查找name +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+ | 1 | SIMPLE | s1 | NULL | ref | idx_id | idx_id | 4 | const | 1 | 100.00 | NULL | +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+ row in set, 1 warning (0.08 sec) mysql> explain select id from s1 where id=1000; #在輔助索引中就找到了所有信息,Using index表明覆蓋索引 +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+ | 1 | SIMPLE | s1 | NULL | ref | idx_id | idx_id | 4 | const | 1 | 100.00 | Using index | +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+ row in set, 1 warning (0.03 sec)
從上面的例子能夠看出來,覆蓋索引的另外一個好處就是快速統計。
mysql> explain select count(*) from buy_log; +----+-------------+---------+-------+---------------+--------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+---------+-------+---------------+--------+---------+------+------+-------------+ | 1 | SIMPLE | buy_log | index | NULL | userid | 4 | NULL | 7 | Using index | +----+-------------+---------+-------+---------------+--------+---------+------+------+-------------+ 1 row in set (0.00 sec)
0.先運行看看是否真的很慢,注意設置SQL_NO_CACHE,講緩存關掉。 1.where條件單表查,鎖定最小返回記錄表。這句話的意思是把查詢語句的where都應用到表中返回的記錄數最小的表開始查起,單表每一個字段分別查詢,看哪一個字段的區分度最高 2.explain查看執行計劃,是否與1預期一致(從鎖定記錄較少的表開始查詢) 3.order by limit 形式的sql語句讓排序的表優先查 4.瞭解業務方使用場景 5.加索引時參照建索引的幾大原則 6.觀察結果,不符合預期繼續從0分析