mysql索引


 

1 初識索引  

  索引在MySQL中也叫是一種“鍵”,是存儲引擎用於快速找到記錄的一種數據結構 —— 排好序的快速查找數據結構

  數據本身之外,數據庫還維護着一個滿足特定查找算法的數據結構,這些數據結構以某種方式指向數據,這樣就可以在這些數據結構的基礎上實現高級查找算法,這種數據結構就是索引。

  索引對於良好的性能非常關鍵,尤其是當表中的數據量越來越大時,索引對於性能的影響愈發重要。索引優化應該是對查詢性能優化最有效的手段了。索引能夠輕易將查詢性能提高好幾個數量級。

  索引相當於字典的音序表,如果要查某個字,如果不使用音序表,則需要從幾百頁中逐頁去查。

2 索引的原理

  索引的目的在於提高查詢效率,與我們查閱圖書所用的目錄是一個道理:先定位到章,然后定位到該章下的一個小節,然后找到頁數。相似的例子還有:查字典,查火車車次,飛機航班等

  索引的原理:通過不斷地縮小想要獲取數據的范圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是說,有了這種索引機制,我們可以總是用同一種查找方式來鎖定數據。

  索引的本質:實際上是一張表,該表保存了主鍵和索引字段,並指向實體表的記錄,所以索引列也是要占用空間的一般來說索引本身也很大,不可能全部存儲在內存中,因此索引往往以文件形式存儲在硬盤上。

優勢

  (1)類似大學圖書館建書目索引,提高數據檢索效率,降低數據庫的IO成本

  (2)通過索引列對數據進行排序,降低數據排序成本,降低了CPU的消耗

劣勢

  (1)雖然索引大大提高了查詢速度,同時卻會降低更新表(增刪改)的速度。因為更新表時,MySQL不僅要不存數據,還要保存一下索引文件每次更新添加了索引列的字段,都會調整因為更新所帶來的鍵值變化后的索引信息。

  (2)索引只是提高效率的一個因素,如果你的MySQL有大數據量的表,就需要花時間研究建立優秀的索引,或優化查詢語句(需要不斷調整優化)。

3 MySQL索引

  3.1 索引分類

#### 常用索引 ####

#1、單值索引:即一個索引只包含單個列,一個表可以有多個單列索引

#2、唯一索引:索引列的值必須唯一,但允許有空值
    -主鍵索引PRIMARY KEY:加速查找+約束(不為空、不能重復)
    -唯一索引UNIQUE:加速查找+約束(不能重復)

#3、聯合索引:即一個索引包含多個列
    -PRIMARY KEY(id,name):聯合主鍵索引
    -UNIQUE(id,name):聯合唯一索引
    -INDEX(id,name):聯合普通索引
# 組合索引(最左前綴匹配):
    - create unique index 索引名稱 on 表名(列名,列名)
    - drop unique index 索引名稱 on 表名
    
    - create index ix_name_email on userinfo3(name,email,)
    - 最左前綴匹配【一定要包含最左邊的】
          √ select  * from userinfo3 where name='alex';
          √ select  * from userinfo3 where name='alex' and email='asdf';
             
          × select  * from userinfo3 where email='alex@qq.com';
        
# 組合索引效率 > 索引合並 
    組合索引
        - (name,email,)
        select  * from userinfo3 where name='alex' and email='asdf';
        select  * from userinfo3 where name='alex';
                
    索引合並【把多個單列索引合並使用】:
        - name
        - email
        select  * from userinfo3 where name='alex' and email='asdf';
        select  * from userinfo3 where name='alex';
        select  * from userinfo3 where email='asdf';
    
#4、組合索引(最左前綴匹配)
#覆蓋索引:在索引文件中直接獲取數據。
    
        即從輔助索引中就可以得到查詢記錄,而不需要查詢聚集索引中的記錄。
#5、覆蓋索引

  3.2  基本語法

(1)建表時添加索引

  建表同時建立單索引

CREATE TABLE t_user1(id INT ,
userName VARCHAR(20),
PASSWORD VARCHAR(20),
INDEX (userName) #關鍵字INDEX
);

  建表同時建立唯一索引(可以是單或多)

CREATE TABLE t_user2(id INT ,
userName VARCHAR(20),
PASSWORD VARCHAR(20),
UNIQUE INDEX index_userName(userName) #關鍵字UNIQUE和INDEX
);

  建表同時建立聯合索引

CREATE TABLE t_user3(id INT ,
userName VARCHAR(20),
PASSWORD VARCHAR(20),
INDEX index_userName_password(userName,PASSWORD)
);

(2)給已存在表添加索引

-- 單列索引
CREATE INDEX index_userName ON t_user(userName);
-- 唯一索引
CREATE UNIQUE INDEX index_userName ON t_user(userName);
-- 聯合索引
CREATE INDEX index_userName_password ON t_user(userName,PASSWORD);

(3)刪除索引

DROP INDEX index_userName ON t_user;
DROP INDEX index_userName_password ON t_user;

  3.3  MySQL常見瓶頸

    •  CPU:CPU在飽和的時候一般發生在數據裝入在內存或從磁盤上讀取數據時候
    •  IO:磁盤I/O瓶頸發生在裝入數據遠大於內存容量時
    •  服務器硬件的性能瓶頸:top,free,iostat和vmstat來查看系統的性能狀態

4 正確使用索引

4.1 哪些情況需要創建索引

  • 主鍵自動建立唯一索引
  • 頻繁作為查詢的條件的字段應該創建索引
  • 查詢中與其他表關聯的字段,外鍵關系建立索引
  • 單間/組合索引的選擇問題,who?(在高並發下傾向創建組合索引)
  • 查詢中排序的字段,排序字段若通過索引去訪問將大大提高排序的速度
  • 查詢中統計或者分組字段
  • 哪些字段可以建索引? 一般都where、order by 或者 group by 后面的字段。

4.2 哪些情況不要創建索引

  • Where條件里用不到的字段不創建索引
  • 表記錄太少
  • 修改性能(頻繁更新的字段)遠遠大於檢索性能時,不應該創建索引。這是因為,修改性能和檢索性能是互相矛盾的,因為每次更新不單單是更新了記錄還會更新索引,加重IO負擔)
  • 數據重復且分布平均的表字段,因此應該只為經常查詢和經常排序的數據列建立索引。注意,如果某個數據列包含許多重復的內容,為它建立索引就沒有太大的實際效果。
  • 對於那些定義為text, image和bit數據類型的列不應該增加索引。這是因為,這些列的數據量要么相當大,要么取值很少。
  • 對於那些在查詢中很少使用或者參考的列不應該創建索引
#1、and與or的邏輯
    條件1 and 條件2:所有條件都成立才算成立,但凡要有一個條件不成立則最終結果不成立
    條件1 or 條件2:只要有一個條件成立則最終結果就成立

#2、and的工作原理
    條件:
        a = 10 and b = 'xxx' and c > 3 and d =4
    索引:
        制作聯合索引(d,a,b,c)
    工作原理:
        對於連續多個and:mysql會按照聯合索引,從左到右的順序找一個區分度高的索引字段(這樣便可以快速鎖定很小的范圍),加速查詢,即按照d—>a->b->c的順序

#3、or的工作原理
    條件:
        a = 10 or b = 'xxx' or c > 3 or d =4
    索引:
        制作聯合索引(d,a,b,c)
        
    工作原理:
        對於連續多個or:mysql會按照條件的順序,從左到右依次判斷,即a->b->c->d
and/or工作原理

4.3 mysql索引失效case及優化:

1,全值匹配我最愛  
-- 【optimizer會對查詢順序自動進行優化調整】

2,最佳左前綴法則  【帶頭大哥不能死,中間兄弟不能斷】
--   如果索引了多例,要遵守最左前綴法則。指的是查詢從索引的最左前列開始並且不跳過索引中的列。

3,索引列少計算     
-- 不在索引列上做任何操作(計算、函數、(自動or手動)類型轉換),會導致索引失效而轉向全表掃描

4,范圍之后全失效  
-- 存儲引擎不能使用索引中范圍條件右邊的列

5,覆蓋索引代替select * 
-- 盡量使用覆蓋索引(只訪問索引的查詢(索引列和查詢列一致)),減少select*

6,少用 is null或is not null
-- is null,is not null 也無法使用索引

7,少用!=<>
-- mysql在使用不等於(!=或者<>)的時候無法使用索引會導致全表掃描

8,少用or
-- 少用or,用它連接時會索引失效【查看上邊and/or 工作原理】

9like %放右邊
-- 【like以通配符開頭('%abc...')mysql索引失效會變成全表掃描操作】
-- 【放左邊是all,連帶他本身全失效】
-- 【可用覆蓋索引(varchar為380失效)或主鍵索引】

10,varchar類型要加''單引號,切記!!!
-- 【不加會計算-自動轉型】
-- 【導致行鎖升級為表鎖】

11,使用連接(JOIN)來代替子查詢(Sub-Queries)

12,使用count(1)

5 查詢優化神器-explain

5.1 是什么 —— 查看執行計划

  使用EXPLAIN關鍵字可以模擬優化器執行SQL語句,從而知道MySQL是如何處理你的SQL語句的。分析你的查詢語句或是結構的性能瓶頸

  優化語句基本上都是在優化rows。具體用法和字段含義可以參考官網 explain-output

5.2 能干嘛

  • 表的讀取順序
  • 數據讀取操作的操作類型
  • 哪些索引可以使用
  • 哪些索引被實際使用
  • 表之間的引用
  • 每張表有多少行被優化器查詢
  • 索引長度

5.3  執行計划包含的信息

Id

Select_type

Table

Type

Possible_keys

Key

Key_len

Ref

Rows

Extra

【詳解】

1】id :select查詢的序列號,包含一組數字,表示查詢中執行select子句或操作表的順序
-- id相同,執行順序由上至下
-- id不同,如果是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行
-- id相同不同,同時存在
2】select_type:查詢的類型,主要用於區別普通查詢、聯合查詢、子查詢等的復雜查詢
-- SIMPLE:簡單的select查詢,查詢中不包含子查詢或者UNION
-- PRIMARY:查詢中若包含任何復雜的子部分,最外層查詢則被標記為
-- SUBQUERY:在SELECT或者WHERE列表中包含了子查詢
-- DERIVED:在FROM列表中包含的子查詢被標記為DERIVED(衍生)
MySQL會遞歸執行這些子查詢,把結果放在臨時表里。
-- UNION:若第二個SELECT出現在UNION之后,則被標記為UNION;
若UNION包含在FROM子句的子查詢中,外層SELECT將被標記為:DERIVED
-- UNION RESULT:從UNION表獲取結果的SELECT
3table:顯示這一行的數據是關於哪張表的

【4】type:顯示查詢使用了何種類型,從最好到最差依次是:-- system>const>eq_ref>ref>range>index>ALL
    -- system:表只有一行記錄(等於系統表),這是const類型的特例,平時不會出現,這個也可以忽略不計
    -- const:表示通過索引一次就找到了,const用於比較primary key或者unique索引。因為只匹配一行數據,所以很快。如將主鍵至於where列表中,MySQL就能將該查詢轉換為一個常量
    -- eq_ref:唯一性索引,對於每個索引鍵,表中只有一條記錄與之匹配,常見於主鍵或唯一索引掃描
    -- ref:非唯一索引掃描,返回匹配某個單獨值的所有行。
        本質上也是一種索引訪問,它返回所有匹配某個單獨值的行,然而,它可能會找到多個符合條件的行,所以他應該屬於查找和掃描的混合體
    -- range:只檢索給定范圍的行,使用一個索引來選擇行。key列顯示使用了哪個索引
       一般就是在你的where語句中出現了between、<>、in等的查詢這種范圍掃描索引掃描比全表掃描要好,因為他只需要開始索引的某一點,而結束語另一點,不用掃描全部索引。
    -- index:Full Index Scan,index與ALL區別為index類型只遍歷索引樹。
        這通常比ALL快,因為索引文件通常比數據文件小。(也就是說雖然all和index都是讀全表,但index是從索引中讀取的,而all是從硬盤中讀的)
    -- all:FullTable Scan,將遍歷全表以找到匹配的行
5】possible_keys:顯示可能應用在這張表中的索引,一個或多個。
--  查詢涉及的字段上若存在索引,則該索引將被列出,但不一定被查詢實際使用
6key:實際使用的索引。如果為null則沒有使用索引
-- 查詢中若使用了覆蓋索引,則索引和查詢的select字段重疊(USING index)
7】key_len:表示索引中使用的字節數,可通過該列計算查詢中使用的索引的長度。在不損失精確性的情況下,長度越短越好
-- key_len顯示的值為索引最大可能長度,並非實際使用長度,即key_len是根據表定義計算而得,不是通過表內檢索出的
8】ref:顯示索引那一列被使用了,如果可能的話,是一個常數。那些列或常量被用於查找索引列上的值

【9】rows:根據表統計信息及索引選用情況,大致估算出找到所需的記錄所需要讀取的行數

【10】Extra:包含不適合在其他列中顯示但十分重要的額外信息
-- Using filesort(不好):說明mysql會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。MySQL中無法利用索引完成排序操作成為“文件排序”

-- Using temporary:使用了臨時表保存中間結果,MySQL在對查詢結果排序時使用臨時表。常見於排序order by 和分組查詢 group by

-- Using where:表明使用了where過濾

-- USING index:表示相應的select操作中使用了覆蓋索引(Coveing Index),避免訪問了表的數據行,效率不錯!
    -- 如果同時出現using where,表明索引被用來執行索引鍵值的查找;
    -- 如果沒有同時出現using where,表面索引用來讀取數據而非執行查找動作。     

【type類型說明】

  從最好到最差依次是:system>const>eq_ref>ref>range>index>ALL

 一般來說,得保證查詢只是達到range級別,最好達到ref

6 慢查詢優化的基本步驟

# 0.先運行看看是否真的很慢,注意設置SQL_NO_CACHE
# 1.where條件單表查,鎖定最小返回記錄表。這句話的意思是把查詢語句的where都應用到表中返回的記錄數最小的表開始查起,單表每個字段分別查詢,看哪個字段的區分度最高
# 2.explain查看執行計划,是否與1預期一致(從鎖定記錄較少的表開始查詢)
# 3.order by limit 形式的sql語句讓排序的表優先查
# 4.了解業務方使用場景
# 5.加索引時參照建索引的幾大原則
# 6.觀察結果,不符合預期繼續從0分析

7  MySQL鎖機制

7.1  鎖的分類

  (1)從數據操作的類型(讀、寫)分

  • 讀鎖(共享鎖):針對同一份數據,多個讀操作可以同時進行而不會互相影響
  • 寫鎖(排它鎖):當前寫操作沒有完成前,它會阻斷其他寫鎖和讀鎖。
  • 悲觀鎖:
    •  總是假設最壞的情況,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖(共享資源每次只給一個線程使用,其它線程阻塞,用完后再把資源轉讓給其它線程)。
    • 傳統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。Java中synchronized和ReentrantLock等獨占鎖就是悲觀鎖思想的實現。
  • 樂觀鎖:
    •  總是假設最好的情況,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號機制和CAS算法實現。
    • 樂觀鎖適用於多讀的應用類型,這樣可以提高吞吐量,像數據庫提供的類似於write_condition機制,其實都是提供的樂觀鎖。在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現方式CAS實現的。

  (2)從對數據操作的顆粒度

  • 表鎖
  • 行鎖

7.2  表鎖(MyISAM)—— 偏讀

 特點:偏向MyISAM存儲引擎,開銷小,加鎖快,無死鎖,鎖定粒度大,發生鎖沖突的概率最高,並發最低

7.3  行鎖(InnoDB)—— 偏寫

特點:偏向InnoDB存儲引擎,開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖沖突的概率最低,並發度也最高。

-- InnoDB與MyISAM的最大不同有兩點:
一是支持事務(TRANSACTION);
二是采用了行級鎖

7.4  頁鎖(了解即可)

  開銷和加鎖時間界於表鎖和行鎖之間:會出現死鎖;

       鎖定粒度界於表鎖和行鎖之間,並發度一般。

7.5 優化建議

  • 盡可能讓所有數據檢索都通過索引來完成,避免無索引行鎖升級為表鎖
  • 合理設計索引,盡量縮小鎖的范圍
  • 盡可能較少檢索條件,避免間隙鎖
  • 盡量控制事務大小,減少鎖定資源量和時間長度
  • 盡可能低級別事務隔離

8 慢查詢日志

  查看是否開啟及如何開啟:

-- 默認:
SHOW VARIABLES LIKE '%slow_query_log%'

-- 開啟:
set global slow_query_log = 1
# 慢日志
  - 執行時間 > 10
  - 未命中索引
  - 日志文件路徑
            
        配置:
            - 內存
                show variables like '%query%';
                show variables like '%queries%';
                set global 變量名 =- 配置文件
                mysqld --defaults-file='E:\wupeiqi\mysql-5.7.16-winx64\mysql-5.7.16-winx64\my-default.ini'
                
                my.conf內容:
                    slow_query_log = ON
                    slow_query_log_file = D:/....
                    
# 注意:修改配置文件之后,需要重啟服務
MySQL日志管理
========================================================
錯誤日志: 記錄 MySQL 服務器啟動、關閉及運行錯誤等信息
二進制日志: 又稱binlog日志,以二進制文件的方式記錄數據庫中除 SELECT 以外的操作
查詢日志: 記錄查詢的信息
慢查詢日志: 記錄執行時間超過指定時間的操作
中繼日志: 備庫將主庫的二進制日志復制到自己的中繼日志中,從而在本地進行重放
通用日志: 審計哪個賬號、在哪個時段、做了哪些事件
事務日志或稱redo日志: 記錄Innodb事務相關的如事務執行時間、檢查點等
========================================================
# 一、bin-log
1. 啟用
# vim /etc/my.cnf
[mysqld]
log-bin[=dir\[filename]]
# service mysqld restart
2. 暫停
//僅當前會話
SET SQL_LOG_BIN=0;
SET SQL_LOG_BIN=1;
3. 查看
查看全部:
# mysqlbinlog mysql.000002
按時間:
# mysqlbinlog mysql.000002 --start-datetime="2012-12-05 10:02:56"
# mysqlbinlog mysql.000002 --stop-datetime="2012-12-05 11:02:54"
# mysqlbinlog mysql.000002 --start-datetime="2012-12-05 10:02:56" --stop-datetime="2012-12-05 11:02:54" 

按字節數:
# mysqlbinlog mysql.000002 --start-position=260
# mysqlbinlog mysql.000002 --stop-position=260
# mysqlbinlog mysql.000002 --start-position=260 --stop-position=930
4. 截斷bin-log(產生新的bin-log文件)
a. 重啟mysql服務器
b. # mysql -uroot -p123 -e 'flush logs'
5. 刪除bin-log文件
# mysql -uroot -p123 -e 'reset master' 


# 二、查詢日志
啟用通用查詢日志
# vim /etc/my.cnf
[mysqld]
log[=dir\[filename]]
# service mysqld restart

# 三、慢查詢日志
3.1 啟用慢查詢日志
# vim /etc/my.cnf
[mysqld]
log-slow-queries[=dir\[filename]]
long_query_time=n
# service mysqld restart
MySQL 5.6:
slow-query-log=1
slow-query-log-file=slow.log
long_query_time=3  單位為秒

3,2 查看慢查詢日志
測試:BENCHMARK(count,expr)
SELECT BENCHMARK(50000000,2*3);
MySQL日志管理

9 日志分析工具

9.1 mysqldumpshow

  查看mysqldumpshow的幫助信息

s:是表示按何種方式排序
c:訪問次數
l:鎖定時間
r:返回記錄
t:查詢時間
al:平均鎖定時間
ar:平均返回記錄數
at:平均查詢時間
t:即為返回前面多少條的數據
g:后邊搭配一個正則匹配模式,大小寫不敏感的

9.2 Show profiles

  (1)是什么

    是mysql提供可以用來分析當前會話中語句執行的資源消耗情況。可以用於SQL的調優測量。

    官網:http://dev.mysql.com/doc/refman/5.5/en/show-profile.html

    默認情況下,參數處於關閉狀態,並保存最近15次的運行結果

  (2)分析步驟

1.是否支持,看看當前的SQL版本是否支持
2.開啟功能,默認是關閉,使用前需要開啟
3.運行SQL
-- select * from emp group by id%10 limit 150000
-- select * from emp group by id%20 order by 5
4.查看結果,show profiles;
5.診斷SQL,show profile cpu,block io for query 上一步前面的問題SQL 數字號碼;
6.日常開發需要注意的結論
-- converting HEAP to MyISAM 查詢結果太大,內存都不夠用了往磁盤上搬了。
-- Creating tmp table 創建臨時表
    -- 拷貝數據到臨時表
    --  用完再刪除
-- Copying to tmp table on disk 把內存中臨時表復制到磁盤,危險!!!
-- locked

 

推薦視頻:
https://www.bilibili.com/video/av49181542 

配套資料

鏈接:https://pan.baidu.com/s/1WMqGfH1Kf1aDuQfzHNg4cQ
提取碼:edde

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM