在瞭解數據庫索引以前,首先有必要了解一下數據庫索引的數據結構基礎,那麼什麼樣的數據結構能夠做爲索引呢?php
B-tree是最經常使用的用於索引的數據結構。由於它們是時間複雜度低, 查找、刪除、插入操做均可以能夠在對數時間內完成。另一個重要緣由存儲在B-Tree中的數據是有序的。數據庫管理系統(RDBMS)一般決定索引應該用哪些數據結構。可是,在某些狀況下,你在建立索引時能夠指定索引要使用的數據結構。css
B+是一個樹數據結構,一般用於數據庫和操做系統的文件系統中,B+樹的特色是可以保持數據穩定有序,其插入與修改擁有比較穩定的對數時間複雜度,B+樹元素自底向上插入,這個和二叉樹恰好相反。html
B+tree 是一個n叉樹,每一個節點有多個葉子節點,一顆B+樹包含根節點,內部節點,葉子節點。根節點多是一個葉子節點,也多是一個包含兩個或兩個以上葉子節點的節點。python
B+tree的性質:mysql
1.n棵子tree的節點包含n個關鍵字,不用來保存數據而是保存數據的索引。git
2.全部的葉子結點中包含了所有關鍵字的信息,及指向含這些關鍵字記錄的指針,且葉子結點自己依關鍵字的大小自小而大順序連接。面試
3.全部的非終端結點能夠當作是索引部分,結點中僅含其子樹中的最大(或最小)關鍵字。算法
因爲B+tree的性質, 它一般被用於數據庫和操做系統的文件系統中。NTFS, ReiserFS, NSS, XFS, JFS, ReFS 和BFS等文件系統都在使用B+樹做爲元數據索引,由於B+ 樹的特色是可以保持數據穩定有序,其插入與修改擁有較穩定的對數時間複雜度(B+ 樹元素自底向上插入)。sql
B+tree結構原型圖大概以下(引用):數據庫
哈希表是另一種你可能看到用做索引的數據結構-這些索引一般被稱爲哈希索引。使用哈希索引的緣由是,在尋找值時哈希表效率極高。因此,若是使用哈希索引,對於比較字符串是否相等的查詢可以極快的檢索出的值。
哈希表是無順的數據結構,對於不少類型的查詢語句哈希索引都無能爲力。舉例來講,假如你想要找出全部小於40歲的員工。你怎麼使用使用哈希索引進行查詢?這不可行,由於哈希表只適合查詢鍵值對-也就是說查詢相等的查詢(例:like 「WHERE name = ‘Jesus’)。哈希表的鍵值映射也暗示其鍵的存儲是無序的。這就是爲何哈希索引一般不是數據庫索引的默認數據結構-由於在做爲索引的數據結構時,其不像B-Tree那麼靈活
使用R-Tree做爲數據結構的索引一般用來爲空間問題提供幫助。例如,一個查詢要求「查詢出全部距離我兩千米以內的星巴克」,若是數據庫表使用R- Tree索引,這類查詢的效率將會提升。
另外一種索引是位圖索引(bitmap index), 這類索引適合放在包含布爾值(true 和 false)的列上,可是這些值(表示true或false的值)的許多實例-基本上都是選擇性(selectivity)低的列。
2.一、索引
索引是對數據庫表中一列或者多列的值進行排序的一種結構,使用索引能夠快速訪問數據庫表中的特定信息,索引有助於更快的獲取信息。索引是表的目錄,在查找內容以前能夠先在目錄中查找索引位置,以此快速定位查詢數據。對於索引,會保存在額外的文件中。
索引的一個主要的目的就是加快檢索表中數據的方法,也能夠協助信息搜尋者能儘快的找到符合限制條件的記錄。
2.二、索引種類
-1,建立表 + 索引
create table in1( nid int not null auto_increment primary key, name varchar(32) not null, email varchar(64) not null, extra text, index ix_name (name) )
-2,建立表
create index index_name on table_name(column_name)
-3,刪除表
drop index_name on table_name;
-4,查看索引
show index from table_name;
注意:對於建立索引時若是是BLOB 和 TEXT 類型,必須指定length。
create index ix_extra on in1(extra(32));
-1,建立表 + 惟一索引
create table in1( nid int not null auto_increment primary key, name varchar(32) not null, email varchar(64) not null, extra text, unique ix_name (name) )
-2,建立惟一索引
create unique index 索引名 on 表名(列名)
-3,刪除惟一索引
drop unique index 索引名 on 表名
-1,建立表+建立主鍵
create table in1( nid int not null auto_increment primary key, name varchar(32) not null, email varchar(64) not null, extra text, index ix_name (name) ) OR create table in1( nid int not null auto_increment, name varchar(32) not null, email varchar(64) not null, extra text, primary key(ni1), index ix_name (name) )
-2,建立主鍵
alter table 表名 add primary key(列名);
-3,刪除主鍵
alter table 表名 drop primary key; alter table 表名 modify 列名 int, drop primary key;
組合索引是將n個列組合成一個索引
其應用場景爲:頻繁的同時使用n列來進行查詢,如:where n1 = 'alex' and n2 = 666。
-1,建立表
create table in3( nid int not null auto_increment primary key, name varchar(32) not null, email varchar(64) not null, extra text )
-2,建立組合索引
create index ix_name_email on in3(name,email);
如上建立組合索引以後,查詢:
注意:對於同時搜索n個條件時,組合索引的性能好於多個單一索引合併。
-1,條件語句(if條件語句例子)
delimiter \\ CREATE PROCEDURE proc_if () BEGIN declare i int default 0; if i = 1 THEN SELECT 1; ELSEIF i = 2 THEN SELECT 2; ELSE SELECT 7; END IF; END\\ delimiter ;
-2,while循環語句
delimiter \\ CREATE PROCEDURE proc_while () BEGIN DECLARE num INT ; SET num = 0 ; WHILE num < 10 DO SELECT num ; SET num = num + 1 ; END WHILE ; END\\ delimiter ;
-3,repeat循環語句
delimiter \\ CREATE PROCEDURE proc_repeat () BEGIN DECLARE i INT ; SET i = 0 ; repeat select i; set i = i + 1; until i >= 5 end repeat; END\\ delimiter ;
-4,loop循環語句
BEGIN declare i int default 0; loop_label: loop set i=i+1; if i<8 then iterate loop_label; end if; if i>=10 then leave loop_label; end if; select i; end loop loop_label; END
-5,動態執行SQL語句
delimiter \\ DROP PROCEDURE IF EXISTS proc_sql \\ CREATE PROCEDURE proc_sql () BEGIN declare p1 int; set p1 = 11; set @p1 = p1; PREPARE prod FROM 'select * from tb2 where nid > ?'; EXECUTE prod USING @p1; DEALLOCATE prepare prod; END\\ delimiter ;
查看錶結構 desc 表名 - 查看生成表的SQL show create table 表名 - 查看索引 show index from 表名 - 查看執行時間 set profiling = 1; SQL... show profiles;
因爲索引是專門用於加速搜索而生,因此加上索引以後,查詢效率會快到飛起來。 # 有索引 mysql> select * from tb1 where name = 'wupeiqi-888'; +-----+-------------+---------------------+----------------------------------+---------------------+ | nid | name | email | radom | ctime | +-----+-------------+---------------------+----------------------------------+---------------------+ | 255 | tonm | 12474565666@qq.com | cdccccce76a16a90b8a8301d5314204b | 2017-08-03 09:33:35 | +-----+-------------+---------------------+----------------------------------+---------------------+ 1 row in set (0.00 sec) # 無索引 mysql> select * from tb1 where email = 'wupeiqi888@live.com'; +-----+-------------+---------------------+----------------------------------+---------------------+ | nid | name | email | radom | ctime | +-----+-------------+---------------------+----------------------------------+---------------------+ | 256 | tonm | 12474565666@qq.com | 5312269e76a1clslclscc01d5314204b | 2017-08-03 09:33:35 | +-----+-------------+---------------------+----------------------------------+---------------------+ 1 row in set (1.23 sec)
2五、正確使用索引
數據庫表中添加索引後確實會讓查詢速度起飛,但前提必須是正確的使用索引來查詢,若是以錯誤的方式使用,則即便創建索引也會不奏效。
即便創建索引,索引也不會生效:
- like '%xx' select * from tb1 where name like '%cn'; - 使用函數 select * from tb1 where reverse(name) = 'wupeiqi'; - or select * from tb1 where nid = 1 or email = 'seven@live.com'; 特別的:當or條件中有未創建索引的列才失效,如下會走索引 select * from tb1 where nid = 1 or name = 'seven'; select * from tb1 where nid = 1 or email = 'seven@live.com' and name = 'alex' - 類型不一致 若是列是字符串類型,傳入條件是必須用引號引發來,否則... select * from tb1 where name = 999; - != select * from tb1 where name != 'alex' 特別的:若是是主鍵,則仍是會走索引 select * from tb1 where nid != 123 - > select * from tb1 where name > 'alex' 特別的:若是是主鍵或索引是整數類型,則仍是會走索引 select * from tb1 where nid > 123 select * from tb1 where num > 123 - order by select email from tb1 order by name desc; 當根據索引排序時候,選擇的映射若是不是索引,則不走索引 特別的:若是對主鍵排序,則仍是走索引: select * from tb1 order by nid desc; - 組合索引最左前綴 若是組合索引爲:(name,email) name and email -- 使用索引 name -- 使用索引 email -- 不使用索引
一個很是好的類比是把數據庫索引看做是書的索引。若是你有一本關於狗的書,你想要找關於‘黃金獵犬’的那部分。當你能夠經過在書背的索引找到哪幾頁有關於‘黃金獵犬’信息的時候,你爲何要翻完正本書 - 這至關於數據庫中的全表掃描。一樣的,就像一本書的索引包含頁碼同樣,數據庫的索引包含了指針,指向你在SQL中想要查詢的值所在的行。
那麼,使用數據庫索引有什麼缺點呢?其一,索引會佔用空間 - 你的表越大,索引佔用的空間越大。其二,性能損失(主要值更新操做),當你在表中添加、刪除或者更新行數據的時候, 在索引中也會有相同的操做。
記住:創建在某列(或多列)索引須要保存該列最新的數據。
基本原則是隻若是表中某列在查詢過程當中使用的很是頻繁,那就在該列上建立索引。
3.三、limit分頁
不管是否有索引,limit分頁是一個值得關注的問題
每頁顯示10條: 當前 118 120, 125 倒序: 大 小 970 7 6 6 5 54 43 32 19 98 下一頁: select * from tb1 where nid < (select nid from (select nid from tb1 where nid < 當前頁最小值 order by nid desc limit 每頁數據 *【頁碼-當前頁】) A order by A.nid asc limit 1) order by nid desc limit 10; select * from tb1 where nid < (select nid from (select nid from tb1 where nid < 970 order by nid desc limit 40) A order by A.nid asc limit 1) order by nid desc limit 10; 上一頁: select * from tb1 where nid < (select nid from (select nid from tb1 where nid > 當前頁最大值 order by nid asc limit 每頁數據 *【當前頁-頁碼】) A order by A.nid asc limit 1) order by nid desc limit 10; select * from tb1 where nid < (select nid from (select nid from tb1 where nid > 980 order by nid asc limit 20) A order by A.nid desc limit 1) order by nid desc limit 10;
3.四、執行計劃
explain + 查詢SQL - 用於顯示SQL執行信息參數,根據參考信息能夠進行SQL優化
mysql> explain select * from tb2; +----+-------------+-------+------+---------------+------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------+ | 1 | SIMPLE | tb2 | ALL | NULL | NULL | NULL | NULL | 2 | NULL | +----+-------------+-------+------+---------------+------+---------+------+------+-------+ 1 row in set (0.00 sec)
id 查詢順序標識 如:mysql> explain select * from (select nid,name from tb1 where nid < 10) as B; +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+ | 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 9 | NULL | | 2 | DERIVED | tb1 | range | PRIMARY | PRIMARY | 8 | NULL | 9 | Using where | +----+-------------+------------+-------+---------------+---------+---------+------+------+-------------+ 特別的:若是使用union鏈接氣值可能爲null select_type 查詢類型 SIMPLE 簡單查詢 PRIMARY 最外層查詢 SUBQUERY 映射爲子查詢 DERIVED 子查詢 UNION 聯合 UNION RESULT 使用聯合的結果 ... table 正在訪問的表名 type 查詢時的訪問方式,性能:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const ALL 全表掃描,對於數據表從頭至尾找一遍 select * from tb1; 特別的:若是有limit限制,則找到以後就不在繼續向下掃描 select * from tb1 where email = 'seven@live.com' select * from tb1 where email = 'seven@live.com' limit 1; 雖然上述兩個語句都會進行全表掃描,第二句使用了limit,則找到一個後就再也不繼續掃描。 INDEX 全索引掃描,對索引從頭至尾找一遍 select nid from tb1; RANGE 對索引列進行範圍查找 select * from tb1 where name < 'alex'; PS: between and in > >= < <= 操做 注意:!= 和 > 符號 INDEX_MERGE 合併索引,使用多個單列索引搜索 select * from tb1 where name = 'alex' or nid in (11,22,33); REF 根據索引查找一個或多個值 select * from tb1 where name = 'seven'; EQ_REF 鏈接時使用primary key 或 unique類型 select tb2.nid,tb1.name from tb2 left join tb1 on tb2.nid = tb1.nid; CONST 常量 表最多有一個匹配行,由於僅有一行,在這行的列值可被優化器剩餘部分認爲是常數,const表很快,由於它們只讀取一次。 select nid from tb1 where nid = 2 ; SYSTEM 系統 表僅有一行(=系統表)。這是const聯接類型的一個特例。 select * from (select nid from tb1 where nid = 1) as A; possible_keys 可能使用的索引 key 真實使用的 key_len MySQL中使用索引字節長度 rows mysql估計爲了找到所需的行而要讀取的行數 ------ 只是預估值 extra 該列包含MySQL解決查詢的詳細信息 「Using index」 此值表示mysql將使用覆蓋索引,以免訪問表。不要把覆蓋索引和index訪問類型弄混了。 「Using where」 這意味着mysql服務器將在存儲引擎檢索行後再進行過濾,許多where條件裏涉及索引中的列,當(而且若是)它讀取索引時,就能被存儲引擎檢驗,所以不是全部帶where子句的查詢都會顯示「Using where」。有時「Using where」的出現就是一個暗示:查詢可受益於不一樣的索引。 「Using temporary」 這意味着mysql在對查詢結果排序時會使用一個臨時表。 「Using filesort」 這意味着mysql會對結果使用一個外部索引排序,而不是按索引次序從表裏讀取行。mysql有兩種文件排序算法,這兩種排序方式均可以在內存或者磁盤上完成,explain不會告訴你mysql將使用哪種文件排序,也不會告訴你排序會在內存裏仍是磁盤上完成。 「Range checked for each record(index map: N)」 這個意味着沒有好用的索引,新的索引將在聯接的每一行上從新估算,N是顯示在possible_keys列中索引的位圖,而且是冗餘的。 詳細
1)、id列數字越大越先執行,若是說數字同樣大,那麼就從上往下依次執行,id列爲null的就表是這是一個結果集,不須要使用它來進行查詢。
3.五、慢日誌查詢
a、配置MySQL自動記錄慢日誌
slow_query_log = OFF 是否開啓慢日誌記錄
long_query_time = 2 時間限制,超過此時間,則記錄
slow_query_log_file = /usr/slow.log 日誌文件
log_queries_not_using_indexes = OFF 爲使用索引的搜索是否記錄
注:查看當前配置信息:
show variables like '%query%'
修改當前配置:
set global 變量名 = 值
b、查看MySQL慢日誌
mysqldumpslow -s at -a /usr/local/var/mysql/MacBook-Pro-3-slow.log
""" --verbose 版本 --debug 調試 --help 幫助 -v 版本 -d 調試模式 -s ORDER 排序方式 what to sort by (al, at, ar, c, l, r, t), 'at' is default al: average lock time ar: average rows sent at: average query time c: count l: lock time r: rows sent t: query time -r 反轉順序,默認文件倒序拍。reverse the sort order (largest last instead of first) -t NUM 顯示前N條just show the top n queries -a 不要將SQL中數字轉換成N,字符串轉換成S。don't abstract all numbers to N and strings to 'S' -n NUM abstract numbers with at least n digits within names -g PATTERN 正則匹配;grep: only consider stmts that include this string -h HOSTNAME mysql機器名或者IP;hostname of db server for *-slow.log filename (can be wildcard), default is '*', i.e. match all -i NAME name of server instance (if using mysql.server startup script) -l 總時間中不減去鎖定時間;don't subtract lock time from total time """
數據庫的索引很是重要,基本面試數據庫的問題都在索引上,因此這裏小編整理出來,一方面爲了本身複習,一方面也方便你們。
(關於MySQL的安裝,具體見下面博客:http://www.cnblogs.com/wj-1314/p/7573242.html)
(關於MySQL的基礎知識,具體見下面博客:http://www.cnblogs.com/wj-1314/p/8343101.html)
(關於MySQL的筆試知識,具體見下面博客:http://www.cnblogs.com/wj-1314/p/7643125.html)
此篇博客主要參考:http://www.cnblogs.com/wupeiqi/articles/5716963.html;http://www.cnblogs.com/xiaoboluo768/p/5400990.html,寫在這裏主要是爲了鞏固學習知識,同時但願更多的同窗學習。