蒼茫大地一劍盡挽破,何處繁華笙歌落。 難道這普天之下盡沒我容納之處?爲什麼這面試官總要與我爭風相對,這不是難爲人嗎? mysql
每次經歷痛領悟,總會悠然南山下,獨自愁楚。記錄心中一片大海,惟有獨自努力,方能實現行業之俊傑。面試
一個陽光明媚得上午,迎面走來了一位身着雪白雪白得的白色襯衣,灰白色相間的休閒短褲,還給我露出了彰顯男兒本色的黑色腿毛。手拿銀色金屬質感得 MacBook Pro,外加一雙小白鞋。看着那稀少的髮量,在燈光的照耀下,甚至還有點反光。透過那從容不迫的臉色。 sql
我心裏不忍一顫,今天怕是要遇到人了。整整一個架構師的大叔呀。數據庫
果不其然,他手裏拿着個人簡歷,快速的掃了一下,而後用眼角餘光看了一下我,像是在掂量着什麼。上來開口即問。緩存
面試官: 看你簡歷描述精通 Mysql 優化方法,那你先說說你對Mysql的事務的瞭解吧。 安全
我吒吒輝心裏獨自平靜了一些,這個不難,就是一個事務定義和執行得調用的方法。卻不知後面的內容都是對我得嚴刑拷打。服務器
吒吒輝: 好呢,數據庫的事務是指一組sql語句訪問各類數據項的數據庫邏輯處理單元,在這組 SQL 操做中,要麼所有執行成功,要麼所有執行失敗。 微信
舉個經典的例子就是轉帳,事務 A 要進行轉帳,那麼,轉出的帳號要扣錢,轉入的帳號要加錢,這兩操做要麼同時執行成功,要麼都所有執行失敗。爲得就是確保數據的一致性。 多線程
吒吒輝: 通常請求進入MySQL,都是根據不一樣請求類型先在服務層內部作數據處理,而後在由後臺線程把數據刷到磁盤中。從而避免頻繁的讀寫磁盤。 架構
回答完畢後,本身笑笑的模一下頭髮,打理一下本身的髮型,整理了一下脖子上得領結。
面試官:吆喝,這小子還有點料啊,敢在太歲頭上動土,你還要反了天呀你。還在這欺負我頭上的沒頭髮。個小崽子。
面試官: 剛你提到了 MySQL 內部的工做,那你知道事務的特性嗎? 數據是如何刷盤? 服務層數據如何修改? 這樣得工做模式有什麼好處? 怎麼避免頻繁寫入? 讀請求和寫請求在服務層工做是同樣的嗎?
我心裏一鈍,這老禿驢,不要命啦,逮到這麼問我。看來本身得正經點,感受空氣中瀰漫着殺氣騰騰的氣息。
尷尬的衝他笑了一笑。立馬說到
吒吒輝: 在Mysql中事務的四大特性主要包含:
原子性(Atomicity)
一致性(Consistent)
隔離性(Isalotion)
持久性(Durable),簡稱爲 ACID。由它們共同來保證數據的一致性。
面試官: 嗯,說說你對這四大特的理解?
吒吒輝:
原子性: 故名思議原子是最小的元素單位,因此一個事務是一個不可拆分的最小工做單元。整個事務操做要麼所有執行成功,要麼所有失敗回滾。
因執行成功和失敗都是狀態的變化,若是隻有執行效果只有一半,數據就會不完整,從而違背了原子的特色。
一致性: 指數據庫從一個一致性的狀態轉換到另一個一致性的狀態。要保證事務先後的狀態要一致
隔離性:一個事務所作的修改在最終提交之前,對其餘事務是不可見的。
持久性:一旦事務提交,則其所作的修改就會永久保存到磁盤上。是執行結果一直生效。
面試官: 那數據庫底層是如何實現這四大特性的?
頓時,我望向他那張有點油膩的大臉,可他確實氣定神閒的望着我,面相是多麼的隨和。此刻心裏不斷策馬崩騰,第一個問題就給我整了這麼多,後面可咋辦? 心裏感受懸了
吒吒輝:
undo log 主要記錄數據的邏輯變化。因此須要將以前的操做都記錄下來,而後在發生錯誤或執行事務回滾時才能保證回滾到以前的操做。
面試官: 那這個請求過程是怎麼實現的?
好比當寫請求爲 insert 操做進入到數據庫服務層時,會先在緩衝池下面的寫緩衝中進行數據的寫入,而後先把寫請求的物理語言寫入到 read log中,並記錄一條刪除當前語句的邏輯語句到 undo log中。 最後由 MySQL 後臺線程按期刷新數據到磁盤裏面。
可是這個寫緩衝在5.5以前叫 插入緩衝,只針對 insert 優化; 如今對 update 和 delete更新操做都有效。如今叫作:寫緩衝
面試官: 這個寫緩衝有什麼好處呢?
吒吒輝:
避免頻繁磁盤I/O的寫操做,若是沒得這玩意,須要在磁盤裏面讀到數據,而後才能數據上面的修改。
但大多數場景下都是讀多寫少,若是在高併發的場景下,經過打包批次處理就能有效避免,在寫操做處理時資源開銷而影響到大量得讀請求。
一致性: 由 Redo log 日誌保證,也叫重作日誌,是用來實現事務的持久性。該日誌文件由兩部分組成:重作日誌緩衝(redo log buffer)以及重作日誌文件(redo log),前者是在內存中,後者在磁盤中。
當事務提交以後會把全部修改信息都會存到該日誌中。假設有個表叫作t1(id,username) 如今要插入數據(3,'吒吒輝')。
start transaction;
//數據寫入 寫緩衝中
insert into t1 (id,username) values (3,'吒吒輝')
// 生成 重作日誌 id =3 , username = '吒吒輝'
commit;複製代碼
面試官: 那這 redo log 有什麼做用?
爲了提高性能,不會把每次的修改都實時同步到磁盤,而是會先存到Boffer Pool(緩衝池)裏頭,把這個看成緩存來用。而後使用後臺線程去作緩衝池和磁盤之間的同步。
面試官: 那若是緩衝池的數據還沒來得急同步就宕機或斷電了怎麼辦? 這樣但是會致使丟部分已提交事務的修改信息!
吒吒輝: 因此引入redo log來記錄已成功提交事務的修改信息,而且會把redo log持久化到磁盤,系統重啓以後在讀取redo log恢復最新數據
面試官: 能夠舉一個場景,怎麼應用它們呢?
吒吒輝: 假如某個時刻數據庫崩潰,在崩潰以前有相關事務在執行,一半已經提交,一半還未提交。當數據庫重啓進行 crash-recovery 時,就會經過 Redo log將已經提交事務的更改先載入到內存,而後再寫到數據文件,而尚未提交的就經過 Undo log 進行 roll back。
面試官: 嗯,繼續
此時,心裏發毛。回答到這個程度,還面不改色,就一個繼續打發我了?你可真是深造。
吒吒輝:
InnoDB的 MVCC ,是經過在每行記錄的後面保存兩個隱藏的列來實現的。這兩個列, 一個保存了行的建立時間,一個保存了行的過時時間(或刪除時間), 固然存儲的並非實際的時間值,而是系統版本號。
面試官: 那不一樣的SQL請求,MySQL 是如何實現得?
吒吒輝:
面試官: 那這個機制有什麼好處?
吒吒輝:
它主要實現思想是經過數據多版原本作到讀寫分離。每一個請求給到不一樣版數據來執行。從而實現不加鎖讀進而作到讀寫並行。
MVCC機制實現還須要依賴於 undo log與read view
undo log: undo log 中記錄某行記錄的多個版本的數據。
read view: 用來判斷當前版本數據的可讀性
面試官: 那你給我畫個圖看看
什麼鬼,這玩意還畫圖。我用那水汪汪的大眼睛,直勾勾的看着他。只見他看口說到。
** 面試官:沒明白嗎? 就是用白板筆畫出原理圖解
吒吒輝: 我急忙應和到。額額額,好
吒吒輝: 就這樣,每一個請求修改時就會對應一個版本。
面試官: 那這 MVCC 是如何保證多個版本數據得最終一致呢?
吒吒輝: 因數據操做爲多個版本,因此它支持讀寫並行。
在請求進行事務操做前,會先入到讀、寫隊列裏面,而後再分別執行讀寫請求。若第一個寫請求執行事務但未提交,但這時它就會拿到最新版本。
若是此時讀請求進入也會直接執行,只不過讀取數據時得系統版本就爲未執行寫請求的版本號,根據它來進行數據的讀取。
這樣在事務未進行提交前,全部讀操做讀取到的數據是同樣的,由於它受系統當前版本控制。讀取的都是同一個版本數據。
面試官: MySQL是多線程, 那同時有多個線程修改同一行數據,這種狀況怎麼處理的?
吒吒輝: 通常若是系統達到併發執行的狀況,對用戶請求得要求是很高的,每秒至少得幾萬請求,若有併發問題產生。
假如事務 A、B 都要執行 update 操做,事務A先 update 數據時,會先獲取行鎖,鎖定數據,當事務 B 要進行 update 操做時,也會嘗試獲取該數據行的行鎖,但此時已經被事務A佔有,故事務B只能 wait。
吒吒輝: 如事務A,是個很費時的大SQL,長時間沒釋放鎖。那麼事務 B 就會等待超時。
查詢全局等待事務鎖超時時間
SHOW GLOBAL VARIABLES LIKE 'innodb_lock_wait_timeout';
面試官: 這個是在 update 的where後的條件是在有索引的狀況下吧?
吒吒輝: 嗯,是的,由於 innodb 的行數只有索引纔會觸發,鎖定的是行鎖
面試官: 那沒有索引的條件下,就沒辦法快速定位到數據行呀?
吒吒輝: 如果沒有索引的條件下,就會退化爲表鎖。 而後獲取全部行後,Mysql 再過濾符合條件的的行並釋放鎖。日後只有符合條件的行才持有鎖。
吒吒輝: 這樣就下降了併發度,而且性能開銷也會很大。
吒吒輝: 而最後的一致性,其實是經過原子性、持久性、隔離性來實現的
此刻,感受本身多少放鬆了一些,心裏有點小得意,感受達到了巔峯時刻。而後再拿着面前的礦泉水,飲了一口,順便擦了下汗水。一切是那麼得行雲流水。然而面試官那臉仍是一沉不變,像是在醞釀着什麼,這不又開始了。
面試官: 那請繼續數據是如何刷盤? 這樣得工做模式是怎麼避免頻繁讀寫得?
吒吒輝: 說到數據刷盤,首先得談下 innodb 緩衝池。
buffer pool 主要由數據、索引、插入緩衝、自適應哈希索引、鎖等組成。
不一樣請求會根據 buffer pool裏面對應部分數據來執行相關的操做。buffer pool中的數據會根據配置參數按期同步到磁盤中;只是這裏刷盤的數據主要由 數據和日誌 組成。
* 由 innodb_max_dirty_pages_pct 參數決定。在說到這個以前,得說說髒頁。
髒頁指存在於內存中的數據,但未同步到持久化存儲裏面,由於數據庫的數據用頁來讀取,故此稱之爲髒頁。
innodb_max_dirty_pages_pct 參數可動態調整,最小值爲0, 最大值爲99.99,默認值爲 75。
根據緩衝池的大小和存儲髒頁的數量計算比例,若是知足了就調用後臺線程把數據刷新到持久層當中裏面。
這樣作的好處就是合併其它數據頁,從而提升寫入的效率。
由 innodb_flush_log_at_trx_commit 變量來控制日誌緩衝刷新頻繁程度。
0:把日誌緩衝寫到日誌文件,而且每秒鐘刷新一次持久化存儲,可是事務提交時不作任何事。
1: 將日誌緩衝寫到日誌文件,而且每次事務提交都刷新到持久化存儲。這是默認的(而且是最安全的)設置,該設置能保證不會丟失任何已經提交的事務,除非磁盤或者操做系統是「僞」刷新,即寫入到磁盤緩衝。
2: 每次提交時把日誌緩衝寫到日誌文件,可是並不刷新到持久化存儲。InnoDB 每秒作一次刷新到持久化存儲。通常也會選2,若是MySQL進程「掛了」,2 不會丟失任何事務。若是整個服務器「掛了」或者斷電了,則仍是可能會丟失 1s 的事務。
爲何上面把日誌緩衝寫到日誌文件,還沒到持久化存儲呢?
由於在大部分操做系統中,把日誌緩衝寫到日誌文件只是簡單地把數據從 InnoDB 的內存緩衝轉移到了操做系統的緩存,也是在內存裏,並無真的把數據寫到了持久化存儲。因此還須要進行數據磁盤同步的刷寫。
吒吒輝: 緩衝池是下降頻繁得讀寫請求,這個得分狀況來看:
面試官: 嗯嗯
瞬間感受被榨乾了,怎麼都這樣出招,幸虧之前多少有點準備。突然感受本身身後開始滑落着汗滴。只見面試官看了看簡歷,臉上表情祥和,感受在端詳着什麼。。。。
面試官: 小夥,這今天就面到這裏,我看你好像有點熱呀,先回去等通知吧
感情你老這纔開胃呀!還能這樣整。行不行你到是給句花唄。讓我流這麼多汗,我得喝多少水才補得回來,這不虛都被你搞虛。瞬間炸裂開來
吒吒輝: 這樣啊,那好吧。。。。
* 我命由我不禁天,只爲那不甘心的,若有幫助,關注分享額
文章持續更新,可微信搜索「蓮花童子哪吒 」第一時間閱讀,還有在線技術答疑羣聊