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';

#覆蓋索引:在索引文件中直接獲取數據。 即從輔助索引中就可以得到查詢記錄,而不需要查詢聚集索引中的記錄。
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
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 工作原理】 9,like %放右邊 -- 【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 【3】table:顯示這一行的數據是關於哪張表的 【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:顯示可能應用在這張表中的索引,一個或多個。 -- 查詢涉及的字段上若存在索引,則該索引將被列出,但不一定被查詢實際使用 【6】key:實際使用的索引。如果為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);
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