閱讀MySQL實戰45講第9天

目錄

一、你的 SQL 語句為什么變“慢”

二、什么情況會引發數據庫的 flush 過程?

三、分析上面四種場景對性能的影響

四、InnoDB 刷臟頁的控制策略

五、原作者小結:


引言: 一條 SQL 語句,正常執行的時候特別快,但是有時也不知道怎么回事,它就會變得特別慢,并且這樣的場景很難復現,它不只隨機,而且持續時間還很短。 看上去,這就像是數據庫“抖”了一下

一、你的 SQL 語句為什么變“慢”

現在你知道了,InnoDB 在處理更新語句的時候,只做了寫日志這一個磁盤操作。這個日志叫作 redo log(重做日志),也就是《孔乙己》里咸亨酒店掌柜用來記賬的粉板,在更新內存寫完 redo log 后,就返回給客戶端,本次更新成功。 (先寫粉板,有空了在更新到賬本)
做下類比的話,掌柜記賬的賬本是數據文件,記賬用的粉板是日志文件(redo log),掌柜的記憶就是內存。
掌柜總要找時間把賬本更新一下,這對應的就是把內存里的數據寫入磁盤的過程,術語就是 flush (flush->清刷,清空) 在這個flush 操作執行之前,孔乙己的賒賬總額,其實跟掌柜手中賬本里面的記錄是不一致的。因為孔乙己今天的賒賬金額還只在粉板上,而賬本里的記錄是老的,還沒把今天的賒賬算進去。 (目前這兩個頁面內容是不統一的)
①當內存數據頁跟磁盤數據頁內容不一致的時候,我們稱這個內存頁為“臟頁”。
②內存數據寫入到磁盤后,內存和磁盤上的數據頁的內容就一致了,稱為“干凈頁”
不論是臟頁還是干凈頁,都在內存中。在這個例子里,內存對應的就是掌柜的記憶。 (無論是臟頁還是干凈頁,都保存了欠賬的記錄,都可能要經過內存,存入到磁盤里面的)
接下來,用一個示意圖來展示一下“孔乙己賒賬”的整個操作過程。假設原來孔乙己欠賬 10文,這次又要賒 9 文。
? ? ? ? ? ? ? ? ? ?圖 1 “孔乙己賒賬”更新和 flush 過程
平時執行很快的更新操作,其實就是在寫內存和日志,而 MySQL 偶爾“抖”一下的那個瞬間,可能就是在 刷臟頁(flush)

二、什么情況會引發數據庫的 flush 過程?

繼續用咸亨酒店掌柜的這個例子。
想一想:掌柜在什么情況下會把粉板上的賒賬記錄改到賬本上?
①第一種場景是, 粉板滿了 (redo log滿了) ,記不下了。這時候如果再有人來賒賬,掌柜就只得放下手里的活兒,將粉板上的記錄擦掉一些,留出空位以便繼續記賬。當然在擦掉之前,他必須先將正確的賬目記錄到賬本中才行。 (記賬記滿了,先去清理粉板,才能繼續保證記賬的過程)
這個場景,對應的就是 InnoDB 的 redo log 寫滿了。這時候系統會停止所有更新操作,把 checkpoint 往前推進,redo log 留出空間可以繼續寫。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?圖 2 redo log 狀態圖
checkpoint 可不是隨便往前修改一下位置就可以的。比如圖 2 中,把 checkpoint 位置從 CP 推進到 CP’,就需要將兩個點之間的日志(淺綠色部分),對應的所有臟頁都flush 到磁盤上。之后,圖中從 write pos 到 CP’之間就是可以再寫入的 redo log 的區域 (簡單來說,這個推的過程可以理解為協調內存與磁盤數據同步的過程加上清理臟頁)
②第二種場景是,這一天生意太好,要記住的事情太多,掌柜發現自己快記不住了,趕緊找出賬本把孔乙己這筆賬先加進去。
這種場景,對應的就是系統內存不足。當需要新的內存頁,而內存不夠用的時候,就要淘汰一些數據頁,空出內存給別的數據頁使用。如果淘汰的是“臟頁”,就要先將臟頁寫到磁盤。(客房不夠了,必須先清人,再去招待新客人入住)
③第三種場景是,生意不忙的時候,或者打烊之后。這時候柜臺沒事,掌柜閑著也是閑著,不如更新賬本。
這種場景,對應的就是 MySQL 認為系統“空閑”的時候。當然,MySQL“這家酒店”的生意好起來可是會很快就能把粉板記滿的,所以“掌柜”要合理地安排時間,即使是“生意好”的時候,也要見縫插針地找時間,只要有機會就刷一點“臟頁”。 (有空就刷新,沒空也要見縫插針的刷新)
④第四種場景是,年底了咸亨酒店要關門幾天,需要把賬結清一下。這時候掌柜要把所有賬都記到賬本上,這樣過完年重新開張的時候,就能就著賬本明確賬目情況了。 這種場景,對應的就是 MySQL 正常關閉的情況。這時候,MySQL 會把內存的臟頁都flush 到磁盤上,這樣下次 MySQL 啟動的時候,就可以直接從磁盤上讀數據,啟動速度會很快。 (關數據庫后,然后慢慢對賬)

三、分析上面四種場景對性能的影響

其中,第三種情況是屬于 MySQL 空閑時的操作,這時系統沒什么壓力,而第四種場景是數據庫本來就要關閉了。這兩種情況下,你不會太關注“性能”問題。
所以這里,我們主要來分析一下前兩種場景下的性能問題。
第一種是“redo log 寫滿了,要 flush 臟頁”,這種情況是 InnoDB 要盡量避免的。因為出現這種情況的時候,整個系統就不能再接受更新了,所有的更新都必須堵住。如果你從監控上看,這時候更新數會跌為 0。 (前有狼后有虎,進退兩難)

第二種是“內存不夠用了,要先將臟頁寫到磁盤”,這種情況其實是常態。


InnoDB 用緩沖池(buffer pool)管理內存,緩沖池中的內存頁有三種狀態:
第一種是,還沒有使用的;
第二種是,使用了并且是干凈頁;
第三種是,使用了并且是臟頁。
InnoDB 的策略是盡量使用內存,因此對于一個長時間運行的庫來說,未被使用的頁面很少。
而當要讀入的數據頁沒有在內存的時候,就必須到緩沖池中申請一個數據頁。這時候只能把最久不使用的數據頁從內存中淘汰掉:
①如果要淘汰的是一個干凈頁,就直接釋放出來復用; (干凈頁比較少,所以刷臟頁是常態)
②但如果是臟頁呢,就必須將臟頁先刷到磁盤,變成干凈頁后才能復用。 (干凈頁可以直接用,不干凈要沖洗(flush)才能用)
所以,刷臟頁雖然是常態,但是出現以下這兩種情況,都是會明顯影響性能的:
1. 一個查詢要淘汰的臟頁個數太多,會導致查詢的響應時間明顯變長; (臟的太多,刷不過來)
2. 日志寫滿,更新全部堵住,寫性能跌為 0,這種情況對敏感業務來說,是不能接受的。 (刷的能力為0,直接gg)
所以, InnoDB 需要有控制臟頁比例的機制 ,來盡量避免上面的這兩種情況。

四、InnoDB 刷臟頁的控制策略

接下來會介紹InnoDB 臟頁的控制策略,以及和這些策略相關的參數。
首先,你要正確地告訴 InnoDB 所在主機的 IO 能力,這樣 InnoDB 才能知道需要全力刷臟頁的時候,可以刷多快。
1、對于上述這段話比喻一下:這里你可以把數據庫的 IO能力 想象成在倉庫里搬運貨物
  • 磁盤:就像一個大倉庫,數據以文件等形式存放在這里。比如在數據庫中,表數據、索引數據等存放在磁盤文件里。
  • 內存:類似倉庫門口的暫存區。數據庫操作數據時,先把磁盤里的數據(貨物 )搬到內存暫存區(加載到內存 ),這樣處理起來速度更快。比如查詢表數據,先把對應表數據從磁盤讀入內存。
  • IO 操作
    • 讀 IO:從磁盤倉庫往內存暫存區搬運貨物,即數據庫從磁盤讀取數據到內存,像執行查詢語句獲取數據時就會發生讀 IO 。(取)
    • 寫 IO:把內存暫存區處理好的貨物(修改后的數據 )放回磁盤倉庫,也就是數據庫將內存中修改后的數據寫回磁盤,像事務提交時會把修改的數據持久化到磁盤 。(存)
  • 隨機 IO 與順序 IO
    • 隨機 IO:在倉庫里隨機找不同位置的貨物搬運,對應數據庫中數據文件的隨機讀寫,比如索引訪問,要在磁盤不同位置找索引項 。
    • 順序 IO:像沿著倉庫通道按順序搬運貨物,如數據庫日志文件的順序寫入,按順序記錄事務操作,提升寫入效率 。

2、如果你來設計策略控制刷臟頁的速度,會參考哪些因素呢?
這個問題可以這么想,如果刷太慢,會出現什么情況?首先是內存臟頁太多,其次是 redo log 寫滿。
所以,InnoDB 的刷盤速度就是要參考這兩個因素:一個是臟頁比例,一個是 redo log 寫盤速度。
InnoDB 會根據這兩個因素先單獨算出兩個數字。
參數 innodb_max_dirty_pages_pct 是臟頁比例上限,默認值是 75%。InnoDB 會根據當前的臟頁比例(假設為 M),算出一個范圍在 0 到 100 之間的數字,計算這個數字的偽代碼類似這樣:
1 F1(M)
2 {
3 if M>=innodb_max_dirty_pages_pct then
4 return 100;
5 return 100*M/innodb_max_dirty_pages_pct;
6 }
InnoDB 每次寫入的日志都有一個序號,當前寫入的序號跟 checkpoint 對應的序號之間的差值,我們假設為 N。InnoDB 會根據這個 N 算出一個范圍在 0 到 100 之間的數字,這個計算公式可以記為 F2(N)。 F2(N) 算法比較復雜,你只要知道 N 越大,算出來的值越 大就好了。
然后, 根據上述算得的 F1(M) 和 F2(N) 兩個值,取其中較大的值記為 R,之后引擎就可以按照 innodb_io_capacity 定義的能力乘以 R% 來控制刷臟頁的速度。

上述的計算流程比較抽象,不容易理解,畫了一個簡單的流程圖。圖中的 F1、F2就是上面我們通過臟頁比例 redo log 寫入速度算出來的兩個值。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?圖 3 InnoDB 刷臟頁速度策略

現在你知道了,InnoDB 會在后臺刷臟頁,而刷臟頁的過程是要將內存頁寫入磁盤。所以,無論是你的查詢語句在需要內存的時候可能要求淘汰一個臟頁,還是由于刷臟頁的邏輯會占用 IO 資源并可能影響到了你的更新語句,都可能是造成你從業務端感知到mysql“抖”了一下的原因。
要盡量避免這種情況,你就要合理地設置 innodb_io_capacity 的值,并且 平時要多關注臟頁比例,不要讓它經常接近 75%
其中,臟頁比例是通過Innodb_buffer_pool_pages_dirty/Innodb_buffer_pool_pages_total 得到的,具體的命令參考下面的代碼:
1 mysql> select VARIABLE_VALUE into @a from global_status where VARIABLE_NAME = 'Innodb_bu
2 select VARIABLE_VALUE into @b from global_status where VARIABLE_NAME = 'Innodb_buffer_po
3 select @a/@b;

接下來,看一個有趣的策略:
一旦一個查詢請求需要在執行過程中先 flush 掉一個臟頁時,這個查詢就可能要比平時慢了。
而 MySQL 中的一個機制,可能讓你的查詢會更慢:在準備刷一個臟頁的時候,如果這個數據頁旁邊的數據頁剛好是臟頁,就會把這個“鄰居”也帶著一起刷掉;而且這個把“鄰居”拖下水的邏輯還可以繼續蔓延,也就是對于每個鄰居數據頁,如果跟它相鄰的數據頁也還是臟頁的話,也會被放到一起刷。 (連帶效應,跟連連看一樣)
在 InnoDB 中,innodb_flush_neighbors 參數就是用來控制這個行為的,值為 1 的時候會有上述的“連坐”機制,值為 0 時表示不找鄰居,自己刷自己的。找“鄰居”這個優化在機械硬盤時代是很有意義的,可以減少很多隨機 IO。機械硬盤的隨機 IOPS 一般只有幾百,相同的邏輯操作減少隨機 IO 就意味著系統性能的大幅度提升。而如果使用的是 SSD 這類 IOPS 比較高的設備的話,建議你把
innodb_flush_neighbors 的值設置成 0。因為這時候 IOPS 往往不是瓶頸,而“只刷自己”,就能更快地執行完必要的刷臟頁操作,減少 SQL 語句響應時間。
在 MySQL 8.0 中 ,innodb_flush_neighbors 參數的默認值已經是 0 了。

五、原作者小結:

今天這篇文章,我延續第 2 篇中介紹的 WAL 的概念,和你解釋了這個機制后續需要的刷臟頁操作和執行時機。利用 WAL 技術 (也就是先寫粉板,等不忙的時候再寫賬本) ,數據庫將隨機寫轉換成了順序寫,大大提升了數據庫的性能。
但是,由此也帶來了內存臟頁的問題。臟頁會被后臺線程自動 flush,也會由于數據頁淘汰而觸發 flush,而刷臟頁的過程由于會占用資源,可能會讓你的更新和查詢語句的響應時間長一些。在文章里,我也給你介紹了控制刷臟頁的方法和對應的監控方式。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/76553.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/76553.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/76553.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

cin,cin.get(),getchar(),getline(),cin.get line()異同點

文章目錄 1.cin2.cin.get()3.getchar()4.cin.getline()5.getline() 1.cin (1)cin>>等價于cin.operator>>(),即調用成員函數operator>>()進行讀取數據。 (2)當cin>>從緩沖區中讀取數據時&…

VMware虛擬機Ubuntu磁盤擴容

VMware中操作: 選擇要擴容的虛擬機,點擊編輯虛擬機設置 打開后點擊磁盤——>點擊擴展(注意:如果想要擴容的話需要刪除快照) 調整到你想要的容量 點擊上圖的擴展——>確定 然后我們進到虛擬機里面 首先&#…

卷積神經網絡(CNN)可視化網站匯總

深度學習 文章目錄 深度學習前言CNN ExplainerDeep Learning PlaygroundConvNetJSUnderstanding CNN with Interactive Visualizations3Blue1Brown Neural Networks YouTube PlaylistDistill.pubTensorFlow CNN Visualization (TensorBoard)NN-SVGOpenAI Microscope 前言 匯總…

實習技能記錄【3】-----Git操作

1. Git是什么 Git 是一個分布式版本控制系統 (Distributed Version Control System, DVCS)。 它的核心目標是跟蹤文件的變化,讓你能夠隨時記錄、回溯、協作和管理你的項目。 2. 介紹 2.1 Git 工作區、暫存區、本地倉庫介紹 區域名稱英文名說明相關命令工作區Wor…

藍橋杯單片機刷題——按鍵設置當前采集距離為距離參數

設計要求 驅動超聲波傳感器,啟動距離測量功能,并將其結果顯示到數碼管上。 按鍵“S5”定義為參數按鍵,按下S5按鍵,設備自動將當前采集的距離數據作為距離參數; 若測量的距離數據超過距離參數,指示燈L1點亮&#xff…

KV Cache大模型推理加速功能

KV Cache KV Cache是大模型標配的推理加速功能,也是推理過程中,顯存資源巨大開銷的元兇之一。在模型推理時,KV Cache在顯存占用量可達30%以上。 目前大部分針對KV Cache的優化工作,主要集中在工程上。比如著名的VLLM&#xff0c…

Windows 圖形顯示驅動開發-WDDM 2.0功能_上下文監視

功能概述 上下文監視機制是GPU與CPU協同計算的核心同步技術,通過受監視圍欄(Monitored Fence)實現跨硬件單元的高效協調。其核心目標是解決以下場景的同步需求: GPU引擎間同步:例如在多渲染管線中,后處理…

基于YOLOv8的機場跑道異物檢測識別系統:提升航空安全的新一代解決方案(主頁有源碼)

?個人主頁歡迎您的訪問 ?期待您的三連 ? ?個人主頁歡迎您的訪問 ?期待您的三連 ? ?個人主頁歡迎您的訪問 ?期待您的三連? ??? ????????? ?? 1. 機場跑道異物檢測領域概述 機場跑道異物(Foreign Object Debris, FOD)是指存在于機場跑道、滑行道等關…

網絡協議學習

最近在適配ESP32的網絡驅動,借此機會先學習一下網絡通信協議。 以太網幀、IP包及TCP與UDP的報文格式一文讀懂網絡報問中的檢驗和(checksum)—— 原理舉例代碼 提問騰訊元寶提示詞: TCP窗口是干什么的擁塞窗口是什么的

fit 轉 gpx

新增 fit 轉 gpx 功能 https://fittools.cc/home/fit2gpx

C++ I/O 性能優化指南

在高性能計算和大規模數據處理中,I/O 性能優化是提升系統整體效率的關鍵環節。C 作為一種高性能編程語言,提供了豐富的工具和機制來優化 I/O 操作。本文將詳細介紹在 Linux 環境下,如何通過代碼層面的優化、系統調用的選擇以及多線程技術等手…

Python中內置的數據結構類型詳析(內置數據容器)

目錄 1. 元組(Tuple)??2. 列表(List)?3. 字典(Dict)4. 集合(Set)??5. 字符串(Str)6. 隊列(Queue)與棧(Stack&#xf…

Socket多路復用網絡編程應用總結

Socket多路復用網絡編程應用總結 概述 ? 傳統I/O模型的局限性:傳統阻塞式I/O模型每次僅在一個文件描述符(File Descriptor, FD)上執行I/O操作,導致程序需等待單個操作完成,無法高效處理多連接場景(如高并…

安卓開發提示Android Gradle plugin錯誤

The project is using an incompatible version (AGP 8.9.1) of the Android Gradle plugin. Latest supported version is AGP 8.8.0-alpha05 See Android Studio & AGP compatibility options. 改模塊級 build.gradle(如果有獨立配置):…

【C++初階】--- vector容器功能模擬實現

1.什么是vector? 在 C 里,std::vector 是標準模板庫(STL)提供的一個非常實用的容器類,它可以看作是動態數組 2.成員變量 iterator _start;:指向 vector 中第一個元素的指針。 iterator _finish;&#x…

分布式鎖在秒殺場景中的Python實現與CAP權衡

目錄 一、分布式鎖的前世今生 二、秒殺系統的 “硬核” 挑戰 三、Python 實現分布式鎖的 “實戰演練” Redis 實現:快準狠 ZooKeeper 實現:穩如老狗 數據庫實現:老實本分 四、CAP 理論的 “三角戀” 五、性能優化的 “錦囊妙計” 鎖粒度控制:粗細有道 超時機制:別…

企業級開發SpringBoost玩轉Elasticsearch

案例 Spring Boot 提供了 spring-data-elasticsearch 模塊,可以方便地集成 Elasticsearch。 下面我們將詳細講解如何在 Spring Boot 中使用 Elasticsearch 8,并提供示例代碼。 1. 添加依賴: 首先,需要在 pom.xml 文件中添加 spring-data-e…

磐石云智能語音客服系統——技術革新引領服務新體驗

在人工智能技術飛速發展的今天,企業對于智能化客戶服務的需求日益增長。磐石云智能語音客服系統憑借其前沿技術架構與深度場景適配能力,正在重新定義人機交互的邊界。本文將深入解析該系統如何通過技術創新實現服務效率與體驗的雙重突破。 一、意圖識別…

OpenGL學習筆記(assimp封裝、深度測試、模板測試)

目錄 模型加載Assimp網格模型及導入 深度測試深度值精度深度緩沖的可視化深度沖突 模板測試物體輪廓 GitHub主頁:https://github.com/sdpyy1 OpenGL學習倉庫:https://github.com/sdpyy1/CppLearn/tree/main/OpenGLtree/main/OpenGL):https://github.com/sdpyy1/CppL…

通過AWS EKS 生成并部署容器化應用

今天給大家分享一個實戰例子,如何在EKS上創建容器化應用并通過ALB來發布。先介紹一下幾個基本概念: IAM, OpenID Connect (OIDC) 2014 年,AWS Identity and Access Management 增加了使用 OpenID Connect (OIDC) 的聯合身份支持。此功能允許…