Mysql從入門到入神之(二)Select 和Update的執行過程

前言

文本已收錄至個人GitHub倉庫,歡迎Star:github.com/bin39232820…
種一棵樹最好的時間是十年前,其次是如今
我知道不少人不玩qq了,可是懷舊一下,歡迎加入六脈神劍Java菜鳥學習羣,羣聊號碼:549684836 鼓勵你們在技術的路上寫博客mysql

絮叨

咱們繼續來探索mysql。上一篇文章git

一條Select語句的查詢過程

大致來講,MySQL 能夠分爲 Server 層和存儲引擎層兩部分。github

Server 層包括鏈接器、查詢緩存、分析器、優化器、執行器等,涵蓋 MySQL 的大多數核心服 務功能,以及全部的內置函數(如日期、時間、數學和加密函數等),全部跨存儲引擎的功能都 在這一層實現,好比存儲過程、觸發器、視圖等。redis

而存儲引擎層負責數據的存儲和提取。其架構模式是插件式的,支持 InnoDB、MyISAM、 Memory 等多個存儲引擎。如今最經常使用的存儲引擎是 InnoDB,它從 MySQL 5.5.5 版本開始成 爲了默認存儲引擎。sql

也就是說,你執行 create table 建表的時候,若是不指定引擎類型,默認使用的就是 InnoDB。不過,你也能夠經過指定存儲引擎的類型來選擇別的引擎數據庫

鏈接器

鏈接器 很好理解就是你輸入帳號密碼去鏈接mysql ,好比咱們navicat客戶端,好比咱們JDBC數據庫鏈接池這些,都算是鏈接緩存

鏈接完成後,若是你沒有後續的動做,這個鏈接就處於空閒狀態,你能夠在 show processlist 命令中看到它。文本中這個圖是 show processlist 的結果,其中的 Command 列顯示 爲「Sleep」的這一行,就表示如今系統裏面有一個空閒鏈接架構

創建鏈接的過程一般是比較複雜的,在使用中要儘可能減小創建鏈接的動做,也就是儘可能使用長鏈接。因此咱們通常用池技術咯

查詢緩存

鏈接創建完成後,你就能夠執行 select 語句了。執行邏輯就會來到第二步:查詢緩存。函數

MySQL 拿到一個查詢請求後,會先到查詢緩存看看,以前是否是執行過這條語句,也就是相似於redis key 就是你的sql value 就算sql的返回值。post

可是大多數狀況下我會建議你不要使用查詢緩存,爲何呢?由於查詢緩存每每弊大於利。

舉個例子 假設你這個sql是查當前的訂單列表查詢,每次只要有人插入訂單,那麼你這個緩存就要被刪除,因此說這樣的話,對於跟新數據庫還要多一步操做,因此是沒有必須的,mysql默認是關閉緩存的,而且在mysql 8中是沒有緩存這個概念了。

你能夠將參數 query_cache_type 設置成 DEMAND,這樣對於默認的 SQL 語句都不使用查詢緩存

分析器

若是沒有命中查詢緩存,就要開始真正執行語句了。首先,MySQL 須要知道你要作什麼,所以 須要對 SQL 語句作解析

分析器先會作「詞法分析」。你輸入的是由多個字符串和空格組成的一條 SQL 語句,MySQL 須要識別出裏面的字符串分別是什麼,表明什麼。

優化器

通過了分析器,MySQL 就知道你要作什麼了。在開始執行以前,還要先通過優化器的處理。

優化器是在表裏面有多個索引的時候,決定使用哪一個索引

執行器

MySQL 經過分析器知道了你要作什麼,經過優化器知道了該怎麼作,因而就進入了執行器階 段,開始執行語句

對於有索引的表,執行的邏輯也差很少。第一次調用的是「取知足條件的第一行」這個接口,之 後循環取「知足條件的下一行」這個接口,這些接口都是引擎中已經定義好的

小問題

若是表 T 中沒有字段 k,而你執行了這個語句 select * from T where k=1, 那確定是會報「不存在這個列」的錯誤: 「Unknown column ‘k’ in ‘where clause’」。你以爲這個錯誤是在咱們上面提到的哪一個階段報出來的呢?

應該是分析器。優化器是對sql語句優化出一個最好的方案,若是沒有該表,也就不存 在優化一說,因此應該是在分析器裏面對sql作了分析以後,發現沒有該表就直接拋異常提示 了。

SQL更新語句是如何執行的?

前面咱們系統瞭解了一個查詢語句的執行流程,並介紹了執行過程當中涉及的處理模塊。相信你還 記得,一條查詢語句的執行過程通常是通過鏈接器、分析器、優化器、執行器等功能模塊,最後 到達存儲引擎

那麼,一條更新語句的執行流程又是怎樣的呢?

能夠肯定的說,查詢語句的那一套流程,更新語句也是一樣會走一遍

你執行語句前要先鏈接數據庫,這是鏈接器的工做。

在一個表上有更新的時候,跟這個表有關的查詢緩存會失效,因此這條語句就會 把表 T 上全部緩存結果都清空。這也就是咱們通常不建議使用查詢緩存的緣由

接下來,分析器會經過詞法和語法解析知道這是一條更新語句。優化器決定要使用 ID 這個索 引。而後,執行器負責具體執行,找到這一行,而後更新。

與查詢流程不同的是,更新流程還涉及兩個重要的日誌模塊,它們正是咱們今天要討論的主 角:redo log(重作日誌)和 binlog(歸檔日誌)

重要的日誌模塊:redo log

不知道你還記不記得《孔乙己》這篇文章,酒店掌櫃有一個粉板,專門用來記錄客人的賒帳記 錄。若是賒帳的人很少,那麼他能夠把顧客名和帳目寫在板上。但若是賒帳的人多了,粉板總會 有記不下的時候,這個時候掌櫃必定還有一個專門記錄賒帳的帳本。

若是有人要賒帳或者還帳的話,掌櫃通常有兩種作法

  • 一種作法是直接把帳本翻出來,把此次賒的帳加上去或者扣除掉
  • 另外一種作法是先在粉板上記下此次的帳,等打烊之後再把帳本翻出來覈算。

在生意紅火櫃檯很忙時,掌櫃必定會選擇後者,由於前者操做實在是太麻煩了。首先,你得找到 這我的的賒帳總額那條記錄。你想一想,密密麻麻幾十頁,掌櫃要找到那個名字,可能還得帶上老 花鏡慢慢找,找到以後再拿出算盤計算,最後再將結果寫回到帳本上。

在 MySQL 裏也有這個問題,若是每一次的更新操做都須要寫進磁盤,而後磁盤也要找到 對應的那條記錄,而後再更新,整個過程 IO 成本、查找成本都很高。爲了解決這個問題, MySQL 的設計者就用了相似酒店掌櫃粉板的思路來提高更新效率

而粉板和帳本配合的整個過程,其實就是 MySQL 裏常常說到的 WAL 技術,WAL 的全稱是 Write-Ahead Logging,它的關鍵點就是先寫日誌,再寫磁盤,也就是先寫粉板,等不忙的時 候再寫帳本

具體來講,當有一條記錄須要更新的時候,InnoDB 引擎就會先把記錄寫到 redo log(粉板) 裏面,並更新內存,這個時候更新就算完成了。同時,InnoDB 引擎會在適當的時候,將這個操 做記錄更新到磁盤裏面,而這個更新每每是在系統比較空閒的時候作,這就像打烊之後掌櫃作的 事。

與此相似,InnoDB 的 redo log 是固定大小的,好比能夠配置爲一組 4 個文件,每一個文件的大 小是 1GB,那麼這塊「粉板」總共就能夠記錄 4GB 的操做。從頭開始寫,寫到末尾就又回到開 頭循環寫,以下面這個圖所示。

wirte pos 是當前記錄的位置,一邊寫一邊後移,寫到第 3 號文件末尾後就回到 0 號文件開頭。 checkpoint 是當前要擦除的位置,也是日後推移而且循環的,擦除記錄前要把記錄更新到數據 文件。

write pos 和 checkpoint 之間的是「粉板」上還空着的部分,能夠用來記錄新的操做。若是 write pos 追上 checkpoint,表示「粉板」滿了,這時候不能再執行新的更新,得停下來先擦 掉一些記錄,把 checkpoint 推動一下。

有了 redo log,InnoDB 就能夠保證即便數據庫發生異常重啓,以前提交的記錄都不會丟失, 這個能力稱爲crash-safe。

重要的日誌模塊:binlog

MySQL 總體來看,其實就有兩塊:一塊是 Server 層,它主要作的是 MySQL 功能層面的事情;還有一塊是引擎層,負責存儲相關的具體事宜。上面咱們聊到的粉板 redo log 是 InnoDB 引擎特有的日誌,而 Server 層也有本身的日誌,稱爲 binlog(歸檔日誌)。

爲何會有兩份日誌呢?

由於最開始 MySQL 裏並無 InnoDB 引擎。MySQL 自帶的引擎是 MyISAM,可是 MyISAM 沒有 crash-safe 的能力,binlog 日誌只能用於歸檔。而 InnoDB 是另外一個公司以插件形式引入 MySQL 的,既然只依靠 binlog 是沒有 crash-safe 能力的,因此 InnoDB 使用另一套日誌系 統——也就是 redo log 來實現 crash-safe 能力。

redo log 和binlog的區別

  • redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 層實現的,全部引擎均可 以使用
  • redo log 是物理日誌,記錄的是「在某個數據頁上作了什麼修改」;binlog 是邏輯日誌, 記錄的是這個語句的原始邏輯,好比「給 ID=2 這一行的 c 字段加 1
  • redo log 是循環寫的,空間固定會用完;binlog 是能夠追加寫入的。「追加寫」是指 binlog 文件寫到必定大小後會切換到下一個,並不會覆蓋之前的日誌。

真正的執行流程

update T set c=c+1 where ID=2;

  • 執行器先找引擎取 ID=2 這一行。ID 是主鍵,引擎直接用樹搜索找到這一行。若是 ID=2 這一行所在的數據頁原本就在內存中,就直接返回給執行器;不然,須要先從磁盤讀入內 存,而後再返回。
  • 執行器拿到引擎給的行數據,把這個值加上 1,好比原來是 N,如今就是 N+1,獲得新的 一行數據,再調用引擎接口寫入這行新數據。
  • 引擎將這行新數據更新到內存中,同時將這個更新操做記錄到 redo log 裏面,此時 redo log 處於 prepare 狀態。而後告知執行器執行完成了,隨時能夠提交事務
  • 執行器生成這個操做的 binlog,並把 binlog 寫入磁盤。
  • 執行器調用引擎的提交事務接口,引擎把剛剛寫入的 redo log 改爲提交(commit)狀 態,更新完成。

能夠看到,若是不使用「兩階段提交」,那麼數據庫的狀態就有可能和用它的日誌恢復出來的庫 的狀態不一致。

簡單說,redo log 和 binlog 均可以用於表示事務的提交狀態,而兩階段提交就是讓這兩個狀態 保持邏輯上的一致。

結尾

文章出自極客時間的Mysql45講,很是不錯哦

平常求贊

好了各位,以上就是這篇文章的所有內容了,能看到這裏的人呀,都是真粉

創做不易,各位的支持和承認,就是我創做的最大動力,咱們下篇文章見

六脈神劍 | 文 【原創】若是本篇博客有任何錯誤,請批評指教,不勝感激 !

相關文章
相關標籤/搜索