系列
MySQL實(shí)戰(zhàn)45講閱讀筆記-MySQL入門
MySQL實(shí)戰(zhàn)45講閱讀筆記-日志
MySQL實(shí)戰(zhàn)45講閱讀筆記-鎖
MySQL實(shí)戰(zhàn)45講閱讀筆記-索引
MySQL實(shí)戰(zhàn)45講閱讀筆記-MVCC
MySQL的使用作為后端必須要掌握的一個(gè)技術(shù),還是需要了解下基本的原理,方便在我們工程實(shí)踐中對(duì)數(shù)據(jù)庫的運(yùn)用有更深入的了解,自己寫的SQL背后會(huì)發(fā)生什么事情恬叹,所以還是很有必要學(xué)習(xí)下基本的原理的;
當(dāng)然這幾篇是對(duì)MySQL官方文檔碟渺、網(wǎng)上博客和MySQL實(shí)戰(zhàn)45講的總結(jié)透典,沒有翻閱源碼惠勒,總結(jié)的知識(shí)點(diǎn)都是來自于別人的和別人的別人的所以可能相當(dāng)淺顯且錯(cuò)誤百出角钩,終究只是個(gè)閱讀筆記吝沫;
模塊
連接器
連接器負(fù)責(zé)跟客戶端建立連接、獲取權(quán)限递礼、維持和管理連接惨险。
在收到連接請(qǐng)求后,連接器會(huì)到權(quán)限表里面查出你擁有的權(quán)限脊髓。之后這個(gè)連接里面的權(quán)限判斷邏輯辫愉,都將依賴于此時(shí)讀到的權(quán)限。
可以通過show processlist
或者show full processlist
顯示當(dāng)前連接将硝,前者只顯示100條后者顯示全部恭朗,也可以到information_schema
的PROCESSLIST
表查看;
長(zhǎng)連接
在連接在建立之后依疼,就一直打開痰腮,后續(xù)請(qǐng)求可以重用此連接÷砂眨基本步驟是:連接→數(shù)據(jù)傳輸→維護(hù)連接→數(shù)據(jù)傳輸..→關(guān)閉連接膀值。
優(yōu)勢(shì):建立連接的過程通常是比較復(fù)雜,使用長(zhǎng)連接可以避免頻繁建立連接的開銷误辑;
劣勢(shì):維護(hù)長(zhǎng)連接是需要消耗內(nèi)存的沧踏。如果使用過多長(zhǎng)連接,會(huì)消耗大量?jī)?nèi)存資源巾钉,連接太少會(huì)影響用戶的響應(yīng)時(shí)間翘狱。短連接
執(zhí)行操作后就關(guān)閉連接∨椴裕基本就是每一次操作數(shù)據(jù)庫潦匈,都要打開和關(guān)閉數(shù)據(jù)庫連接,基本步驟是:連接→數(shù)據(jù)傳輸→關(guān)閉連接赚导。
優(yōu)勢(shì):用完即斷历等,不會(huì)占用過多內(nèi)存;適用高并發(fā)情況下的請(qǐng)求辟癌,保證效率寒屯;
劣勢(shì):建立和銷毀連接開銷大,在高并發(fā)情況下瞬時(shí)請(qǐng)求過大容易造成內(nèi)存資源緊張黍少;
不同場(chǎng)景下有不同的選擇寡夹,沒有絕對(duì)正確只有適合項(xiàng)目的,JAVA中配合JDBC的連接池一般情況下是推薦使用長(zhǎng)連接厂置;長(zhǎng)連接的連接時(shí)長(zhǎng)是通過wait_timeout
控制菩掏;
線程管理也是在這個(gè)模塊,可以使用show variables like 'thread%'
查看相關(guān)參數(shù)昵济;
-------------------------
thread_cache_size 28
thread_handling one-thread-per-connection
thread_stack 262144
-------------------------
- thread_handling表示線程配置模型
- One-thread-per-connection
每個(gè)客戶端的連接都對(duì)應(yīng)著一個(gè)線程智绸,相應(yīng)的操作也在同一個(gè)線程野揪。請(qǐng)求結(jié)束后,銷毀線程瞧栗,這種方式在高并發(fā)情況下斯稳,會(huì)導(dǎo)致線程的頻繁創(chuàng)建和釋放; - No-Threads
在主線程上面處理連接 - Pool-of-threads
使用線程池迹恐,會(huì)在服務(wù)器上維護(hù)了一個(gè)線程池挣惰,避免為每個(gè)連接都創(chuàng)建銷毀一個(gè)線程。
- thread_cache_size
服務(wù)器最大緩存的線程數(shù)以供重用殴边。當(dāng)客戶端斷開連接時(shí)憎茂,如果客戶端緩存的線程少于thread_cache_size,則將其放入緩存中 锤岸。當(dāng)客戶端有新的連接時(shí)先去從高速緩存中獲取的線程來滿足線程請(qǐng)求竖幔,并且僅當(dāng)高速緩存為空時(shí)才創(chuàng)建新線程。
默認(rèn)值:8 + (max_connections / 100)
show global status like 'thread%'
-------------------------
Threads_cached 13 當(dāng)前線程池中緩存有多少空閑線程
Threads_connected 37 當(dāng)前的線程使用數(shù)量
Threads_created 196289 已經(jīng)創(chuàng)建的線程總數(shù)
Threads_running 1 當(dāng)前激活的線程數(shù)
-------------------------
可以通過高峰期查看Threads_created
和Threads_created
調(diào)整thread_cache_size
是偷;
根據(jù)物理內(nèi)存推薦值(網(wǎng)上都這么說) 1G->8; 2G->16;3G->32; >3G->64
查詢緩存
Query Cache是用來緩存所執(zhí)行的SELECT語句以及該語句的結(jié)果集赏枚,MySql在實(shí)現(xiàn)Query Cache的具體技術(shù)細(xì)節(jié)上類似典型的KV存儲(chǔ),就是將SELECT語句和該查詢語句的結(jié)果集做了一個(gè)HASH映射并保存在一定的內(nèi)存區(qū)域中晓猛;如果緩存命中饿幅,將省略優(yōu)化器和執(zhí)行器的相關(guān)步驟,效率極高戒职;
查詢緩存的失效非常頻繁栗恩,只要有對(duì)一個(gè)表的更新,這個(gè)表上所有的查詢緩存都會(huì)被清空洪燥。從MySQL5.7.20就不被推薦使用磕秤,而在MySQL8中已經(jīng)刪除該模塊了;只有在特定的靜態(tài)表中才推薦使用捧韵;
show global status like 'Qcache%'
-------------------------
Qcache_free_blocks 1 緩存池中空閑塊的個(gè)數(shù)
Qcache_free_memory 1031832 緩存中空閑內(nèi)存量
Qcache_hits 0 緩存命中次數(shù)
Qcache_inserts 0 緩存寫入次數(shù)
Qcache_lowmem_prunes 0 因內(nèi)存不足刪除緩存次數(shù)
Qcache_not_cached 1329826476 查詢未被緩存次數(shù)
Qcache_queries_in_cache 0 當(dāng)前緩存中緩存的SQL數(shù)量
Qcache_total_blocks 1 緩存總block數(shù)
-------------------------
query_cache_type
可以設(shè)置為0(緩存禁用)或者2(通過SQL_CACHE指定需要緩存的查詢)市咆;
打開查詢緩存對(duì)讀和寫操作都會(huì)帶來額外消耗:讀查詢?cè)陂_始之前必須先檢查是否命中緩存;如果這個(gè)讀查詢可以被緩存再来,那么當(dāng)完成執(zhí)行后蒙兰,需要將結(jié)果存入緩存;每次寫入操作時(shí)芒篷,需要將對(duì)應(yīng)表的所有緩存都設(shè)置失效搜变。如果緩存較大或碎片很多,則會(huì)帶來很大消耗针炉。
優(yōu)化器
在創(chuàng)建內(nèi)部分析樹之后挠他,MySQL應(yīng)用了各種優(yōu)化技術(shù)。這些技術(shù)可以包括篡帕,重寫查詢殖侵,掃描表的順序以及選擇要使用的正確索引贸呢。對(duì)于連接查詢,MySQL優(yōu)化器調(diào)查的可能計(jì)劃數(shù)量隨著查詢中引用的表的數(shù)量呈指數(shù)增長(zhǎng)拢军。對(duì)于少量表(通常小于7到10)楞陷,這不是問題。但是朴沿,當(dāng)提交較大的查詢時(shí)猜谚,在查詢優(yōu)化中花費(fèi)的時(shí)間可能很容易成為服務(wù)器性能的主要瓶頸败砂。
更靈活的查詢優(yōu)化方法使用戶能夠控制優(yōu)化器在搜索最佳查詢?cè)u(píng)估計(jì)劃時(shí)的詳盡程度赌渣。一般的想法是,優(yōu)化器調(diào)查的計(jì)劃越少昌犹,編譯查詢所花費(fèi)的時(shí)間就越少坚芜。另一方面,由于優(yōu)化器會(huì)跳過某些計(jì)劃斜姥,因此可能無法找到最佳計(jì)劃鸿竖。
InnoDB
InnoDB是一種平衡高可靠性和高性能的通用存儲(chǔ)引擎。在MySQL 5.7中铸敏,InnoDB是默認(rèn)的MySQL存儲(chǔ)引擎缚忧。
優(yōu)勢(shì)
DML操作遵循ACID(原子性,一致性杈笔,隔離性和持久性)模型闪水,具有提交,回滾和崩潰恢復(fù)功能的事務(wù)來保護(hù)用戶數(shù)據(jù)蒙具。
行級(jí)鎖定和Oracle風(fēng)格的一致性讀取可提高多用戶并發(fā)性和性能球榆。
InnoDB表格將數(shù)據(jù)排列在磁盤上,以根據(jù)主鍵優(yōu)化查詢 禁筏。每個(gè)InnoDB表都有一個(gè)稱為聚簇索引的主鍵索引 持钉,用于組織數(shù)據(jù)以最小化主鍵查找的I / O.
InnoDB表的DML語句在事務(wù)的上下文中操作,因此它們的效果可以作為單個(gè)單元提交或回滾篱昔。
- DML
數(shù)據(jù)操縱語言每强,一組 SQL執(zhí)行語句INSERT
、UPDATE
州刽、DELETE
和SELECT ... FOR UPDATE
;
InnoDB內(nèi)存結(jié)構(gòu)
緩沖池 Buffer Pool
緩沖池是主存儲(chǔ)器中的一個(gè)區(qū)域舀射,用于在訪問時(shí)緩存表和索引數(shù)據(jù);所有的對(duì)數(shù)據(jù)庫的操作都是先對(duì)緩沖池中的操作怀伦,有個(gè)后臺(tái)線程定期修改到磁盤里面脆烟;為了提高緩存管理的效率,緩沖池是由多個(gè)數(shù)據(jù)頁(頁面可以包括一行或多行數(shù)據(jù)房待,每個(gè)頁16k默認(rèn)innodb_page_size
=16k)構(gòu)成的一個(gè)鏈表邢羔,使用LRU(least recently used)算法管理鏈表驼抹;
如果對(duì)一個(gè)數(shù)據(jù)頁進(jìn)行修改后需要讀取其內(nèi)容,則直接在內(nèi)存中讀取拜鹤,加速了讀取數(shù)據(jù)的效率框冀,通過show engine innodb status
可以直接查看Buffer Pool的命中率Buffer pool hit rate
,所謂的命中率就是訪問的數(shù)據(jù)頁在內(nèi)存中不需要到磁盤加載的敏簿;
數(shù)據(jù)頁淘汰算法-LRU
當(dāng)緩沖池中的數(shù)據(jù)頁全部被使用了明也,這時(shí)候需要從磁盤加載一個(gè)新的頁面時(shí)肯定需要淘汰掉某個(gè)頁面,
innodb buffer pool
使用的是最近最少使用(LRU)算法惯裕,同時(shí)順便對(duì)這個(gè)算法修改了一下温数;整個(gè)鏈表分為
Young
區(qū)和Old
區(qū),根據(jù)參數(shù)innodb_old_blocks_pct
來決定它們的大小蜻势,默認(rèn)值3/8為Old區(qū)撑刺,5/8為Young區(qū);當(dāng)一個(gè)新的數(shù)據(jù)頁進(jìn)來的時(shí)候握玛,默認(rèn)的位置是放在old區(qū)的
head
够傍,當(dāng)這個(gè)數(shù)據(jù)頁連續(xù)被訪問的時(shí)間超過1s,會(huì)被移動(dòng)到Y(jié)oung區(qū)的頭部挠铲,如果這個(gè)數(shù)據(jù)頁連續(xù)被訪問的時(shí)間小于1s冕屯,則位置不變,這個(gè)1s是由參數(shù)innodb_old_blocks_time
控制的拂苹,這樣做的好處是假如一個(gè)大表全表掃描需要訪問很多的數(shù)據(jù)頁安聘,但是每個(gè)數(shù)據(jù)頁訪問的時(shí)間都是很短且訪問一次后再次訪問的可能性很小,所以放在Old區(qū)能夠更快的淘汰掉釋放空間醋寝,不至于讓Buffer Pool
中常被訪問的熱點(diǎn)數(shù)據(jù)頁被意外淘汰搞挣,能有效提升緩沖池的命中率;
- InnoDB緩沖池里面有什么音羞?
數(shù)據(jù)緩存 - InnoDB數(shù)據(jù)頁面囱桨;
索引緩存 - 索引數(shù)據(jù);
緩沖數(shù)據(jù) - 臟頁: 在內(nèi)存中修改但尚未刷新(寫入)到數(shù)據(jù)磁盤的數(shù)據(jù)嗅绰;
內(nèi)部結(jié)構(gòu) - InnoDB緩沖池還存儲(chǔ)內(nèi)部結(jié)構(gòu)舍肠,如自適應(yīng)哈希索引,行級(jí)鎖等窘面;
相關(guān)配置:
show global status like 'innodb_buffer_pool_read%'
------------------------------------------------
Innodb_buffer_pool_read_requests 57119197605 表示從內(nèi)存中讀取邏輯的請(qǐng)求數(shù)
Innodb_buffer_pool_reads 471363395 表示InnoDB緩沖池?zé)o法滿足的請(qǐng)求數(shù)翠语;需要從磁盤中讀取
------------------------------------------------
show global status like 'innodb_buffer_pool_page%'
------------------------------------------------
Innodb_buffer_pool_pages_data 8183 顯示臟和干凈的數(shù)據(jù)和索引頁面的數(shù)量
Innodb_buffer_pool_pages_dirty 26 顯示在內(nèi)存中修改但尚未寫入數(shù)據(jù)文件的InnoDB緩沖池?cái)?shù)據(jù)頁數(shù)
Innodb_buffer_pool_pages_flushed 2729397 表示從InnoDB緩沖池中刷新臟頁的請(qǐng)求數(shù)
Innodb_buffer_pool_pages_free 0 顯示InnoDB緩沖池中的空閑頁面
Innodb_buffer_pool_pages_misc 8 顯示繁忙的頁面數(shù)
Innodb_buffer_pool_pages_total 8191
------------------------------------------------
推薦的計(jì)算公式 Performance = innodb_buffer_pool_reads / innodb_buffer_pool_read_requests * 100 ,值當(dāng)然是越小越好;
相關(guān)參數(shù):
-
innodb_buffer_pool_size
直接決定了緩沖池的大小财边,非常重要的配置項(xiàng)肌括,MySQL5.7以后可以通過
set global innodb_buffer_pool_size=xx
動(dòng)態(tài)配置,在專用數(shù)據(jù)庫服務(wù)器上面可以設(shè)置成最大內(nèi)存的50-80%酣难;
show status where variable_name='InnoDB_buffer_pool_resize_status';
可以監(jiān)控在線緩沖池調(diào)整進(jìn)度谍夭,也可以在mysql錯(cuò)誤日志中查看黑滴;
-
innodb_buffer_pool_instances
buffer pool將劃分為多個(gè)實(shí)例以提高系統(tǒng)并發(fā)性, 減少線程間讀寫緩存的爭(zhēng)用。當(dāng)innodb_buffer_pool_size
大于 1GB 時(shí),innodb_buffer_pool_instances
默認(rèn)為 8紧索;
Change Buffer
當(dāng)對(duì)二級(jí)索引進(jìn)行insert袁辈、update、delete時(shí)珠漂,如果目標(biāo)數(shù)據(jù)頁在內(nèi)存則直接對(duì)內(nèi)存進(jìn)行操作晚缩,但是如果數(shù)據(jù)頁不在內(nèi)存時(shí),需要把數(shù)據(jù)頁從磁盤讀入內(nèi)存里面媳危,ChangeBuffer的作用是緩存對(duì)二級(jí)索引的數(shù)據(jù)操作(主鍵索引是用不上的)荞彼,并且在數(shù)據(jù)頁被加載進(jìn)buffer pool時(shí)將change buffer中的操作合并,這樣一來可以避免很多對(duì)磁盤的隨機(jī)IO济舆;另外ChangeBuffer是可以持久化的卿泽,意味著數(shù)據(jù)是會(huì)被寫在磁盤中莺债,實(shí)際上ChangeBuffer是儲(chǔ)存在ibdata1(系統(tǒng)表空間)里面的滋觉;
磁盤一個(gè)I/O請(qǐng)求所花費(fèi)的時(shí)間=尋道時(shí)間+旋轉(zhuǎn)延遲+數(shù)據(jù)傳輸時(shí)間;
順序IO是指讀取和寫入操作基于邏輯塊逐個(gè)連續(xù)訪問來自相鄰地址的數(shù)據(jù)齐邦。在順序IO訪問中椎侠,磁盤所需的磁道搜索時(shí)間顯著減少,因?yàn)樽x/寫磁頭可以以最小的移動(dòng)訪問下一個(gè)塊措拇。
隨機(jī)IO是指讀寫操作時(shí)間連續(xù)我纪,但訪問地址不連續(xù),磁頭在兩次IO操作之間需要作比較大的移動(dòng)動(dòng)作才能重新開始讀/寫數(shù)據(jù)丐吓。
SELECT (SELECT COUNT(*) FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE
WHERE PAGE_TYPE LIKE 'IBUF%') AS change_buffer_pages,
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE) AS total_pages,
(SELECT ((change_buffer_pages/total_pages)*100))
AS change_buffer_page_percentage;
-------------------------------------
change_buffer_pages 44
total_pages 8191
change_buffer_page_percentage 0.5372
-------------------------------------
Change Buffer使用的是Innodb buffer pool
里面的內(nèi)存浅悉,所以最大值為buffer pool
的大小,使用參數(shù)innodb_change_buffer_max_size
來控制
show variables like 'innodb_change%'
-------------------------------------
innodb_change_buffer_max_size 25
innodb_change_buffering all
-------------------------------------
innodb_change_buffer_max_size
25表示max size為innodb buffer pool的25%
innodb_change_buffering
- none:不使用Change Buffer
- inserts:在insert操作的時(shí)候使用
- deletes:在delete和update操作的時(shí)候使用
- changes:包括inserts和deletes
- purges:在后臺(tái)使用purges的時(shí)候使用
- all:包括inserts券犁、deletes术健、changes、purges
日志緩存區(qū) log buffer
log buffer儲(chǔ)存要寫入磁盤上日志文件的數(shù)據(jù)的內(nèi)存區(qū)域粘衬;日志緩沖區(qū)大小由innodb_log_buffer_size
變量定義 荞估;默認(rèn)大小為16MB。日志緩沖區(qū)的內(nèi)容會(huì)定期刷新到磁盤稚新。大的日志緩沖區(qū)使大的事務(wù)能夠運(yùn)行勘伺,而無需在事務(wù)提交之前將重做日志數(shù)據(jù)寫入磁盤。因此如果有大量更新褂删,插入或刪除的事務(wù)飞醉,增加日志緩沖區(qū)的大小可以節(jié)省磁盤I / O。
相關(guān)配置:
-
innodb_flush_log_at_trx_commit
- 0:每秒寫入日志并將其刷新到磁盤一次屯阀;未刷新日志的事務(wù)可能會(huì)在崩潰中丟失缅帘。
- 1:在每次事務(wù)提交時(shí)寫入日志并刷新到磁盤
- 2:在每次事務(wù)提交后寫入日志噪裕,并每秒刷新一次磁盤;未刷新日志的事務(wù)可能會(huì)在崩潰中丟失
InnoDB磁盤結(jié)構(gòu)
Innodb是按照表空間(Tablespace)的結(jié)構(gòu)進(jìn)行儲(chǔ)存的股毫;
共享表空間(System Tablespace)
Innodb的所有數(shù)據(jù)保存在一個(gè)單獨(dú)的表空間里面膳音,而這個(gè)表空間可以由很多個(gè)文件組成,一個(gè)表可以跨多個(gè)文件存在铃诬,所以其大小限制不再是文件大小的限制祭陷,而是其自身的限制。從Innodb的官方文檔中可以看到趣席,其表空間的最大限制為64TB兵志;
可以在配置文件中找到datadir
所配置的路徑,默認(rèn)的共享表空間文件名叫ibdata1
;
獨(dú)立表空間(File-Per-Table Tablespace)
每個(gè)InnoDB表都存儲(chǔ)在自己獨(dú)立的表空間數(shù)據(jù)文件宣肚。.ibd文件儲(chǔ)存數(shù)據(jù)和索引想罕,.frm儲(chǔ)存表結(jié)構(gòu)數(shù)據(jù);
不同處:
truncate或者drop儲(chǔ)存在獨(dú)立表空間的表時(shí)能回收磁盤空間霉涨,而truncate或者drop儲(chǔ)存在系統(tǒng)表空間的表時(shí)會(huì)在ibdata文件內(nèi)部標(biāo)記可用空間按价,并不會(huì)縮小文件大小笙瑟;
獨(dú)立表空間可以使用optimize table
來壓縮或重新創(chuàng)建表文件楼镐;當(dāng)運(yùn)行這條命令時(shí),InnoDB
創(chuàng)建一個(gè)新的.ibd
具有臨時(shí)名稱的文件往枷,該文件存儲(chǔ)的實(shí)際數(shù)據(jù)是該表所需要的空間框产。優(yōu)化完成后,InnoDB
刪除舊.ibd
文件并將其替換為新文件错洁。
雙寫緩存區(qū)(Doublewrite Buffer)
首先需要理解一些概念秉宿,磁盤物理操作的基本單位是扇區(qū),是真實(shí)存在的物理單位屯碴,在linux下使用fdisk -l
命令可以了解該磁盤的基本信息比如
操作系統(tǒng)與磁盤之間數(shù)據(jù)交流的最小單位是磁盤塊描睦,是操作系統(tǒng)抽象出來的一個(gè)概念;linux內(nèi)核要求 block size = sector size * 2^n窿锉,即是扇區(qū)的整數(shù)倍酌摇;linux可以通過
tune2fs -l /dev/vda1|grep Block size
查看block size大小(一般是Block size: 4096
),操作系統(tǒng)是以頁管理的嗡载,一般大小為4k;
而在Innodb里面窑多,page是存儲(chǔ)的最基本結(jié)構(gòu),也是對(duì)磁盤管理的最小單位洼滚;可以使用show variables like 'innodb_page_size'
查看埂息,一般為16k;
任何數(shù)據(jù)庫IO操作最終都是體現(xiàn)在對(duì)扇區(qū)的IO操作上面,比如現(xiàn)在需要對(duì)一頁(16k)的數(shù)據(jù)寫入磁盤千康,因?yàn)榇疟P扇區(qū)的大小為512bytes享幽,所以是需要對(duì)多個(gè)扇區(qū)進(jìn)行操作,當(dāng)這個(gè)操作進(jìn)行到一半時(shí)發(fā)生數(shù)據(jù)庫宕機(jī)或者掉電拾弃,就會(huì)出現(xiàn)數(shù)據(jù)頁只有部分寫入磁盤值桩,這種情況叫做頁斷裂(partial write)
;
Doublewrite buffer
是Innodb表空間內(nèi)部分配的一片緩沖區(qū)且與數(shù)據(jù)頁一樣有物理存儲(chǔ)空間,儲(chǔ)存在共享表空間中豪椿。當(dāng)臟頁的數(shù)據(jù)flush到磁盤時(shí)先將數(shù)據(jù)寫入doublewrite buffer中奔坟,然后在fsync到磁盤上,如果期間發(fā)生掉電并且導(dǎo)致page數(shù)據(jù)損壞搭盾,可以通過buffer內(nèi)的數(shù)據(jù)進(jìn)行恢復(fù)咳秉;因?yàn)閷懭雂oublewrite buffer和數(shù)據(jù)頁落盤的時(shí)間點(diǎn)是不一樣的所以不會(huì)出現(xiàn)兩個(gè)都是損壞的情況;
當(dāng)然雙寫會(huì)帶來一定的消耗鸯隅,但是不是兩倍于直接落盤澜建,因?yàn)閿?shù)據(jù)頁的數(shù)據(jù)是做為一個(gè)大的連續(xù)的塊(chunk)順序?qū)懭腚p寫緩存區(qū)中,只需要一次fsync蝌以;
重做日志(Redo Log)
InnoDB記錄了對(duì)數(shù)據(jù)文件的物理更改炕舵,并保證總是日志先行,也就是所謂的WAL(Write-Ahead Logging)饼灿,即在持久化數(shù)據(jù)文件前幕侠,保證之前的redo日志已經(jīng)寫到磁盤帝美。redo log默認(rèn)在磁盤上由兩個(gè)名為ib_logfile0和ib_logfile1(默認(rèn)是兩個(gè))的文件物理表示碍彭,MySQL以循環(huán)方式寫入這兩個(gè)文件;
- 為什么需要redo log
當(dāng)修改innodb表上某行數(shù)據(jù)時(shí)悼潭,如果該行不在內(nèi)存中則需要將該數(shù)據(jù)頁從磁盤讀入到內(nèi)存去然后在內(nèi)存中更新該行庇忌,現(xiàn)在內(nèi)存中的數(shù)據(jù)頁與磁盤中就不一致了,把這種內(nèi)存數(shù)據(jù)頁與磁盤數(shù)據(jù)頁不一致的數(shù)據(jù)頁稱為臟頁(dirty page)舰褪,DB需要把臟頁數(shù)據(jù)寫入磁盤皆疹,但是如果每一次更新數(shù)據(jù)就會(huì)帶來一次磁盤操作的話那么機(jī)器肯定撐不住占拍;所以說mysql會(huì)把標(biāo)記為臟頁的數(shù)據(jù)頁儲(chǔ)存在一個(gè)flush list里面略就,用一個(gè)專門的后臺(tái)線程定時(shí)刷臟;那么如果在mysql還沒有刷臟的時(shí)候數(shù)據(jù)庫掛了怎么辦呢晃酒,所以就需要redo log表牢; - 儲(chǔ)存著什么
由日志緩存區(qū)(redo log buffer)和日志文件(redo log file)構(gòu)成,redo log
是物理日志贝次,保存的是數(shù)據(jù)頁上被修改的值崔兴;還有一種是邏輯日志,保存的是SQL語句比如binlog;
一開始是存在于
redolog buffer
敲茄,這部分是屬于mysql進(jìn)程內(nèi)存中的位谋,所以寫這部分的效率是很高的;在一個(gè)事務(wù)的更新過程中redolog可能是要寫多次的堰燎,所以在commit
前日志是保存在redolog buffer中掏父,等待commit時(shí)在寫到redolog file
中;
其實(shí)寫redolog file
也不是直接寫到磁盤秆剪,是寫到文件系統(tǒng)的page cahce里面损同,最后才是持久化到磁盤,fsync一般來說是IO性能的瓶頸鸟款;
page cache用來緩存文件數(shù)據(jù)膏燃,是屬于操作系統(tǒng)的緩存,如果想要持久化到磁盤中需要使用fsync何什;
除了后臺(tái)線程會(huì)每秒刷一次之外组哩,還有兩種情況會(huì)主動(dòng)寫盤;
-
redo log buffer
即將占用innodb_log_buffer_size
的一半的時(shí)候处渣,后臺(tái)線程會(huì)主動(dòng)寫盤伶贰,但是也只是寫入到page cache里面并沒有使用fsync; - 當(dāng)
innodb_flush_log_at_trx_commit
是1的時(shí)候罐栈,如果并行的事務(wù)提交黍衙,那么會(huì)將redo log buffer的所有日志(包括其他線程只寫到一半的日志)都會(huì)持久化到磁盤,因?yàn)閞edo log buffer是共享的荠诬;
show variables like 'innodb_log%'
-------------------------------------
innodb_log_buffer_size 16777216 寫入日志文件緩沖區(qū)的字節(jié)
innodb_log_file_size 50331648 ib_logfile文件的大小
innodb_log_files_in_group 2 控制日志文件數(shù)
-------------------------------------
參考
How to allocate innodb_buffer_pool_size in MySQL?
頁斷裂(partial write)與doublewrite技術(shù)
IO系統(tǒng)性能之一:衡量性能的幾個(gè)指標(biāo)
磁盤I/O那些事
MySQL · 引擎特性 · InnoDB redo log漫游
詳細(xì)分析MySQL事務(wù)日志(redo log和undo log)
MySQL · 引擎特性 · Innodb change buffer介紹