索引及執行計劃

索引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';
View Code

壓力測試

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
View Code
相關文章
相關標籤/搜索