(一) mysql 架構
1.1 邏輯架構
第二層:查詢解析,分析,優化,緩存以及所有的內置函數(日期,時間,數學等等),以及所有的誇存儲引擎的功能:存儲過程、觸發器、視圖等
第三層:包含存儲引擎,存儲引擎是負責數據的存儲和提取的。存儲引擎都實現了同樣的接口,屏蔽了不同ing存儲引擎的差異。
存儲引擎API 的底層函數,用於執行"開始事務"或者"根據主鍵提取一行記錄"等操作
注意:存儲引擎不會去解析sql ,不同存儲引擎間不互相通信,只是簡單響應上層服務器的請求
InnoDB 是一個例外,會解析外鍵定義,因爲mysql服務器本身沒有實現該功能
1.2 併發控制
-
問題
多個查詢需要在同一時刻修改數據
類比郵件,如果兩封郵件同時到達同一個收件人服務器,入過不控制,會發生兩封郵件內容相互追加 -
讀寫鎖
寫操作: 加鎖(獨佔鎖),只能有一個線程在寫
讀操作:只能在沒有寫鎖存在的情況下才能獲取(共享鎖),可多個線程讀
注:一個寫鎖或阻塞其他的寫鎖與讀鎖 -
鎖粒度
含義:鎖定的數據量越少,則系統的併發程度越高(比如jdk1.7 與jdk1.8 的hashmap 的實現區別)
3.1表鎖(tabkle lock)
寫操作對整張表加鎖(鎖開銷小),會同時阻塞其他用戶對該表的讀、寫操作
3.2 行鎖(row lock)
特點: 最大程度支持併發處理(鎖開銷最大)
注:行級鎖只在存儲引擎實現1.3 事務
- 含義
指的是一組原子性的sql查詢,或者是說是一個獨立的工作單元(單元內的sql要麼都成功,要麼全部失敗) - 事物特性以及隔離級別
原子+一致+持久+隔離 - 隔離級別:
- read uncommitted
- read committed
- repeatble read(mysql 默認的事務隔離級別)
- serilizable (完全犧牲併發性,只能串行操作,隔離級別最高)
- 不考慮隔離性可能出現的問題:
詳細參見另一篇博客
https://blog.csdn.net/qq_36922927/article/details/89326635
-
死鎖
含義:兩個或多個 -
事務日誌
日誌:順序io
磁盤io:是隨機io
預寫日誌: -
mysql 中的事務
- auto commit
- set transaction isolation level (設置事務隔離級別)
- 同一個事務中使用多種存儲引擎不可靠(事務是在存儲引擎中實現的)
- 事務回滾,如果事務中包含非事務型表,那麼該表不會回滾,也不會報錯,可能有提示
1.4 多版本併發控制(MVCC)
Mysql,Oracle,PostgreSQL 等關係數據庫都實現了MVCC(無統一標準,實現機制可能不同)
- 含義:
- 可理解爲是行級鎖的變種
- 根據事務開始的時間不同,每個事物對同一張表,同一時刻看到的數據可能是不一樣的
- InnoDB 的MVCC
- 兩個隱藏列,分別保存行的創建時間,行的過期時間(這裏的時間不是實際時間值,而是指版本號)
- 事務開始時刻的系統版本號會作爲事務的版本號,用來和查詢到的每行記錄的版本號比較
InnodDB 在repeatable read 隔離級別下的curd 操作邏輯如下
- select
a. 只查找版本號 當前事務版本號的行(也就是隻查找版本早於當前事務的數據行)
查到的行 要麼是事務開始前存在的,要麼是自身插入或者修改過的
b. 行的刪除版本要麼未定義,要麼大於當前事務版本號。這是確保事務讀取到的行,在事務開始之前未被刪除 - insert
爲新插入的行以當前系統版本號作爲行版本號 - update
插入一行新紀錄,保存當前系統版本號作爲行版本號,同時保存當前系統版本號到原來的行作爲刪除標識
table(id,col1,col2,col3)// 表定義
原行:
(1,a,b,c,1,) //最後兩列是版本號 // 假設當前系統版本號是100
// 事務開始時,取當前系統版本號作爲事務版本號,然後系統版本號自增
操作 update table set col1 =f where id=1;// 此事務版本號爲100 , 事務開始時,系統版本號自增爲101
結果:
新增一條行記錄:
(1,f,b,c,101,);//當前系統版本號101 作爲行標識(行版本號)
修改原記錄:當前系統版本號作爲刪除標誌
(1,a,b,c,1,101);//
- delete
爲刪除的每一行保存當前系統版本號作爲行版本號
注: InnoDB 的mvcc 只在Repeatable read ,read committed 這兩個事務級別下工作
read uncommitted 總是讀取最新的數據行,而不是符合當前事務版本的數據行
serializable:對所有讀取的行都加鎖
1.5 mysql的存儲引擎
- InnoDB
- 設計用來處理大量的短期事務,短期事務大部分是正常提交的,很少被回滾
- 性能和自動崩潰恢復特性,在非事務性存儲場景也很流行
- 表基於聚簇索引(主鍵查詢性能很高,但是二級索引中必須包含主鍵列,如果主鍵列很大,那麼其他索引都會很大)
- MyIsAM
- 全文索引,壓縮,空間函數
- 不支持事務和行級鎖(只能表加鎖(這也是性能瓶頸所在))
- 延遲更新索引
- 壓縮表 (數據不再修改的數據表)
- 執行表修復可能導致一些數據丟失
- 存儲引擎的選擇-
- 除非需要用到innodb 不具備的特性,否則優先選擇innodb
- 除非萬不得已,不要選擇混合的存儲影青
- 事務,如果不需要事務,那麼可以選擇MyIsam
- 備份:在線熱備份,innodb
- 崩潰恢復:MyIsAm 崩潰後損壞概率比InnoDB高很多,而且恢復速度慢
一些場景:
- 日誌型應用:對插入速度要求高,考慮MyISAM
- 讀多寫少,如果不介意MyISAM 的崩潰恢復問題(這個問題可以在測試環境拔掉電源測試),可以選擇MyISAM
- 訂單處理:支持事務是必要選項,INNODB
(二) 剖析mysql 查詢
2.1 服務器負載
含義:找出效率低下的查詢,定位和優化"壞"查詢,提高應用性能
工具:
-
慢查詢日誌:找出代價高的查詢(慢查詢日誌開銷低,精度高)
-
通用日誌:在查詢請求到服務器時進行記錄,所以不包含響應時間和執行計劃等重要信息
-
pt-query-digest:分析查詢日誌的工具,可以將慢查詢日誌生成剖析報告
剖析報告示例:
參數解讀:
參數 | 含義 |
---|---|
Rank | 排名,越慢越靠前 |
Query ID | 對查詢語句計算出的哈希指紋 |
V/M | 方差均值比(離差指數),與查詢對應的執行時間正相關 |
Item | 相關查詢的簡寫,至於那個? 是 替代了表名後面的分片標識(這裏的分片是什麼我暫時不清楚) |
剖析報告後面還有詳細報告
頂部包含:查詢執行的頻率,平均併發度,以及查詢性能最差的一次執行在日誌文件中的字節偏移值
接着是直方圖:Query_time distribution,查詢時間分佈
2.2 剖析單條查詢
- show profile:MySql 5.1以後的版本引入(默認是禁用的)
SET profiling =1;
啓用後,在服務器上執行的所有sql語句都會測量其耗費的時間,其他一些查詢狀態變更的數據
當一條查詢提交到服務器時,此工具會記錄剖析信息到一張臨時表中,斌哥且給查詢賦予一個從1開始的整數標識符
執行:show profile ; 顯示剖析結果
mysql 默認的時間只會精確到兩位小數
但是 show frofile 是顯示了 7位小數,精度是很高的
來查看下Query_Id=6 的查詢的具體數據
show profile for query 6;
發現並不是按照耗時時間排序的,我想知道的是耗時較多的是哪些步驟
以下步驟格式化輸出:
SET @query_id=6;
SELECT
state,
SUM(DURATION) AS Total_R,
ROUND(
100 * SUM(duration) / (
SELECT
SUM(duration)
FROM
information_schema.PROFILING
WHERE
QUERY_ID =@query_id
),2)AS Pct_R,
COUNT(*) as Calls,
SUM(duration)/COUNT(*) as "R/Call"
from information_schema.PROFILING
where QUERY_ID=@query_id
GROUP BY STATE
ORDER BY Total_R DESC;
以上是單表查詢,沒有發現特別耗時的條目。
- show status
計數器,顯示某些活動如讀索引的頻繁程度,但無法給出消耗多少時間
參數很多,以後用到再來細究
注:show status 會區分內存臨時表和磁盤臨時表
這點在explain 中是不能區別的
下圖中:
磁盤臨時表:Created_tmp_disk_table 0個
總的臨時表:Created_tmp_tables 6
內存臨時表: 總的臨時表數-磁盤=6
未使用索引的讀:Handler_read_rnd_next
2.3 診斷間歇性問題
含義:間歇性問題是指系統偶爾停頓或者慢查詢
計數器表
爲了統計網站的訪問數
- 單獨的表來統計數據,只有一行數據
那麼對於每一個想更新這一行的事務來說,這條記錄上都有一個全局的互斥鎖,變爲串行,併發性能堪憂 - 爲了提高把併發性能,可以設置多行
預先設置100行,每個事務隨機選取一行來更新
所有行數據累加即得最終結果
如果不想預先生成行,可以使用 on duplicate key
3. 如果爲了減少表數據的行數
可以使用定時任務,將數據統計到地0行,然後刪除其他行
加快alter table
alter table 操作可能導致服務停止,耗時
解決方案:
- 先在不提供服務的數據庫上進行alter 操作,完成後,和主庫切換
- “影子拷貝”,建立一張和源表無關的表,然後重命名,刪除操作來交換兩張表
- 不是所有的alter table 都會導致表重建,alter 對應的列即可
索引基礎
B_Tree 索引
名字是Btree ,但是底層實現可能是B+,T_Tree 結構存儲這種索引
B_Tree:
- 所有值是按順序存儲的,每一個葉子到根的距離相同
- 指針實際上定義子節點頁中值得上限和下限
- 最終存儲引擎要麼找到對應的值,要麼不存在
如何能使用上索引
滿足最左原則即可
索引列必須按照建立索引的順序使用,不能跳過,如果跳過,那麼只能在跳過列之前的索引生效
. 如果不是按照索引的最左列開始查找,則無法使用索引
. 不能跳過索引列
. 如果有某個列的範圍查詢,則其右邊的列無法使用索引優化查詢
hash 索引(Memory 殷勤支持
hash 表實現
索引=hash 值+數據行的指針
注意:
- hash 索引只包含哈希值和行指針
- hash 索引數據不是按照索引值順序存儲,故無法用於排序
- hash 索引只支持等值查詢 =,IN(),<>
不支持任何範圍查詢,例如where price》100 - 訪問hash 速度快,除非hash 衝突很多,如果有哈市衝突,存儲引擎必須便利鏈表的所有行指針,逐行進行比較
- 如果hash 衝突很多,一些索引維護操作的代價很高。刪除一行,需要遍歷對應hash 值鏈表的每一行,找到並刪除對應行的引用,衝突越多,代價越大
innodb 的自適應hash 索引:
innnodb 在察覺到某些索引使用率很高,自動在B-tree 上建立一層hash 索引,快速定位到節點
創建自定義hash 索引
比如:在存儲大量URL,根據URL查找,如果使用B+Tree 存儲URL,內容很大,可以存儲一個索引列(hash)
hash 列可以使用觸發器來在插入數據時,自動插入hash 值
空間數據索引(R-Tree)
可以用作地理數據存儲,無需前綴查詢,從所有維度來索引數據
全文索引
查找的是文本中的關鍵詞,而不是直接比較瑣索引的值
索引的優點
- 減少服務器需要掃描的數據量
- 避免排序和臨時表
- 將隨機io變爲順序IO
如果數據表數據小,全表掃描效率更高
中,大型表,索引非常有效
特大型表。建立和使用索引的代價隨之增長,可以使用分區技術查出需要的一組數據,而不是一條一條記錄的去匹配
聚簇索引
聚簇索引不是一種單獨的索引類型,而是一種數據存儲方式。具體細節依賴於實現方式,但是Innodb 的聚簇索引實際在同一個結構中保存了B-Tree索引和數據行。
數據行實際上存放在索引的葉子頁中。
“聚簇”表示數據和相鄰的減值緊湊的存放在一起。因爲無法同時把數據行存放在兩個不同的地方,所以一個表只能有一個聚簇索引。
聚集的優點:
- 相關數據存儲在一起,如電子郵箱實現時,可以將ID 來聚集數據,只需要從磁盤讀取少數數據也就能獲取某個用戶的全部郵件。如果不適用聚簇,可能每封郵件發生一次IO
- 數據訪問更加快(相對於非聚集索引)
- 使用覆蓋索引掃描的查詢可以直接使用葉節點的主鍵值
缺點:
覆蓋索引
使用索引直接獲取列的數據,這樣不需要讀取數據行。如果一個索引包含(或者是覆蓋)所有要查詢的字段的值,稱之爲“覆蓋索引”
好處:
- 索引條目通常遠小於數據行大小,如果只需要讀取索引,那麼mysql 就會極大的減少數據訪問量
- 索引通常是按照列值順序存儲的(至少在單個頁內是如此)
- 一些存儲引擎如MyIsam,在內存中只緩存索引,數據則依賴操作系統來緩存,因此訪問數據需要一次系統調用
— 未完待續