一,索引前傳
在了解數據庫索引之前,首先有必要了解一下數據庫索引的數據結構基礎,那么什么樣的數據結構可以作為索引呢?
B-tree是最常用的用於索引的數據結構。因為它們是時間復雜度低, 查找、刪除、插入操作都可以可以在對數時間內完成。另外一個重要原因存儲在B-Tree中的數據是有序的。數據庫管理系統(RDBMS)通常決定索引應該用哪些數據結構。但是,在某些情況下,你在創建索引時可以指定索引要使用的數據結構。
B+是一個樹數據結構,通常用於數據庫和操作系統的文件系統中,B+樹的特點是能夠保持數據穩定有序,其插入與修改擁有比較穩定的對數時間復雜度,B+樹元素自底向上插入,這個和二叉樹剛好相反。
B+tree 是一個n叉樹,每個節點有多個葉子節點,一顆B+樹包含根節點,內部節點,葉子節點。根節點可能是一個葉子節點,也可能是一個包含兩個或兩個以上葉子節點的節點。
B+tree的性質:
1.n棵子tree的節點包含n個關鍵字,不用來保存數據而是保存數據的索引。
2.所有的葉子結點中包含了全部關鍵字的信息,及指向含這些關鍵字記錄的指針,且葉子結點本身依關鍵字的大小自小而大順序鏈接。
3.所有的非終端結點可以看成是索引部分,結點中僅含其子樹中的最大(或最小)關鍵字。
由於B+tree的性質, 它通常被用於數據庫和操作系統的文件系統中。NTFS, ReiserFS, NSS, XFS, JFS, ReFS 和BFS等文件系統都在使用B+樹作為元數據索引,因為B+ 樹的特點是能夠保持數據穩定有序,其插入與修改擁有較穩定的對數時間復雜度(B+ 樹元素自底向上插入)。
B+tree結構原型圖大概如下(引用):
哈希表索引是怎么工作的?
哈希表是另外一種你可能看到用作索引的數據結構-這些索引通常被稱為哈希索引。使用哈希索引的原因是,在尋找值時哈希表效率極高。所以,如果使用哈希索引,對於比較字符串是否相等的查詢能夠極快的檢索出的值。
哈希索引的缺點是什么呢?
哈希表是無順的數據結構,對於很多類型的查詢語句哈希索引都無能為力。舉例來說,假如你想要找出所有小於40歲的員工。你怎么使用使用哈希索引進行查詢?這不可行,因為哈希表只適合查詢鍵值對-也就是說查詢相等的查詢(例:like “WHERE name = ‘Jesus’)。哈希表的鍵值映射也暗示其鍵的存儲是無序的。這就是為什么哈希索引通常不是數據庫索引的默認數據結構-因為在作為索引的數據結構時,其不像B-Tree那么靈活
還有什么其他類型的索引?
使用R-Tree作為數據結構的索引通常用來為空間問題提供幫助。例如,一個查詢要求“查詢出所有距離我兩公里之內的星巴克”,如果數據庫表使用R- Tree索引,這類查詢的效率將會提高。
另一種索引是位圖索引(bitmap index), 這類索引適合放在包含布爾值(true 和 false)的列上,但是這些值(表示true或false的值)的許多實例-基本上都是選擇性(selectivity)低的列。
二,索引內容
2.1、索引
索引是對數據庫表中一列或者多列的值進行排序的一種結構,使用索引可以快速訪問數據庫表中的特定信息,索引有助於更快的獲取信息。索引是表的目錄,在查找內容之前可以先在目錄中查找索引位置,以此快速定位查詢數據。對於索引,會保存在額外的文件中。
索引的一個主要的目的就是加快檢索表中數據的方法,也可以協助信息搜尋者能盡快的找到符合限制條件的記錄。
2.2、索引種類
- 普通索引:僅加速查詢
- 唯一索引:加速查詢 + 列值唯一(可以有null)
- 唯一索引是不允許其中任何兩行具有相同索引值的索引,當現有數據存在大量的重復的鍵值的時候,大多數數據庫不允許唯一索引與表一起保存,數據庫還可能防止添加將表中創建重復鍵值的新數據。
- 主鍵索引:加速查詢 + 列值唯一 + 表中只有一個(不可以有null)
- 數據庫表經常有一列或者多列組合,其值唯一標識表中的每一行,每一列稱為表的主鍵,在數據庫關系圖中為表定義主鍵將自動創建主鍵索引,主鍵索引是唯一索引的特定類型,該索引要求主鍵索引的每個值都唯一。
- 組合索引:多列值組成一個索引,
專門用於組合搜索,其效率大於索引合並 - 全文索引:對文本的內容進行分詞,進行搜索
——普通索引
-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);
如上創建組合索引之后,查詢:
- name and email -- 使用索引
- 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 ;
2.3、相關命令
查看表結構 desc 表名 - 查看生成表的SQL show create table 表名 - 查看索引 show index from 表名 - 查看執行時間 set profiling = 1; SQL... show profiles;
2.4、索引的優缺點(使用索引和不使用索引)
-
在設計數據庫時,通過創建一個惟一的索引,能夠在索引和信息之間形成一對一的映射式的對應關系,增加數據的惟一性特點。
-
能提高數據的搜索及檢索速度,符合數據庫建立的初衷。
-
能夠加快表與表之間的連接速度,這對於提高數據的參考完整性方面具有重要作用。
-
在信息檢索過程中,若使用分組及排序子句進行時,通過建立索引能有效的減少檢索過程中所需的分組及排序時間,提高檢索效率。
-
建立索引之后,在信息查詢過程中可以使用優化隱藏器,這對於提高整個信息檢索系統的性能具有重要意義。
-
在數據庫建立過程中,需花費較多的時間去建立並維護索引,特別是隨着數據總量的增加,所花費的時間將不斷遞增。
-
在數據庫中創建的索引需要占用一定的物理存儲空間,這其中就包括數據表所占的數據空間以及所創建的每一個索引所占用的物理空間,如果有必要建立起聚簇索引,所占用的空間還將進一步的增加
-
在對表中的數據進行修改時,例如對其進行增加、刪除或者是修改操作時,索引還需要進行動態的維護,這給數據庫的維護速度帶來了一定的麻煩。
由於索引是專門用於加速搜索而生,所以加上索引之后,查詢效率會快到飛起來。 # 有索引 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)
25、正確使用索引
數據庫表中添加索引后確實會讓查詢速度起飛,但前提必須是正確的使用索引來查詢,如果以錯誤的方式使用,則即使建立索引也會不奏效。
即使建立索引,索引也不會生效:
- 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 -- 不使用索引
2.6,使用索引注意事項
三,索引后記
3.1把數據庫索引類比成什么比較好呢?
一個非常好的類比是把數據庫索引看作是書的索引。如果你有一本關於狗的書,你想要找關於‘黃金獵犬’的那部分。當你可以通過在書背的索引找到哪幾頁有關於‘黃金獵犬’信息的時候,你為什么要翻完正本書 - 這相當於數據庫中的全表掃描。同樣的,就像一本書的索引包含頁碼一樣,數據庫的索引包含了指針,指向你在SQL中想要查詢的值所在的行。
3.2使用數據庫索引會有什么代價?
那么,使用數據庫索引有什么缺點呢?其一,索引會占用空間 - 你的表越大,索引占用的空間越大。其二,性能損失(主要值更新操作),當你在表中添加、刪除或者更新行數據的時候, 在索引中也會有相同的操作。
記住:建立在某列(或多列)索引需要保存該列最新的數據。
基本原則是只如果表中某列在查詢過程中使用的非常頻繁,那就在該列上創建索引。
3.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.4、執行計划
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.5、慢日志查詢
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,寫在這里主要是為了鞏固學習知識,同時希望更多的同學學習。