索引html
索引是一種特殊的文件(InnoDB數據表上的索引是表空間的一個組成部分),它們包含着對數據表裏全部記錄的引用指針。更通俗的說,數據庫索引比如是一本書前面的目錄,能加快數據庫的查詢速度。對於一個SQL語句,在沒有索引的狀況下,數據庫會遍歷所有數據後選擇符合條件的;而有了相應的索引以後,數據庫會直接在索引中查找符合條件的選項。node
總之:索引是爲了優化查詢,而select 查詢有三種狀況:緩存查詢(不在mysql中進行數據查詢),全表掃描,索引掃描mysql
索引的種類redis
Mysql目前主要有如下幾種索引類型:FULLTEXT,HASH,BTREE,RTREE。算法
FULLTEXTsql
即爲全文索引,目前只有MyISAM引擎支持。數據庫
全文索引並非和MyISAM一塊兒誕生的,它的出現是爲了解決WHERE name LIKE 「%word%"這類針對文本的模糊查詢效率較低的問題。vim
HASH緩存
因爲HASH的惟一(幾乎100%的惟一)及相似鍵值對的形式,很適合做爲索引。bash
HASH索引能夠一次定位,不須要像樹形索引那樣逐層查找,所以具備極高的效率。可是,這種高效是有條件的,即只在「=」和「in」條件下高效,對於範圍查詢、排序及組合索引仍然效率不高。
BTREE(btree b+tree b*tree)
BTREE索引就是一種將索引值按必定的算法,存入一個樹形的數據結構中(二叉樹),每次查詢都是從樹的入口root開始,依次遍歷node,獲取leaf。這是MySQL裏默認和最經常使用的索引類型。
RTREE
RTREE在MySQL不多使用,僅支持geometry數據類型,支持該類型的存儲引擎只有MyISAM、BDb、InnoDb、NDb、Archive幾種。
相對於BTREE,RTREE的優點在於範圍查找。
Btree索引分類
彙集索引:基於主鍵,自動生成的,通常是建表時建立主鍵.若是沒有主鍵,自動選擇惟一鍵作爲彙集索引.
輔助索引:人爲建立的(普通索引,覆蓋索引)
惟一索引:人爲建立(普通索引,彙集索引)
執行計劃
見http://www.javashuo.com/article/p-mqinpdcx-cp.html
運維規範
1、數據庫索引的設計原則:
爲了使索引的使用效率更高,在建立索引時,必須考慮在哪些字段上建立索引和建立什麼類型的索引。
那麼索引設計原則又是怎樣的?
0.建表時必定要有主鍵,若是不相關列能夠做爲主鍵,作一個無關列
1.選擇惟一性索引
惟一性索引的值是惟一的,能夠更快速的經過該索引來肯定某條記錄。
例如,學生表中學號是具備惟一性的字段。爲該字段創建惟一性索引能夠很快的肯定某個學生的信息。
若是使用姓名的話,可能存在同名現象,從而下降查詢速度。
主鍵索引和惟一鍵索引,在查詢中使用是效率最高的。
select count(*) from world.city;
select count(distinct countrycode) from world.city;
select count(distinct countrycode,population ) from world.city;
注意:若是重複值較多,能夠考慮採用聯合索引
2.爲常常須要排序、分組和聯合操做的字段創建索引
常常須要ORDER BY、GROUP BY,join on等操做的字段,排序操做會浪費不少時間。
若是爲其創建索引,能夠有效地避免排序操做。
3.爲常做爲where查詢條件的字段創建索引
若是某個字段常常用來作查詢條件,那麼該字段的查詢速度會影響整個表的查詢速度。所以,
爲這樣的字段創建索引,能夠提升整個表的查詢速度。
3.1 常常查詢
3.2 列值的重複值少(業務層面調整)
注:若是常常做爲條件的列,重複值特別多,能夠創建聯合索引。
4.儘可能使用前綴來索引
若是索引字段的值很長,最好使用值的前綴來索引。例如,TEXT和BLOG類型的字段,進行全文檢索
會很浪費時間。若是隻檢索字段的前面的若干個字符,這樣能夠提升檢索速度。
------------------------以上的是重點關注的,如下是能保證則保證的--------------------
5.限制索引的數目
索引的數目不是越多越好。每一個索引都須要佔用磁盤空間,索引越多,須要的磁盤空間就越大。
修改表時,對索引的重構和更新很麻煩。越多的索引,會使更新表變得很浪費時間。
6.刪除再也不使用或者不多使用的索引(percona toolkit)
表中的數據被大量更新,或者數據的使用方式被改變後,原有的一些索引可能再也不須要。數據庫管理
員應當按期找出這些索引,將它們刪除,從而減小索引對更新操做的影響。
7.大表加索引,要在業務不繁忙期間操做
總結
(1) 必需要有主鍵,若是沒有能夠作爲主鍵條件的列,建立無關列
(2) 常常作爲where條件列 order by group by join on的條件(業務:產品功能+用戶行爲)
(3) 最好使用惟一值多的列做爲索引,若是索引列重複值較多,能夠考慮使用聯合索引
(4) 列值長度較長的索引列,咱們建議使用前綴索引.
(5) 下降索引條目,一方面不要建立沒用索引,不常使用的索引清理,percona toolkit
(6) 索引維護要避開業務繁忙期
不走索引的狀況(開發規範)
1) 沒有查詢條件,或者查詢條件沒有創建索引
select * from tab; 全表掃描。
在業務數據庫中,特別是數據量比較大的表。
是沒有全表掃描這種需求。
一、對用戶查看是很是痛苦的。
二、對服務器來說毀滅性的。
(1)select * from tab; SQL改寫成如下語句: selec * from tab order by price limit 10 須要在price列上創建索引 (2)select * from tab where name='zhangsan' name列沒有索引 改: 1、換成有索引的列做爲查詢條件 二、將name列創建索引
2) 查詢結果集是原表中的大部分數據,應該是25%以上。
查詢的結果集,超過了總數行數25%,優化器以爲就沒有必要走索引了。 假如:tab表 id,name id:1-100w ,id列有索引 select * from tab where id>500000; 若是業務容許,可使用limit控制。 怎麼改寫 ? 結合業務判斷,有沒有更好的方式。若是沒有更好的改寫方案 儘可能不要在mysql存放這個數據了。放到redis裏面。
3) 索引自己失效,統計數據不真實
索引有自我維護的能力。
對於表內容變化比較頻繁的狀況下,有可能會出現索引失效。
4) 查詢條件使用函數在索引列上,或者對索引列進行運算,運算包括(+,-,*,/,! 等)
例子:
錯誤的例子:select * from test where id-1=9;
正確的例子:select * from test where id=10;
算術運算
函數運算
desc select * from blog_userinfo where DATE_FORMAT(last_login,'%Y-%m-%d') >= '2019-01-01';
子查詢
5)隱式轉換致使索引失效.這一點應當引發重視.也是開發中常常會犯的錯誤.
select * from t1 where telnum=110;
6) <> ,not in 不走索引
7) like "%_" 百分號在最前面不走
8) 單獨引用聯合索引裏非第一位置的索引列.做爲條件查詢時不走索引.
CREATE TABLE t1 (id INT,NAME VARCHAR(20),age INT ,sex ENUM('m','f'),money INT); ALTER TABLE t1 ADD INDEX t1_idx(money,age,sex); 走索引的狀況測試: EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND age=30 AND sex='m'; EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND age=30 ; EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE money=30 AND sex='m'; ----->部分走索引 不走索引的: EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE age=20 EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE age=30 AND sex='m'; EXPLAIN SELECT NAME,age,sex,money FROM t1 WHERE sex='m';
壓力測試
1、模擬數據庫數據 爲了測試咱們建立一個oldboy的庫建立一個t1的表,而後導入50萬行數據,腳本以下: vim slap.sh #!/bin/bash HOSTNAME="localhost" PORT="3306" USERNAME="root" PASSWORD="123" DBNAME="oldboy" TABLENAME="t1" #create database mysql -h ${HOSTNAME} -P${PORT} -u${USERNAME} -p${PASSWORD} -e "drop database if exists ${DBNAME}" create_db_sql="create database if not exists ${DBNAME}" mysql -h ${HOSTNAME} -P${PORT} -u${USERNAME} -p${PASSWORD} -e "${create_db_sql}" #create table create_table_sql="create table if not exists ${TABLENAME}(stuid int not null primary key,stuname varchar(20) not null,stusex char(1) not null,cardid varchar(20) not null,birthday datetime,entertime datetime,address varchar(100)default null)" mysql -h ${HOSTNAME} -P${PORT} -u${USERNAME} -p${PASSWORD} ${DBNAME} -e "${create_table_sql}" #insert data to table i="1" while [ $i -le 500000 ] do insert_sql="insert into ${TABLENAME} values($i,'alexsb_$i','1','110011198809163418','1990-05-16','2017-09-13','oldboyedu')" mysql -h ${HOSTNAME} -P${PORT} -u${USERNAME} -p${PASSWORD} ${DBNAME} -e "${insert_sql}" let i++ done #select data select_sql="select count(*) from ${TABLENAME}" mysql -h ${HOSTNAME} -P${PORT} -u${USERNAME} -p${PASSWORD} ${DBNAME} -e "${select_sql}" 執行腳本: sh slap.sh 2、檢查數據可用性 mysql -uroot -p123 select count(*) from oldboy.t1; 3、在沒有優化以前咱們使用mysqlslap來進行壓力測試 # 100個線程,總共2000個請求 mysqlslap --defaults-file=/etc/my.cnf \ --concurrency=100 --iterations=1 --create-schema='oldboy' \ --query="select * from oldboy.t1 where stuname='alexsb_100'" engine=innodb \ --number-of-queries=2000 -uroot -p123 -verbose