提供了相似於書中目錄的做用,目的是爲了優化查詢
B樹索引 Hash索引 R樹 Full text GIS
B-tree B+Tree 在範圍查詢方面提供了更好的性能(> < >= <= like) B*Tree
(1). 索引是基於表中,列(索引鍵)的值生成的B樹結構 (2). 首先提取此列全部的值,進行自動排序 (3). 將排好序的值,均勻的分佈到索引樹的葉子節點中(16K) (4). 而後生成此索引鍵值所對應得後端數據頁的指針 (5). 生成枝節點和根節點,根據數據量級和索引鍵長度,生成合適的索引樹高度 id name age gender select * from t1 where id=10; 問題: 基於索引鍵作where查詢,對於id列是順序IO,可是對於其餘列的查詢,多是隨機IO.
(1)表中設置了主鍵,主鍵列就會自動被做爲彙集索引. (2)若是沒有主鍵,會選擇惟一鍵做爲彙集索引. (3)彙集索引必須在建表時纔有意義,通常是表的無關列(ID)
(1) 在建表時,設置了主鍵列(ID) (2) 在未來錄入數據時,就會按照ID列的順序存儲到磁盤上.(咱們又稱之爲彙集索引組織表) (3) 將排好序的整行數據,生成葉子節點.能夠理解爲,磁盤的數據頁就是葉子節點
彙集索引只能有一個,非空惟一,通常時主鍵 輔助索引,能夠有多個,是配合彙集索引使用的 彙集索引葉子節點,就是磁盤的數據行存儲的數據頁 MySQL是根據彙集索引,組織存儲數據,數據存儲時就是按照彙集索引的順序進行存儲數據 輔助索引,只會提取索引鍵值,進行自動排序生成B樹結構
1.普通的單列輔助索引 2.聯合索引 多個列做爲索引條件,生成索引樹,理論上設計的好的,能夠減小大量的回表 查詢 3.惟一索引 索引列的值都是惟一的.
1. 數據量級, 解決方法:分表,分庫,分佈式 2. 索引列值過長 , 解決方法:前綴索引 3. 數據類型: 變長長度字符串,使用了char,解決方案:變長字符串使用varchar enum類型的使用enum ('山東','河北','黑龍江','吉林','遼寧','陝西'......) 1 2 3
db01 [world]>desc city; +-------------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+----------+------+-----+---------+----------------+ | ID | int(11) | NO | PRI | NULL | auto_increment | | Name | char(35) | NO | | | | | CountryCode | char(3) | NO | MUL | | | | District | char(20) | NO | | | | | Population | int(11) | NO | | 0 | | +-------------+----------+------+-----+---------+----------------+ 5 rows in set (0.00 sec) Field :列名字 key :有沒有索引,索引類型 PRI: 主鍵索引 UNI: 惟一索引 MUL: 輔助索引(單列,聯和,前綴)
db01 [world]>alter table city add index idx_name(name); 表 索引名(列名) db01 [world]>create index idx_name1 on city(name); db01 [world]>show index from city; 注意: 以上操做不表明生產操做,咱們不建議在一個列上建多個索引同一個表中,索引名不能同名。
### 7.1.2 刪除索引: db01 [world]>alter table city drop index idx_name1; 表名 索引名
Master [world]>alter table city add index idx_co_po(countrycode,population);
db01 [world]>alter table city add index idx_di(district(5)); 注意:數字列不能用做前綴索引。
db01 [world]>alter table city add unique index idx_uni1(name); ERROR 1062 (23000): Duplicate entry 'San Jose' for key 'idx_uni1'
統計city表中,以省的名字爲分組,統計組的個數javascript
select district,count(id) from city group by district; 需求: 找到world下,city表中 name列有重複值的行,最後刪掉重複的行 db01 [world]>select name,count(id) as cid from city group by name having cid>1 order by cid desc; db01 [world]>select * from city where name='suzhou';
===============================================php
(1)獲取到的是優化器選擇完成的,他認爲代價最小的執行計劃. 做用: 語句執行前,先看執行計劃信息,能夠有效的防止性能較差的語句帶來的性能問題. 若是業務中出現了慢語句,咱們也須要藉助此命令進行語句的評估,分析優化方案。 (2) select 獲取數據的方法 1. 全表掃描(應當儘可能避免,由於性能低) 2. 索引掃描 3. 獲取不到數據
獲取優化器選擇後的執行計劃css
table: city ---->查詢操做的表 ** possible_keys: CountryCode,idx_co_po ---->可能會走的索引 ** key: CountryCode ---->真正走的索引 *** type: ref ---->索引類型 ***** Extra: Using index condition ---->額外信息 *****
從左到右性能依次變好. ALL : 全表掃描,不走索引 例子: 1. 查詢條件列,沒有索引 SELECT * FROM t_100w WHERE k2='780P'; 2. 查詢條件出現如下語句(輔助索引列) USE world DESC city; DESC SELECT * FROM city WHERE countrycode <> 'CHN'; DESC SELECT * FROM city WHERE countrycode NOT IN ('CHN','USA'); DESC SELECT * FROM city WHERE countrycode LIKE '%CH%'; 注意:對於彙集索引列,使用以上語句,依然會走索引 DESC SELECT * FROM city WHERE id <> 10; INDEX : 全索引掃描 1. 查詢須要獲取整個索引樹種的值時: DESC SELECT countrycode FROM city; 2. 聯合索引中,任何一個非最左列做爲查詢條件時: idx_a_b_c(a,b,c) ---> a ab abc SELECT * FROM t1 WHERE b SELECT * FROM t1 WHERE c RANGE : 索引範圍掃描 輔助索引> < >= <= LIKE IN OR 主鍵 <> NOT IN 例子: 1. DESC SELECT * FROM city WHERE id<5; 2. DESC SELECT * FROM city WHERE countrycode LIKE 'CH%'; 3. DESC SELECT * FROM city WHERE countrycode IN ('CHN','USA'); 注意: 1和2例子中,能夠享受到B+樹的優點,可是3例子中是不能享受的. 因此,咱們能夠將3號列子改寫: DESC SELECT * FROM city WHERE countrycode='CHN' UNION ALL SELECT * FROM city WHERE countrycode='USA'; ref: 非惟一性索引,等值查詢 DESC SELECT * FROM city WHERE countrycode='CHN'; eq_ref: 在多表鏈接時,鏈接條件使用了惟一索引(uk pK) DESC SELECT b.name,a.name FROM city AS a JOIN country AS b ON a.countrycode=b.code WHERE a.population <100; DESC country system,const : 惟一索引的等值查詢 DESC SELECT * FROM city WHERE id=10;
extra: filesort ,文件排序. SHOW INDEX FROM city; ALTER TABLE city ADD INDEX CountryCode(CountryCode); ALTER TABLE city DROP INDEX idx_c_p; DESC SELECT * FROM city WHERE countrycode='CHN' ORDER BY population ALTER TABLE city ADD INDEX idx_(population); DESC SELECT * FROM city WHERE countrycode='CHN' ORDER BY population ALTER TABLE city ADD INDEX idx_c_p(countrycode,population); ALTER TABLE city DROP INDEX idx_; ALTER TABLE city DROP INDEX CountryCode; DESC SELECT * FROM city WHERE countrycode='CHN' ORDER BY population 結論: 1.當咱們看到執行計劃extra位置出現filesort,說明由文件排序出現 2.觀察須要排序(ORDER BY,GROUP BY ,DISTINCT )的條件,有沒有索引 3. 根據子句的執行順序,去建立聯合索引 索引優化效果測試: 優化前: [root@db01 ~]# mysqlslap --defaults-file=/etc/my.cnf \ > --concurrency=100 --iterations=1 --create-schema='oldboy' \ > --query="select * from oldboy.t_100w where k2='780P'" engine=innodb \ > --number-of-queries=2000 -uroot -p123 -verbose mysqlslap: [Warning] Using a password on the command line interface can be insecure. Benchmark Running for engine rbose Average number of seconds to run all queries: 701.743 seconds Minimum number of seconds to run all queries: 701.743 seconds Maximum number of seconds to run all queries: 701.743 seconds Number of clients running queries: 100 Average number of queries per client: 20 優化後: [root@db01 ~]# mysqlslap --defaults-file=/etc/my.cnf --concurrency=100 --iterations=1 --create-schema='oldboy' --query="select * from oldboy.t_100w where k2='780P'" engine=innodb --number-of-queries=2000 -uroot -p123 -verbose mysqlslap: [Warning] Using a password on the command line interface can be insecure. Benchmark Running for engine rbose Average number of seconds to run all queries: 0.190 seconds Minimum number of seconds to run all queries: 0.190 seconds Maximum number of seconds to run all queries: 0.190 seconds Number of clients running queries: 100 Average number of queries per client: 20 聯合索引: 1. SELECT * FROM t1 WHERE a= b= 咱們創建聯合索引時: ALTER TABLE t1 ADD INDEX idx_a_b(a,b); ALTER TABLE t1 ADD INDEX idx_b_a(b,a); 以上的查詢不考慮索引的順序,優化器會自動調整where的條件順序 注意: 索引,咱們在這種狀況下建索引時,須要考慮哪一個列的惟一值更多,哪一個放在索引左邊. 2. 若是出現where 條件中出現不等值查詢條件 DESC SELECT * FROM t_100w WHERE num <1000 AND k2='DEEF'; 咱們建索引時: ALTER TABLE t_100w ADD INDEX idx_2_n(k2,num); 語句書寫時 DESC SELECT * FROM t_100w WHERE k2='DEEF' AND num <1000 ; 3. 若是查詢中出現多子句 咱們要按照子句的執行順序進行創建索引.
題目意思: 咱們公司業務慢,請你從數據庫的角度分析緣由 1.mysql出現性能問題,我總結有兩種狀況: (1)應急性的慢:忽然夯住 應急狀況:數據庫hang(卡了,資源耗盡) 處理過程: 1.show processlist; 獲取到致使數據庫hang的語句 2. explain 分析SQL的執行計劃,有沒有走索引,索引的類型狀況 3. 建索引,改語句 (2)一段時間慢(持續性的): (1)記錄慢日誌slowlog,分析slowlog (2)explain 分析SQL的執行計劃,有沒有走索引,索引的類型狀況 (3)建索引,改語句
業務 1.產品的功能 2.用戶的行爲 "熱"查詢語句 --->較慢--->slowlog "熱"數據
爲了使索引的使用效率更高,在建立索引時,必須考慮在哪些字段上建立索引和建立什麼類型的索引。那麼索引設計原則又是怎樣的?
略.回顧一下,彙集索引結構.java
惟一性索引的值是惟一的,能夠更快速的經過該索引來肯定某條記錄。 例如,學生表中學號是具備惟一性的字段。爲該字段創建惟一性索引能夠很快的肯定某個學生的信息。 若是使用姓名的話,可能存在同名現象,從而下降查詢速度。 優化方案: (1) 若是非得使用重複值較多的列做爲查詢條件(例如:男女),能夠將表邏輯拆分 (2) 能夠將此列和其餘的查詢類,作聯和索引 select count(*) from world.city; select count(distinct countrycode) from world.city; select count(distinct countrycode,population ) from world.city;
排序操做會浪費不少時間。 where A B C ----》 A B C in where A group by B order by C A,B,C 若是爲其創建索引,優化查詢 注:若是常常做爲條件的列,重複值特別多,能夠創建聯合索引。
若是索引字段的值很長,最好使用值的前綴來索引。
索引的數目不是越多越好。 可能會產生的問題: (1) 每一個索引都須要佔用磁盤空間,索引越多,須要的磁盤空間就越大。 (2) 修改表時,對索引的重構和更新很麻煩。越多的索引,會使更新表變得很浪費時間。 (3) 優化器的負擔會很重,有可能會影響到優化器的選擇. percona-toolkit中有個工具,專門分析索引是否有用
pt-duplicate-key-checker 表中的數據被大量更新,或者數據的使用方式被改變後,原有的一些索引可能再也不須要。數據庫管理 員應當按期找出這些索引,將它們刪除,從而減小索引對更新操做的影響。
(1) 必需要有主鍵,若是沒有能夠作爲主鍵條件的列,建立無關列 (2) 常常作爲where條件列 order by group by join on, distinct 的條件(業務:產品功能+用戶行爲) (3) 最好使用惟一值多的列做爲索引,若是索引列重複值較多,能夠考慮使用聯合索引 (4) 列值長度較長的索引列,咱們建議使用前綴索引. (5) 下降索引條目,一方面不要建立沒用索引,不常使用的索引清理,percona toolkit(xxxxx) (6) 索引維護要避開業務繁忙期
select * from tab; 全表掃描。 select * from tab where 1=1; 在業務數據庫中,特別是數據量比較大的表。 是沒有全表掃描這種需求。 一、對用戶查看是很是痛苦的。 二、對服務器來說毀滅性的。 (1) select * from tab; SQL改寫成如下語句: select * from tab order by price limit 10 ; 須要在price列上創建索引 (2) select * from tab where name='zhangsan' name列沒有索引 改: 一、換成有索引的列做爲查詢條件 二、將name列創建索引
查詢的結果集,超過了總數行數25%,優化器以爲就沒有必要走索引了。 假如:tab表 id,name id:1-100w ,id列有(輔助)索引 select * from tab where id>500000; 若是業務容許,可使用limit控制。 怎麼改寫 ? 結合業務判斷,有沒有更好的方式。若是沒有更好的改寫方案 儘可能不要在mysql存放這個數據了。放到redis裏面。
索引有自我維護的能力。 對於表內容變化比較頻繁的狀況下,有可能會出現索引失效。 通常是刪除重建 現象: 有一條select語句日常查詢時很快,忽然有一天很慢,會是什麼緣由 select? --->索引失效,,統計數據不真實 DML ? --->鎖衝突
例子: 錯誤的例子:select * from test where id-1=9; 正確的例子:select * from test where id=10; 算術運算 函數運算 子查詢
這樣會致使索引失效. 錯誤的例子: mysql> alter table tab add index inx_tel(telnum); Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> mysql> desc tab; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | YES | | NULL | | | name | varchar(20) | YES | | NULL | | | telnum | varchar(20) | YES | MUL | NULL | | +--------+-------------+------+-----+---------+-------+ 3 rows in set (0.01 sec) mysql> select * from tab where telnum='1333333'; +------+------+---------+ | id | name | telnum | +------+------+---------+ | 1 | a | 1333333 | +------+------+---------+ 1 row in set (0.00 sec) mysql> select * from tab where telnum=1333333; +------+------+---------+ | id | name | telnum | +------+------+---------+ | 1 | a | 1333333 | +------+------+---------+ 1 row in set (0.00 sec) mysql> explain select * from tab where telnum='1333333'; +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ | 1 | SIMPLE | tab | ref | inx_tel | inx_tel | 63 | const | 1 | Using index condition | +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ 1 row in set (0.00 sec) mysql> explain select * from tab where telnum=1333333; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tab | ALL | inx_tel | NULL | NULL | NULL | 2 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from tab where telnum=1555555; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tab | ALL | inx_tel | NULL | NULL | NULL | 2 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from tab where telnum='1555555'; +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ | 1 | SIMPLE | tab | ref | inx_tel | inx_tel | 63 | const | 1 | Using index condition | +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ 1 row in set (0.00 sec) mysql>
EXPLAIN SELECT * FROM teltab WHERE telnum <> '110'; EXPLAIN SELECT * FROM teltab WHERE telnum NOT IN ('110','119'); mysql> select * from tab where telnum <> '1555555'; +------+------+---------+ | id | name | telnum | +------+------+---------+ | 1 | a | 1333333 | +------+------+---------+ 1 row in set (0.00 sec) mysql> explain select * from tab where telnum <> '1555555'; 單獨的>,<,in 有可能走,也有可能不走,和結果集有關,儘可能結合業務添加limit or或in 儘可能改爲union EXPLAIN SELECT * FROM teltab WHERE telnum IN ('110','119'); 改寫成: EXPLAIN SELECT * FROM teltab WHERE telnum='110' UNION ALL SELECT * FROM teltab WHERE telnum='119'
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%' 走range索引掃描 EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110' 不走索引 %linux%類的搜索需求,可使用elasticsearch+mongodb 專門作搜索服務的數據庫產品