日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

阿龙的学习笔记---MySQL45讲的总结(一)

發布時間:2023/12/14 数据库 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 阿龙的学习笔记---MySQL45讲的总结(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

學習這個大佬的課,的確超級有經驗:https://time.geekbang.org/column/intro/100020801


查詢語句過程

  • 連接器,分析器,優化器,執行器,最后到存儲引擎。

  • MySQL 可以分為 Server 層存儲引擎層兩部分。

    • Server 層包括連接器、查詢緩存、分析器、優化器、執行器等,涵蓋 MySQL 的大多數核心服務功能。
    • 存儲引擎層負責數據的存儲和提取。
  • 連接器:

    • 第一步,你會先連接到這個數據庫上,這時候接待你的就是連接器。連接器負責跟客戶端建立連接、獲取權限、維持和管理連接。
  • 查詢緩存:

    • 連接完成后,執行 select 語句了。會先查詢緩存。之前執行過的語句及其結果可能會以 key-value 對的形式,被直接緩存在內存中。key 是查詢的語句,value 是查詢的結果。
    • 但是,只要有對一個表的更新,這個表上所有的查詢緩存都會被清空。所以靜態表比較適合,更新較多的表不適合,反而影響效率。
  • 分析器:

    • 分析器先會做“詞法分析”。再做“語法分析”。根據詞法分析的結果,語法分析器會根據語法規則,判斷你輸入的這個 SQL 語句是否滿足 MySQL 語法。
  • 優化器

    • 經過了分析器,MySQL 就知道你要做什么了。在開始執行之前,還要先經過優化器的處理。優化器是在表里面有多個索引的時候,決定使用哪個索引;或者在一個語句有多表關聯(join)的時候,決定各個表的連接順序
  • 執行器:

    • 優化器知道了該怎么做,于是就進入了執行器階段,開始執行語句。如果有權限,就打開表繼續執行。打開表的時候,執行器就會根據表的引擎定義,去使用這個引擎提供的接口。

更新語句過程

  • 依然走上面的路:

    • 先連接
    • 查詢緩存會清空。
    • 分析器分析出是一條更新語句。
    • 優化器進行索引等優化。
    • 執行器執行。但是除了更新表中數據外,還有redolog和binlog兩個日志文件的更新。即物理日志 redo log 和邏輯日志 binlog。
  • redo log: InnoDB的物理日志模塊:

    • 是為了解決每次都得更新到數據庫而導致效率低下的問題。
    • 這個先日志的技術叫:WAL,全稱是 Write-Ahead Logging。具體來說,當有一條記錄需要更新的時候,InnoDB 引擎就會先把記錄寫到 redo log(粉板)里面,并更新內存,這個時候更新就算完成了。同時,InnoDB 引擎會在適當的時候,將這個操作記錄更新到磁盤里面,而這個更新往往是在系統比較空閑的時候做,這就像打烊以后掌柜做的事。
    • log存儲空間是一個環形結構,大小是固定的,如果滿了則需要寫回數據庫中一些。
    • log還解決了數據庫如果發生異常宕機的問題,重啟后可以恢復。要將持久化至磁盤的參數設置為每次事務都持久化到磁盤。
  • bin log: 系統server層的歸檔日志模塊:

    • 前面我們講過,MySQL 整體來看,一塊是 Server 層,它主要做的是 MySQL 功能層面的事情;一塊是引擎層,負責存儲相關的具體事宜。redo log 是 InnoDB 引擎特有的日志,而 Server 層也有自己的日志,稱為 binlog(歸檔日志),所有引擎都能用。
    • 除了這個不同之外,redo log 是物理日志,記錄的是“在某個數據頁上做了什么修改”;binlog 是邏輯日志,記錄的是這個語句的原始邏輯,比如“給 ID=2 這一行的 c 字段加 1 ”。redo log 是環形循環寫,binlog 是可以追加寫到一定大小后會切換到下一個。
  • 具體執行流程:

    • 先取到這一行的數據,再調用引擎接口寫入這行新數據。
    • 引擎將這行新數據更新到內存中,同時將這個更新操作記錄到 redo log 里面,變成 prepare。
    • 執行器生成這個操作的 binlog,并把 binlog 寫入磁盤。
    • 執行器提交事務,rodo log 變成 commit。
  • rodelog 的兩階段:prepare 和 commit:

    • 為了 bin 和 redo 的一致性。

事務隔離:

  • 針對ACID中的隔離性,問題針對多個事務同時進行產生的臟讀/不可重復讀/幻讀,定義了四個隔離級別以解決這些問題。

    未提交是指,一個事務還沒提交時,它做的變更就能被別的事務看到。 讀提交是指,一個事務提交之后,它做的變更才會被其他事務看到。 可重復讀是指,事務在執行期間看到的數據前后必須是一致的。 串行化,顧名思義是對于同一行記錄,所有操作是串行的(是這樣嗎?),“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖沖突的時候,后訪問的事務必須等前一個事務執行完成,才能繼續執行。
  • 作者簡單的闡述了一下更直觀的表述:

    “讀未提交”隔離級別下直接返回記錄上的最新值,沒有視圖概念; “讀提交”隔離級別下,這個視圖是在每個 SQL 語句開始執行的時候創建的; 可重復讀”隔離級別下,這個視圖是在事務啟動時創建的,整個事務存在期間都用這個視圖。 而“串行化”隔離級別下直接用加鎖的方式來避免并行訪問。
  • 詳細描述”可重復讀“的實現:

    • 在 MySQL 中,實際上每條記錄在更新的時候都會同時記錄一條回滾(undo)操作。記錄上的最新值,通過回滾操作,都可以得到前一個狀態的值。
    • 不同時刻啟動的事務會有不同的 read-view,,同一條記錄在系統中可以存在多個版本,就是數據庫的多版本并發控制(MVCC)。
    • 回滾日志(undo log)的刪除,在最早的視圖(read-view)之前的會被刪除。回滾日志記錄的數據量比較大,這也是為什么不應該使用長事務的原因之一,會導致回滾日志的文件很大。
  • 事務的啟動:實際操作注意點,誤操作導致長事務

    • autocommit 選項為1,則是設置在每個語句之后自動commit(我理解為每個語句是一個事務),但如果一些情況下默認設置為0,那么執行一個語句之后,就開啟了事務,但是沒有提交,這樣會導致你可能并不想開啟事務,或者一直沒有commit而導致長事務。
    • 所以作者建議,如果要打開事務,每次手動開啟。即set autocommit=1, 通過顯式語句的方式來啟動事務。

索引詳解:

  • 索引的出現是為了提高查詢效率,但是實現索引的方式卻有很多種:
    • 哈希表:查詢很快,但是無序,區間操作時不好使。所以,哈希表這種結構適用于只有等值查詢的場景,比如 Memcached 及其他一些 NoSQL 引擎。
    • 有序數組:等值查詢或是區間查詢,都能二分,效率很高。但是插入/刪除操作效率極低。
    • 多叉搜索樹:優點快,并且插入也快,需要做平衡, B+樹是InnoDB。
    • 跳表LSM 樹等數據結構也被用于引擎設計中。(自己看)
    • 經驗:數據庫底層存儲的核心就是基于這些數據模型的。每碰到一個新數據庫,我們需要先關注它的數據模型,這樣才能從理論上分析出這個數據庫的適用場景。
  • InnoDB 的索引:
    • B+樹。
    • 索引類型分為 主鍵索引 和 非主鍵索引。主鍵是聚簇索引;非主鍵索引中存儲的是主鍵的值,也叫二級索引,需要查詢兩次。
  • 索引維護:
    • 自增的主鍵比較利于維護,不會產生葉子節點的分裂或合并。
  • 優化索引:
    • 覆蓋索引技巧:
      • 比如當我們查詢非主鍵索引的時候,取某個字段的值,則需要二次回表再去主鍵索引中找一遍,這樣就要查找兩個B+樹。這時覆蓋索引這個技巧就用得上了。
      • 個人認為這里說的覆蓋索引不是一種類型,只是一種技巧,是利用聯合索引實現的。比如從非主鍵查找主鍵的值,那么只需要一次。那么比如建立<身份證,姓名>的聯合索引,那么如果有很多請求都是通過身份證查找名字,那么在這個聯合索引中就能遍歷一次找到。
    • 最左前綴原則:
      • 兩個層面,一是字符串可以最左前綴匹配,因為排序就是按照這個比較方法排序的。
      • 聯合索引中,也是一個技巧和特性。聯合索引也支持最左前綴的匹配。比如還是<身份證,姓名>這個聯合索引,假如創建了這個聯合索引,那么<身份證>這個單獨的索引就可以不用建立了,因為聯合索引存儲時,按照順序建立索引,身份證在前,前面匹配到即可,所以按身份證查詢時,也能用到索引的優勢。
    • 索引下推優化:
      • MySQL5.6之后推出的。
      • 比如建立了(name, age)的聯合索引。語句是:select * from tuser where name like '張%' and age=10 and ismale=1;
      • 那么沒有索引下推的話,應該在(name,age)中查詢到前綴是”張“的人,然后依次回表,查詢是否滿足條件。
      • 如果有的話,那么命中了name=”張%“之后,還會再判斷 age 是否滿足。滿足了之后,才會回表,這樣回表次數更少。
      • 總結: 在滿足語句需求的情況下, 盡量少地訪問資源是數據庫設計的重要原則之一。我們在使用數據庫的時候,尤其是在設計表結構時,也要以減少資源消耗作為目標。

并發控制——全局鎖 & 表鎖

  • 全局鎖:

    • 將整個數據庫鎖住,處于只讀狀態,其他更新/定于語句或事務都會阻塞。
    • 典型使用場景是在全庫邏輯備份中。如果不加鎖,則可能導致數據不一致。
      • 能拿到一致性視圖的不用全局鎖的方式:在InnoDB中,可重復讀的隔離級別,會在事務開始時創建一致性視圖,那么這時備份則不用全局鎖。
      • 但是只有InnoDB有啊,比如MyISAM就得用全局鎖。所以并不是沒用。
  • 表級鎖

    • 表級鎖是鎖住整個表,而不是整個數據庫。InnoDB中很少用到表級鎖。
    • 另一種表級鎖是元數據鎖MDL(metadata lock)。這個鎖主要是針對表結構的。在訪問某個表時,這個表的結構不應該發生變化,會加讀鎖。在修改這個表結構時,加寫鎖。上述是默認自動加鎖的。
      • 不過這個鎖又可能會造成問題,比如長事務一直沒有釋放MDL讀鎖,你要改變結構會加MDL寫鎖,然后會等待之前的讀鎖事務釋放。那么之后的所有讀操作都不能進行了。
      • 解決方法:首先對于長事務的處理,之前提過。再者更改表結構的語句可以設置一個等待時間,超時則放棄重試。

并發控制——行鎖

  • MySQL 的行鎖是在引擎層由各個引擎自己實現的。
  • 在 InnoDB 事務中,行鎖是在需要的時候才加上的,但并不是不需要了就立刻釋放,而是要等到事務結束時才釋放。這個就是兩階段鎖協議。
  • 所以經驗是要把最可能造成鎖沖突、最可能影響并發度的鎖盡量往后放。
  • 死鎖和死鎖檢測:
    • 兩種策略:
      • 一種是超時等待,但時間太長影響性能,時間太短會誤操作;
      • 另一種是死鎖檢測。是有額外負擔的,時間復雜度O(n),會導致性能下降。
    • 解決熱點行更新的性能問題:
      • 去掉死鎖檢測,不太靠譜,除非保證沒有死鎖。
      • 控制并發度。如果同時只有10個線程更新熱點行,那么死鎖檢測也很快,其他的排隊。牛逼的專家可以修改MySQL源碼,或者使用中間件排隊。再或者將熱點行拆分成多行,這個需要考慮業務具體邏輯。

事務隔離?查詢與更新的差異

  • 前面提到可重復讀隔離級別,事務 T 啟動的時候會創建一個視圖 read-view,之后事務 T 執行期間,即使有其他事務修改了數據,事務 T 看到的仍然跟在啟動時看到的一樣。

  • 但是在更新的時候,情況就復制了起來,這時要加行鎖,等到鎖了之后,修改的是視圖中的值,還是最新的值呢??

  • 比如如下操作:表中有id=1,k=1這個數據。

    注意: begin/start transaction 命令并不是一個事務的起點,在執行到它們之后的第一個操作 InnoDB 表的語句,事務才真正啟動。如果你想要馬上啟動一個事務,可以使用 start transaction with consistent snapshot 這個命令。第一種啟動方式,一致性視圖是在執行第一個快照讀語句時創建的;第二種啟動方式,一致性視圖是在執行 start transaction with consistent snapshot 時創建的。

    • 結論:事務A讀取到的是1,事務B select 到的是最新的3。先看一下MVCC具體實現,在分析查詢與更新。
  • MVCC

    • InnoDB 里面每個事務有一個唯一的事務 ID,叫作 transaction id,事務開始時申請,嚴格遞增。
    • 每一行也有多個版本,按順序,每個版本會記錄更新的事務的trx_id。多個版本也不是物理存儲多個版本,而是通過**回滾日志(undo_log)**進行回滾。否則每次創建快照都要將數據版本全取出來就太復雜了。
    • 可重復讀的定義,一個事務啟動的時候,能夠看到所有已經提交的事務結果。
    • 具體實現
      • 在實現上, InnoDB 為每個事務構造了一個啟動時活躍事務數組,用來保存這個事務啟動瞬間,當前正在“活躍”的所有事務 ID。“活躍”指的就是,啟動了但還沒提交。數組里面事務 ID 的最小值記為低水位,當前系統里面已經創建過的事務 ID 的最大值加 1 記為高水位

      • 這樣,對于當前事務的啟動瞬間來說,一個數據版本的 row trx_id,有以下幾種可能:

      • 如果落在綠色部分,表示這個版本是已提交的事務或者是當前事務自己生成的,這個數據是可見的;
      • 如果落在紅色部分,表示這個版本是由將來啟動的事務生成的,是肯定不可見的;
      • 如果落在黃色部分,那就包括兩種情況
      • 若 row trx_id 在數組中,表示這個版本是由還沒提交的事務生成的,不可見;
      • 若 row trx_id 不在數組中,表示這個版本是已經提交了的事務生成的,可見。
      • 利用這個特性,開始事務創建快照的時候只需要創建這個數組即可。

  • 查詢與更新的差異

    • 普通的查詢我們可以知道,在開始事務時創建的視圖,查詢的版本肯定按照上述的規則,所以在上面的例子中,不會查詢到B和C的更改。
    • 更新就不太一樣了,只看下圖的B和C。B事務先開始,C事務后開始然后提交。所以按理說B是看不到C的更新這個操作的。**但是B需要更新操作,更新數據都是先讀后寫的,而這個讀,只能讀當前的值,稱為“當前讀”(current read)。**這時 k 讀出是 2, 加 1 是 3。而自己的更新時能被本事務看到的,所以之后select k則結果是3。

總結

以上是生活随笔為你收集整理的阿龙的学习笔记---MySQL45讲的总结(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。