前言:大部分業務研發同窗在工做的90%的場景下會和各類數據打交道,在此過程當中邁不過去的一個坎就是MySQL了,這篇文章從研發同窗的視角來說述在MySQL使用中須要瞭解的各類關鍵技術;
下面我會分別從MySQL的體系架構、事務、鎖、索引、性能、部署架構幾個方面來介紹; 非特殊註明的狀況下專指innodb引擎,但願各位在看完以後可以有所收穫!html
想要了解一個技術體系很是建議的一種方式是先看系統總體的架構設計,這樣可以快速的預覽整個框架,下降陌生感;而後經過分析一個典型的執行過程加強對過程的把控;mysql
在MySQL的設計中總體能夠分爲三層:文件系統、存儲(索引)、服務層;咱們所直接面向主要是服務層相關的模塊如:鏈接池、SQL、優化器、緩存等,能夠經過下圖對MySQL的組成有個大概的瞭解 程序員
再看一下一個SQL的執行過程,SQL Cache->解析器->優化器->執行引擎->存儲引擎;是否是相似於咱們設計的高性能服務端架構經過緩存->業務邏輯->業務數據;只是這個業務邏輯是在數據庫領域的而已;面試
數據庫事務應該是大部分程序員在面試時的必經題目了,先一句話描述一下我所理解的事務:
在併發場景保障一次會話對數據操做符合預期(原子性、一致性、隔離性、持久性)的方式;
下面會分爲名詞解釋和隔離級別兩趴來幫助咱們理解事務;
sql
想象一下咱們有多個客戶端(A/B/N)在並行的操做一張表T1中的數據,那麼就可能會存在以下幾種狀況:
數據庫
一、髒讀:A可能讀取到B會話中未提交事務修改的數據(不可預期的數據結果)
二、不可重複讀:一個事物內屢次查詢同一數據行,結果是不一致
三、幻讀:範圍檢索時兩次檢索會看到不一樣的數據行(範圍檢索不只限於大於小於,部分的in場景或者全表掃描同樣會被覆蓋)
segmentfault
在以上場景下,MySQL定義了以下四種隔離級別:
PS:默認隔離級別是Repeatable_Read緩存
敲重點:session
面試必考題:
一、MySQL事務的的隔離級別及其特性
二、解釋一下來髒讀、不可重複讀、幻讀的含義及其出現的場景 可以很好的回答以上問題,在MySQL事務單項上就及格了;
進階:
一、數據庫默認的隔離級別是什麼?大家線上數據庫用的是哪一個級別?爲何選擇這個級別?
二、MySQL RR 級別是如何解決不可重複讀?
三、MySQL RR 級別是否解決了幻讀?
數據結構
想回答好進階問題,就要對MySQL的鎖 & MVCC有必定的瞭解了
鎖是併發場景下解決共享資源操做衝突問題的常見手段,同理MySQL也採用了該種策略,下面會分別介紹一下MySQL的鎖
在正式介紹以前補充一點背景信息,對於MySQL而言,讀分爲快照讀和當前讀,
快照讀:select * from t1 where ;
當前讀:select xxx lock in share mode;select xxx for
update;insert/update/delete xxx;
表鎖
表鎖字義是對標進行加鎖,特色是開銷小,加鎖快,併發度低; MYISAM 引擎僅支持表鎖
在innodb引擎寫操做中兩種意向鎖(意向共享鎖和意向排他鎖)也是一種表鎖;
頁鎖
BDB引擎支持的鎖類型,引擎和相關資料都比較少,鎖粒度介於行和表之間
行鎖
一、行鎖和字面意思有一點區別,主要是區分於Oracle數據庫的行鎖是針對物理數據塊進行加鎖,而MySQL是針對索引進行加鎖的,也就意味着只有走索引才能加行鎖不然在須要加鎖的場景下就是表鎖
二、行鎖開銷大、加鎖慢、併發度高(衝突機率低)
三、行鎖一樣分爲共享鎖和排他鎖
間隙鎖
間隙鎖是在RR級別下生效,在當前讀場景下會鎖定索引間的間隙,保證索引間的記錄不變;以防止插入或更新間隙之間的記錄;
而RR級別下的間隙鎖也就部分解決了幻讀的問題
nextKey lock
等於 行鎖+間隙鎖
鎖衝突表
. | IX | IS | X | S |
---|---|---|---|---|
IX | 兼容 | 兼容 | 衝突 | 衝突 |
IS | 兼容 | 兼容 | 衝突 | 兼容 |
X | 衝突 | 衝突 | 衝突 | 衝突 |
S | 衝突 | 兼容 | 衝突 | 兼容 |
MVCC翻譯過來是多版本併發控制;在RC和RR級別下生效,就是解決上面所提到不一樣隔離級別下的不可重複讀和幻讀的問題;
咱們假設本身來來實現這個效果,通常的思路是否是在一個事物開啓的時候
一、經過記錄當前記錄的快照,在事物結束以前一直讀該快照來保障單次事物內結果是可預期的;
二、再加上鎖來阻止其餘併發更新
思路是正確的可是MySQL對快照的處理上不像咱們想象的同樣真的插入一條記錄而是經過undo_log+readview的機制來實現的
數據結構
innodb引擎在數據中增長了
事務號DB_TRX_ID(建立、更新、刪除)
回滾段指針DB_ROLL_PTR(用於追溯歷史版本數據)
DB_ROW_ID(隨着新行插入而單調遞增的行ID)
undo_log
insert/update/delete時會產生undo log
undo log中包含重建該行記錄被更新以前內容
readview
RC:事務中每條select語句都會建立一個快照(read view);
RR:事務在begin/start transaction以後的第一條select讀操做後, 會建立一個快照(read view), 將當前系統中活躍的其餘事務記錄記錄起來 而這個區分也就決定了RR和RC兩個級別下可見性的區別;
補充說明一下,有一種說法是幻讀在當前讀場景下是解決了; 可是在快照讀的場景下仍是仍是存在對方已經提交可是在當前session讀不出來該數據,因此是部分解決了(對細節感興趣的話能夠開兩個session 自行驗證一下);
根據最上面的MySQL的體系架構圖能夠得知數據最終是存儲在硬盤文件系統中的,計算機體系中硬盤數據是訪問最慢的,可是受限於內存數據的空間,在這種約束下經過針對具體業務場景,對高頻數據訪問場景經過增長索引的方式提升數據訪問效率
聚簇索引
以主鍵建立的索引
一、彙集索引在葉子節點存儲的是表中的數據
非聚簇索引(二級索引)
非主鍵建立的索引
一、索引葉子節點存儲的是主鍵和索引列
二、在檢索列中包含非索引列時,須要用葉子節點的主鍵,再去表中檢索其餘列(回表)
數據結構應該都不陌生,而索引就是經過不一樣數據結構的特性:查詢複雜度、空間、查詢和新增性能等來作出的選擇;
在一個海量數據的場景下咱們想要保障查詢的效率很直接的一種方式就是根據構建hash表;
另一種相對可控的就是樹形結構能夠經過控制樹的深度來保證查詢的效率;
B+ tree
它是一個矮胖子;左右兩個子樹的高度差的絕對值不超過1,而且左右兩個子樹都是一棵平衡二叉樹;
這樣的結構保障了查詢效率的相對高效和穩定;
可是咱們看一下在更新和新增場景下因爲要維持以上特性,須要進行分裂或合併,這樣對插入性能來講是有必定損失的;
Hash表
hash表的好處是查詢複雜度理論值是o(1),,可是帶來的問題以下
一、空間的消耗
二、不支持範圍和區間查詢
三、大數據量場景下的hash衝突,可能會致使退化成鏈表
在目前的MySQL版本中hash索引是MySQL自適應的沒法干預
基於以上索引的設計能夠看出,能夠分析出在索引使用的過程當中有以下特色
一個表可能建立多個索引
一次只能走一個索引
最左匹配
索引選擇上根據 where 條件中的列字段,從左至右的匹配,直到遇到區間或範圍查詢列;
注意此處和SQL中where 後 in、=條件的列字段順序無關;
查詢列不參與計算
若是對列字段添加了計算函數,則沒法走該字段的索引
where 和 order by limit 場景下的索引
在存在where 和 order by的場景下,本覺得會按照where條件走索引,可是在實際狀況下可能會發現最終索引是order by 字段,優先選擇order by字段索引;
緣由是:limit存在時,查詢的順序就有可能發生變化,查詢過程不是先經過where過濾再排序再limit而是根據order by索引反向取進而而後匹配where 條件看看是否知足,有點相似於全表掃描了;
即便是知道了以上各類原則,但在實際過程當中仍是可能遇到不符合預期的狀況,這時就須要MySQL提供的debug利器執行計劃了
type index: 掃描所有索引樹
range: 掃描部分索引,索引範圍掃描,對索引的掃描開始於某一點,返回匹配值域的行,常見於between、<、>等的查詢
ref: 非惟一性索引掃描,返回匹配某個單獨值的全部行。常見於使用非惟一索引即惟一索引的非惟一前綴進行的查找
eq_ref:惟一性索引掃描,對於每一個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或惟一索引掃描
const, system: 當MySQL對查詢某部分進行優化,並轉換爲一個常量時,使用這些類型訪問。如將主鍵置於where列表中,MySQL就能將該查詢轉換爲一個常量。system是const類型的特例,當查詢的表只有一行的狀況下, 使用system。
NULL: MySQL在優化過程當中分解語句,執行時甚至不用訪問表或索引。
possible_keys:可選的索引列
key:當前選擇的索引列
ref:鏈接匹配條件,若是走主鍵索引的話,該值爲: const, 全表掃描的話,爲null值
rows:掃描行數
extra: using index表示在相應的select中使用了覆蓋索引。 usingwhere表示存儲引擎搜到記錄後進行了後過濾(POST-FILTER),若是查詢未能使用索引,usingwhere的做用只是提醒咱們mysql要用where條件過濾z結果集。 using temporay表示用臨時表來存儲結果集,常見於排序和分組查詢。 usingfilesort,mysql中沒法用索引完成的排序成爲文件排序。
索引區分度
選擇高區分度列
覆蓋索引:查詢列儘可能包含在索引中,不然須要回表
SQL優化
大表翻頁:查詢列儘可能是索引列,不然建議用join或子查詢的方式經過主鍵完成
樂觀鎖
select for update 和基於version的樂觀鎖,在極限衝突場景下到底哪一種性能更高待驗證
數據量控制
分庫分表 冷熱數據分離
讀寫分離:備庫能夠考慮承擔一部分讀流量
物理或者邏輯架構的設計對於面向客戶提供的可靠性相當重要,做爲數據的核心數據的不可丟失以及提供高性能的解決方案,從架構層面有一些設計方式;
可靠性的一個重要保障方式就是作副本,一種經典的MySQL部署方式master-slave模式,部署兩個節點:master和slave;
master節點提供讀寫服務,經過解析binlog把數據變動複製到另一臺slave節點;存在ms級的延時,在某種程度上保障了數據的物理隔離,下降單點故障帶來的損失;
分庫實際上是一種邏輯上的架構,不過通常來講比較建議不一樣的邏輯庫能夠散列分佈,來提升吞吐性能,在各個邏輯庫上又能夠作主從架構,每個節點作一個備份。
無論是分庫仍是分表都要注意維度,按照業務場景來評估,儘可能保證檢索場景覆蓋了拆分維度。
傳統上而言主備都是在同地域甚至是同一個機房來下降同步延時,可是在一些極限場景下對數據的可用性以及吞吐量要求更高,這個時候跨地域的邏輯庫以及多副本的一致性架構就開始出現了,阿里巴巴電商交易在15年開始規劃跨地域數據解決方案,其中主要用到的策略
一、按router路由,保障同一用戶落在同一地域
二、跨地域的數據同步
三、同一sequence生產方式(集中式、分佈式)
當前基於最新分佈式一致協議(Paxos)構建的 多副本強一致方案也有落地
瞭解一些其餘類型的數據庫,在作技術選型和決策的時候可能會有所幫助,目前主流的大概是這些
MySQL/Oracle/PG/SQLServer 等
Hbase/MongoDB/Cassandra/LevelDB/Redis等
Neo4j/GraphDB等
歡迎交流關於數據庫的各類有意思的場景和問題