MySQL實(shí)戰(zhàn)45講閱讀筆記-MySQL入門

系列
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ù)庫(kù)的運(yùn)用有更深入的了解,自己寫(xiě)的SQL背后會(huì)發(fā)生什么事情,所以還是很有必要學(xué)習(xí)下基本的原理的;


當(dāng)然這幾篇是對(duì)MySQL官方文檔、網(wǎng)上博客和MySQL實(shí)戰(zhàn)45講的總結(jié),沒(méi)有翻閱源碼,總結(jié)的知識(shí)點(diǎn)都是來(lái)自于別人的和別人的別人的所以可能相當(dāng)淺顯且錯(cuò)誤百出,終究只是個(gè)閱讀筆記;

模塊

連接器

連接器負(fù)責(zé)跟客戶端建立連接、獲取權(quán)限、維持和管理連接。
在收到連接請(qǐng)求后,連接器會(huì)到權(quán)限表里面查出你擁有的權(quán)限。之后這個(gè)連接里面的權(quán)限判斷邏輯,都將依賴于此時(shí)讀到的權(quán)限。

可以通過(guò)show processlist或者show full processlist顯示當(dāng)前連接,前者只顯示100條后者顯示全部,也可以到information_schemaPROCESSLIST表查看;

  • 長(zhǎng)連接
    在連接在建立之后,就一直打開(kāi),后續(xù)請(qǐng)求可以重用此連接?;静襟E是:連接→數(shù)據(jù)傳輸→維護(hù)連接→數(shù)據(jù)傳輸..→關(guān)閉連接。
    優(yōu)勢(shì):建立連接的過(guò)程通常是比較復(fù)雜,使用長(zhǎng)連接可以避免頻繁建立連接的開(kāi)銷;
    劣勢(shì):維護(hù)長(zhǎng)連接是需要消耗內(nèi)存的。如果使用過(guò)多長(zhǎng)連接,會(huì)消耗大量?jī)?nèi)存資源,連接太少會(huì)影響用戶的響應(yīng)時(shí)間。

  • 短連接
    執(zhí)行操作后就關(guān)閉連接?;揪褪敲恳淮尾僮鲾?shù)據(jù)庫(kù),都要打開(kāi)和關(guān)閉數(shù)據(jù)庫(kù)連接,基本步驟是:連接→數(shù)據(jù)傳輸→關(guān)閉連接。
    優(yōu)勢(shì):用完即斷,不會(huì)占用過(guò)多內(nèi)存;適用高并發(fā)情況下的請(qǐng)求,保證效率;
    劣勢(shì):建立和銷毀連接開(kāi)銷大,在高并發(fā)情況下瞬時(shí)請(qǐng)求過(guò)大容易造成內(nèi)存資源緊張;

不同場(chǎng)景下有不同的選擇,沒(méi)有絕對(duì)正確只有適合項(xiàng)目的,JAVA中配合JDBC的連接池一般情況下是推薦使用長(zhǎng)連接;長(zhǎng)連接的連接時(shí)長(zhǎng)是通過(guò)wait_timeout控制;

線程管理也是在這個(gè)模塊,可以使用show variables like 'thread%'查看相關(guān)參數(shù);

-------------------------
thread_cache_size      28
thread_handling        one-thread-per-connection
thread_stack           262144
-------------------------
  1. 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è)線程。
  1. thread_cache_size
    服務(wù)器最大緩存的線程數(shù)以供重用。當(dāng)客戶端斷開(kāi)連接時(shí),如果客戶端緩存的線程少于thread_cache_size,則將其放入緩存中 。當(dāng)客戶端有新的連接時(shí)先去從高速緩存中獲取的線程來(lái)滿足線程請(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ù)
-------------------------

可以通過(guò)高峰期查看Threads_createdThreads_created調(diào)整thread_cache_size;
根據(jù)物理內(nèi)存推薦值(網(wǎng)上都這么說(shuō)) 1G->8; 2G->16;3G->32; >3G->64

參考來(lái)自MySQL官方文檔

查詢緩存

Query Cache是用來(lái)緩存所執(zhí)行的SELECT語(yǔ)句以及該語(yǔ)句的結(jié)果集,MySql在實(shí)現(xiàn)Query Cache的具體技術(shù)細(xì)節(jié)上類似典型的KV存儲(chǔ),就是將SELECT語(yǔ)句和該查詢語(yǔ)句的結(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    緩存寫(xiě)入次數(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(通過(guò)SQL_CACHE指定需要緩存的查詢);
打開(kāi)查詢緩存對(duì)讀和寫(xiě)操作都會(huì)帶來(lái)額外消耗:讀查詢?cè)陂_(kāi)始之前必須先檢查是否命中緩存;如果這個(gè)讀查詢可以被緩存,那么當(dāng)完成執(zhí)行后,需要將結(jié)果存入緩存;每次寫(xiě)入操作時(shí),需要將對(duì)應(yīng)表的所有緩存都設(shè)置失效。如果緩存較大或碎片很多,則會(huì)帶來(lái)很大消耗。

優(yōu)化器

在創(chuàng)建內(nèi)部分析樹(shù)之后,MySQL應(yīng)用了各種優(yōu)化技術(shù)。這些技術(shù)可以包括,重寫(xiě)查詢,掃描表的順序以及選擇要使用的正確索引。對(duì)于連接查詢,MySQL優(yōu)化器調(diào)查的可能計(jì)劃數(shù)量隨著查詢中引用的表的數(shù)量呈指數(shù)增長(zhǎng)。對(duì)于少量表(通常小于7到10),這不是問(wèn)題。但是,當(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ì)跳過(guò)某些計(jì)劃,因此可能無(wú)法找到最佳計(jì)劃。

查詢語(yǔ)句更新流程

InnoDB

InnoDB是一種平衡高可靠性和高性能的通用存儲(chǔ)引擎。在MySQL 5.7中,InnoDB是默認(rèn)的MySQL存儲(chǔ)引擎。
優(yōu)勢(shì)
DML操作遵循ACID(原子性,一致性,隔離性和持久性)模型,具有提交,回滾和崩潰恢復(fù)功能的事務(wù)來(lái)保護(hù)用戶數(shù)據(jù)。
行級(jí)鎖定和Oracle風(fēng)格的一致性讀取可提高多用戶并發(fā)性和性能。
InnoDB表格將數(shù)據(jù)排列在磁盤上,以根據(jù)主鍵優(yōu)化查詢 。每個(gè)InnoDB表都有一個(gè)稱為聚簇索引的主鍵索引 ,用于組織數(shù)據(jù)以最小化主鍵查找的I / O.
InnoDB表的DML語(yǔ)句在事務(wù)的上下文中操作,因此它們的效果可以作為單個(gè)單元提交回滾。

  • DML
    數(shù)據(jù)操縱語(yǔ)言,一組 SQL執(zhí)行語(yǔ)句 INSERT、UPDATEDELETESELECT ... FOR UPDATE;
innodb架構(gòu)
InnoDB內(nèi)存結(jié)構(gòu)
緩沖池 Buffer Pool

緩沖池是主存儲(chǔ)器中的一個(gè)區(qū)域,用于在訪問(wèn)時(shí)緩存表和索引數(shù)據(jù);所有的對(duì)數(shù)據(jù)庫(kù)的操作都是先對(duì)緩沖池中的操作,有個(gè)后臺(tái)線程定期修改到磁盤里面;為了提高緩存管理的效率,緩沖池是由多個(gè)數(shù)據(jù)頁(yè)(頁(yè)面可以包括一行或多行數(shù)據(jù),每個(gè)頁(yè)16k默認(rèn)innodb_page_size=16k)構(gòu)成的一個(gè)鏈表,使用LRU(least recently used)算法管理鏈表;

如果對(duì)一個(gè)數(shù)據(jù)頁(yè)進(jìn)行修改后需要讀取其內(nèi)容,則直接在內(nèi)存中讀取,加速了讀取數(shù)據(jù)的效率,通過(guò)show engine innodb status可以直接查看Buffer Pool的命中率Buffer pool hit rate,所謂的命中率就是訪問(wèn)的數(shù)據(jù)頁(yè)在內(nèi)存中不需要到磁盤加載的;

數(shù)據(jù)頁(yè)淘汰算法-LRU


當(dāng)緩沖池中的數(shù)據(jù)頁(yè)全部被使用了,這時(shí)候需要從磁盤加載一個(gè)新的頁(yè)面時(shí)肯定需要淘汰掉某個(gè)頁(yè)面,innodb buffer pool使用的是最近最少使用(LRU)算法,同時(shí)順便對(duì)這個(gè)算法修改了一下;
整個(gè)鏈表分為Young區(qū)和Old區(qū),根據(jù)參數(shù)innodb_old_blocks_pct來(lái)決定它們的大小,默認(rèn)值3/8為Old區(qū),5/8為Young區(qū);
當(dāng)一個(gè)新的數(shù)據(jù)頁(yè)進(jìn)來(lái)的時(shí)候,默認(rèn)的位置是放在old區(qū)的head,當(dāng)這個(gè)數(shù)據(jù)頁(yè)連續(xù)被訪問(wèn)的時(shí)間超過(guò)1s,會(huì)被移動(dòng)到Y(jié)oung區(qū)的頭部,如果這個(gè)數(shù)據(jù)頁(yè)連續(xù)被訪問(wèn)的時(shí)間小于1s,則位置不變,這個(gè)1s是由參數(shù)innodb_old_blocks_time控制的,這樣做的好處是假如一個(gè)大表全表掃描需要訪問(wèn)很多的數(shù)據(jù)頁(yè),但是每個(gè)數(shù)據(jù)頁(yè)訪問(wèn)的時(shí)間都是很短且訪問(wèn)一次后再次訪問(wèn)的可能性很小,所以放在Old區(qū)能夠更快的淘汰掉釋放空間,不至于讓Buffer Pool中常被訪問(wèn)的熱點(diǎn)數(shù)據(jù)頁(yè)被意外淘汰,能有效提升緩沖池的命中率;

  • InnoDB緩沖池里面有什么?
    數(shù)據(jù)緩存 - InnoDB數(shù)據(jù)頁(yè)面;
    索引緩存 - 索引數(shù)據(jù);
    緩沖數(shù)據(jù) - 臟頁(yè): 在內(nèi)存中修改但尚未刷新(寫(xiě)入)到數(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ù)和索引頁(yè)面的數(shù)量
Innodb_buffer_pool_pages_dirty           26 顯示在內(nèi)存中修改但尚未寫(xiě)入數(shù)據(jù)文件的InnoDB緩沖池?cái)?shù)據(jù)頁(yè)數(shù)
Innodb_buffer_pool_pages_flushed    2729397 表示從InnoDB緩沖池中刷新臟頁(yè)的請(qǐng)求數(shù)
Innodb_buffer_pool_pages_free             0 顯示InnoDB緩沖池中的空閑頁(yè)面
Innodb_buffer_pool_pages_misc             8 顯示繁忙的頁(yè)面數(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以后可以通過(guò)
    set global innodb_buffer_pool_size=xx動(dòng)態(tài)配置,在專用數(shù)據(jù)庫(kù)服務(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ā)性, 減少線程間讀寫(xiě)緩存的爭(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ù)頁(yè)在內(nèi)存則直接對(duì)內(nèi)存進(jìn)行操作,但是如果數(shù)據(jù)頁(yè)不在內(nèi)存時(shí),需要把數(shù)據(jù)頁(yè)從磁盤讀入內(nèi)存里面,ChangeBuffer的作用是緩存對(duì)二級(jí)索引的數(shù)據(jù)操作(主鍵索引是用不上的),并且在數(shù)據(jù)頁(yè)被加載進(jìn)buffer pool時(shí)將change buffer中的操作合并,這樣一來(lái)可以避免很多對(duì)磁盤的隨機(jī)IO;另外ChangeBuffer是可以持久化的,意味著數(shù)據(jù)是會(huì)被寫(xiě)在磁盤中,實(shí)際上ChangeBuffer是儲(chǔ)存在ibdata1(系統(tǒng)表空間)里面的;

磁盤一個(gè)I/O請(qǐng)求所花費(fèi)的時(shí)間=尋道時(shí)間+旋轉(zhuǎn)延遲+數(shù)據(jù)傳輸時(shí)間;
順序IO是指讀取和寫(xiě)入操作基于邏輯塊逐個(gè)連續(xù)訪問(wèn)來(lái)自相鄰地址的數(shù)據(jù)。在順序IO訪問(wèn)中,磁盤所需的磁道搜索時(shí)間顯著減少,因?yàn)樽x/寫(xiě)磁頭可以以最小的移動(dòng)訪問(wèn)下一個(gè)塊。
隨機(jī)IO是指讀寫(xiě)操作時(shí)間連續(xù),但訪問(wèn)地址不連續(xù),磁頭在兩次IO操作之間需要作比較大的移動(dòng)動(dòng)作才能重新開(kāi)始讀/寫(xiě)數(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來(lái)控制

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ǔ)存要寫(xiě)入磁盤上日志文件的數(shù)據(jù)的內(nèi)存區(qū)域;日志緩沖區(qū)大小由innodb_log_buffer_size變量定義 ;默認(rèn)大小為16MB。日志緩沖區(qū)的內(nèi)容會(huì)定期刷新到磁盤。大的日志緩沖區(qū)使大的事務(wù)能夠運(yùn)行,而無(wú)需在事務(wù)提交之前將重做日志數(shù)據(jù)寫(xiě)入磁盤。因此如果有大量更新,插入或刪除的事務(wù),增加日志緩沖區(qū)的大小可以節(jié)省磁盤I / O。
相關(guān)配置:

  • innodb_flush_log_at_trx_commit
    • 0:每秒寫(xiě)入日志并將其刷新到磁盤一次;未刷新日志的事務(wù)可能會(huì)在崩潰中丟失。
    • 1:在每次事務(wù)提交時(shí)寫(xiě)入日志并刷新到磁盤
    • 2:在每次事務(wù)提交后寫(xiě)入日志,并每秒刷新一次磁盤;未刷新日志的事務(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來(lái)壓縮或重新創(chuàng)建表文件;當(dāng)運(yùn)行這條命令時(shí), InnoDB創(chuàng)建一個(gè)新的 .ibd具有臨時(shí)名稱的文件,該文件存儲(chǔ)的實(shí)際數(shù)據(jù)是該表所需要的空間。優(yōu)化完成后,InnoDB刪除舊.ibd文件并將其替換為新文件。

雙寫(xiě)緩存區(qū)(Doublewrite Buffer)

首先需要理解一些概念,磁盤物理操作的基本單位是扇區(qū),是真實(shí)存在的物理單位,在linux下使用fdisk -l命令可以了解該磁盤的基本信息比如

每個(gè)扇區(qū)大小為512bytes

操作系統(tǒng)與磁盤之間數(shù)據(jù)交流的最小單位是磁盤塊,是操作系統(tǒng)抽象出來(lái)的一個(gè)概念;linux內(nèi)核要求 block size = sector size * 2^n,即是扇區(qū)的整數(shù)倍;linux可以通過(guò)tune2fs -l /dev/vda1|grep Block size查看block size大小(一般是Block size: 4096),操作系統(tǒng)是以頁(yè)管理的,一般大小為4k;

而在Innodb里面,page是存儲(chǔ)的最基本結(jié)構(gòu),也是對(duì)磁盤管理的最小單位;可以使用show variables like 'innodb_page_size' 查看,一般為16k;
任何數(shù)據(jù)庫(kù)IO操作最終都是體現(xiàn)在對(duì)扇區(qū)的IO操作上面,比如現(xiàn)在需要對(duì)一頁(yè)(16k)的數(shù)據(jù)寫(xiě)入磁盤,因?yàn)榇疟P扇區(qū)的大小為512bytes,所以是需要對(duì)多個(gè)扇區(qū)進(jìn)行操作,當(dāng)這個(gè)操作進(jìn)行到一半時(shí)發(fā)生數(shù)據(jù)庫(kù)宕機(jī)或者掉電,就會(huì)出現(xiàn)數(shù)據(jù)頁(yè)只有部分寫(xiě)入磁盤,這種情況叫做頁(yè)斷裂(partial write);

Doublewrite buffer是Innodb表空間內(nèi)部分配的一片緩沖區(qū)且與數(shù)據(jù)頁(yè)一樣有物理存儲(chǔ)空間,儲(chǔ)存在共享表空間中。當(dāng)臟頁(yè)的數(shù)據(jù)flush到磁盤時(shí)先將數(shù)據(jù)寫(xiě)入doublewrite buffer中,然后在fsync到磁盤上,如果期間發(fā)生掉電并且導(dǎo)致page數(shù)據(jù)損壞,可以通過(guò)buffer內(nèi)的數(shù)據(jù)進(jìn)行恢復(fù);因?yàn)閷?xiě)入doublewrite buffer和數(shù)據(jù)頁(yè)落盤的時(shí)間點(diǎn)是不一樣的所以不會(huì)出現(xiàn)兩個(gè)都是損壞的情況;

當(dāng)然雙寫(xiě)會(huì)帶來(lái)一定的消耗,但是不是兩倍于直接落盤,因?yàn)閿?shù)據(jù)頁(yè)的數(shù)據(jù)是做為一個(gè)大的連續(xù)的塊(chunk)順序?qū)懭腚p寫(xiě)緩存區(qū)中,只需要一次fsync;

重做日志(Redo Log)

InnoDB記錄了對(duì)數(shù)據(jù)文件的物理更改,并保證總是日志先行,也就是所謂的WAL(Write-Ahead Logging),即在持久化數(shù)據(jù)文件前,保證之前的redo日志已經(jīng)寫(xiě)到磁盤。redo log默認(rèn)在磁盤上由兩個(gè)名為ib_logfile0ib_logfile1(默認(rèn)是兩個(gè))的文件物理表示,MySQL以循環(huán)方式寫(xiě)入這兩個(gè)文件;

  • 為什么需要redo log
    當(dāng)修改innodb表上某行數(shù)據(jù)時(shí),如果該行不在內(nèi)存中則需要將該數(shù)據(jù)頁(yè)從磁盤讀入到內(nèi)存去然后在內(nèi)存中更新該行,現(xiàn)在內(nèi)存中的數(shù)據(jù)頁(yè)與磁盤中就不一致了,把這種內(nèi)存數(shù)據(jù)頁(yè)與磁盤數(shù)據(jù)頁(yè)不一致的數(shù)據(jù)頁(yè)稱為臟頁(yè)(dirty page),DB需要把臟頁(yè)數(shù)據(jù)寫(xiě)入磁盤,但是如果每一次更新數(shù)據(jù)就會(huì)帶來(lái)一次磁盤操作的話那么機(jī)器肯定撐不??;所以說(shuō)mysql會(huì)把標(biāo)記為臟頁(yè)的數(shù)據(jù)頁(yè)儲(chǔ)存在一個(gè)flush list里面,用一個(gè)專門的后臺(tái)線程定時(shí)刷臟;那么如果在mysql還沒(méi)有刷臟的時(shí)候數(shù)據(jù)庫(kù)掛了怎么辦呢,所以就需要redo log;
  • 儲(chǔ)存著什么
    日志緩存區(qū)(redo log buffer)日志文件(redo log file)構(gòu)成,redo log是物理日志,保存的是數(shù)據(jù)頁(yè)上被修改的值;還有一種是邏輯日志,保存的是SQL語(yǔ)句比如binlog;

redo log的寫(xiě)入過(guò)程

一開(kāi)始是存在于redolog buffer,這部分是屬于mysql進(jìn)程內(nèi)存中的,所以寫(xiě)這部分的效率是很高的;在一個(gè)事務(wù)的更新過(guò)程中redolog可能是要寫(xiě)多次的,所以在commit前日志是保存在redolog buffer中,等待commit時(shí)在寫(xiě)到redolog file中;

其實(shí)寫(xiě)redolog file也不是直接寫(xiě)到磁盤,是寫(xiě)到文件系統(tǒng)的page cahce里面,最后才是持久化到磁盤,fsync一般來(lái)說(shuō)是IO性能的瓶頸;

page cache用來(lái)緩存文件數(shù)據(jù),是屬于操作系統(tǒng)的緩存,如果想要持久化到磁盤中需要使用fsync;


除了后臺(tái)線程會(huì)每秒刷一次之外,還有兩種情況會(huì)主動(dòng)寫(xiě)盤;

  • redo log buffer 即將占用innodb_log_buffer_size的一半的時(shí)候,后臺(tái)線程會(huì)主動(dòng)寫(xiě)盤,但是也只是寫(xiě)入到page cache里面并沒(méi)有使用fsync;
  • 當(dāng)innodb_flush_log_at_trx_commit是1的時(shí)候,如果并行的事務(wù)提交,那么會(huì)將redo log buffer的所有日志(包括其他線程只寫(xiě)到一半的日志)都會(huì)持久化到磁盤,因?yàn)閞edo log buffer是共享的;
show variables like 'innodb_log%'
-------------------------------------
innodb_log_buffer_size  16777216  寫(xiě)入日志文件緩沖區(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?
頁(yè)斷裂(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介紹

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容