自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

詳細(xì)了解 InnoDB 內(nèi)存結(jié)構(gòu)及其原理

存儲(chǔ) 存儲(chǔ)軟件
聊了一下MySQL和InnoDB的日志,和兩次寫,總的來(lái)說(shuō)算是一個(gè)入門級(jí)別的介紹,這篇文章就來(lái)詳細(xì)介紹一下InnoDB的內(nèi)存結(jié)構(gòu)。

 [[392749]]

之前寫過(guò)一篇文章「簡(jiǎn)單了解InnoDB原理」,現(xiàn)在回過(guò)頭看,其實(shí)里面只是把緩沖池(Buffer Pool),重做日志緩沖(Redo Log Buffer)、插入緩沖(Insert Buffer)和自適應(yīng)哈希索引(Adaptive Hash Index)等概念簡(jiǎn)單的介紹了一下。

除此之外還聊了一下MySQL和InnoDB的日志,和兩次寫,總的來(lái)說(shuō)算是一個(gè)入門級(jí)別的介紹,這篇文章就來(lái)詳細(xì)介紹一下InnoDB的內(nèi)存結(jié)構(gòu)。

InnoDB內(nèi)存結(jié)構(gòu)

其大致結(jié)構(gòu)如下圖。

InnoDB內(nèi)存的兩個(gè)主要區(qū)域分別為Buffer Pool和Log Buffer,此處的Log Buffer目前是用于緩存Redo Log。而Buffer Pool則是MySQL或者說(shuō)InnoDB中,十分重要、核心的一部分,位于主存。這也是為什么其訪問(wèn)數(shù)據(jù)的效率高,你可以暫時(shí)把它理解成Redis那樣的內(nèi)存數(shù)據(jù)庫(kù),因?yàn)槲覀兏潞托略霎?dāng)然它不是,只是這樣會(huì)更加方便我們理解。

Buffer Pool

通常來(lái)說(shuō),宿主機(jī)80%的內(nèi)存都應(yīng)該分配給Buffer Pool,因?yàn)锽uffer Pool越大,其能緩存的數(shù)據(jù)就更多,更多的操作都會(huì)發(fā)生在內(nèi)存,從而達(dá)到提升效率的目的。

由于其存儲(chǔ)的數(shù)據(jù)類型和數(shù)據(jù)量非常多,Buffer Pool存儲(chǔ)的時(shí)候一定會(huì)按照某些結(jié)構(gòu)去存儲(chǔ),并且做了某些處理。否則獲取的時(shí)候除了遍歷所有數(shù)據(jù)之外,沒(méi)有其他的捷徑,這樣的低效率操作肯定是無(wú)法支撐MySQL的高性能的。

因此,Buffer Pool被分成了很多頁(yè),這在之前的文章中也有講過(guò),這里不再贅述。每頁(yè)可以存放很多數(shù)據(jù),剛剛也提到了,InnoDB一定是對(duì)數(shù)據(jù)做了某些操作。

InnoDB使用了鏈表來(lái)組織頁(yè)和頁(yè)中存儲(chǔ)的數(shù)據(jù),頁(yè)與頁(yè)之間形成了雙向鏈表,這樣可以方便的從當(dāng)前頁(yè)跳到下一頁(yè),同時(shí)使用LRU(Least Recently Used)算法去淘汰那些不經(jīng)常使用的數(shù)據(jù)。

同時(shí),每頁(yè)中的數(shù)據(jù)也通過(guò)單向鏈表進(jìn)行鏈接。因?yàn)檫@些數(shù)據(jù)是分散到Buffer Pool中的,單向鏈表將這些分散的內(nèi)存給連接了起來(lái)。

Log Buffer

Log Buffer用來(lái)存儲(chǔ)那些即將被刷入到磁盤文件中的日志,例如Redo Log,該區(qū)域也是InnoDB內(nèi)存的重要組成部分。Log Buffer的默認(rèn)值為16M,如果我們需要進(jìn)行調(diào)整的話,可以通過(guò)配置參數(shù)innodb_log_buffer_size來(lái)進(jìn)行調(diào)整。

當(dāng)Log Buffer如果較大,就可以存儲(chǔ)更多的Redo Log,這樣一來(lái)在事務(wù)提交之前我們就不需要將Redo Log刷入磁盤,只需要丟到Log Buffer中去即可。因此較大的Log Buffer就可以更好的支持較大的事務(wù)運(yùn)行;同理,如果有事務(wù)會(huì)大量的更新、插入或者刪除行,那么適當(dāng)?shù)脑龃驦og Buffer的大小,也可以有效的減少部分磁盤I/O操作。

至于Log Buffer中的數(shù)據(jù)刷入到磁盤的頻率,則可以通過(guò)參數(shù)innodb_flush_log_at_trx_commit來(lái)決定。

Buffer Pool的LRU算法

了解完了InnoDB的內(nèi)存結(jié)構(gòu)之后,我們來(lái)仔細(xì)看看Buffer Pool的LRU算法是如何實(shí)現(xiàn)將最近沒(méi)有使用過(guò)的數(shù)據(jù)給過(guò)期的。

原生LRU

首先明確一點(diǎn),此處的LRU算法和我們傳統(tǒng)的LRU算法有一定的區(qū)別。為什么呢?因?yàn)閷?shí)際生產(chǎn)環(huán)境中會(huì)存在全表掃描的情況,如果數(shù)據(jù)量較大,可能會(huì)將Buffer Pool中存下來(lái)的熱點(diǎn)數(shù)據(jù)給全部替換出去,而這樣就會(huì)導(dǎo)致該段時(shí)間MySQL性能斷崖式下跌。

對(duì)于這種情況,MySQL有一個(gè)專用名詞叫緩沖池污染。所以MySQL對(duì)LRU算法做了優(yōu)化。

優(yōu)化后的LRU

優(yōu)化之后的鏈表被分成了兩個(gè)部分,分別是 New Sublist 和 Old Sublist,其分別占用了 Buffer Pool 的3/4和1/4。

鏈表的前3/4,也就是 New Sublist 存放的是訪問(wèn)較為頻繁的頁(yè),而后1/4也就是 Old Sublist 則是反問(wèn)的不那么頻繁的頁(yè)。Old Sublist中的數(shù)據(jù),會(huì)在后續(xù)Buffer Pool剩余空間不足、或者有新的頁(yè)加入時(shí)被移除掉。

了解了鏈表的整體構(gòu)造和組成之后,我們就以新頁(yè)被加入到鏈表為起點(diǎn),把整體流程走一遍。首先,一個(gè)新頁(yè)被放入到Buffer Pool之后,會(huì)被插入到鏈表中 New Sublist 和 Old Sublist 相交的位置,該位置叫MidPoint。

該鏈表存儲(chǔ)的數(shù)據(jù)來(lái)源有兩部分,分別是:

  • MySQL的預(yù)讀線程預(yù)先加載的數(shù)據(jù)
  • 用戶的操作,例如Query查詢

默認(rèn)情況下,由用戶操作影響而進(jìn)入到Buffer Pool中的數(shù)據(jù),會(huì)被立即放到鏈表的最前端,也就是 New Sublist 的 Head 部分。但如果是MySQL啟動(dòng)時(shí)預(yù)加載的數(shù)據(jù),則會(huì)放入MidPoint中,如果這部分?jǐn)?shù)據(jù)被用戶訪問(wèn)過(guò)之后,才會(huì)放到鏈表的最前端。

這樣一來(lái),雖然這些頁(yè)數(shù)據(jù)在鏈表中了,但是由于沒(méi)有被訪問(wèn)過(guò),就會(huì)被移動(dòng)到后1/4的 Old Sublist中去,直到被清理掉。

優(yōu)化Buffer Pool的配置

在實(shí)際的生產(chǎn)環(huán)境中,我們可以通過(guò)變更某些設(shè)置,來(lái)提升Buffer Pool運(yùn)行的性能。

  • 例如,我們可以分配盡量多的內(nèi)存給Buffer Pool,如此就可以緩存更多的數(shù)據(jù)在內(nèi)存中
  • 當(dāng)前有足夠的內(nèi)存時(shí),就可以搞多個(gè)Buffer Pool實(shí)例,減少并發(fā)操作所帶來(lái)的數(shù)據(jù)競(jìng)爭(zhēng)
  • 當(dāng)我們可以預(yù)測(cè)到即將到來(lái)的大量請(qǐng)求時(shí),我們可以手動(dòng)的執(zhí)行這部分?jǐn)?shù)據(jù)的預(yù)讀請(qǐng)求
  • 我們還可以控制Buffer Pool刷數(shù)據(jù)到磁盤的頻率,以根據(jù)當(dāng)前MySQL的負(fù)載動(dòng)態(tài)調(diào)整

那我們?cè)趺粗喇?dāng)前運(yùn)行的 MySQL 中 Buffer Pool 的狀態(tài)呢?我們可以通過(guò)命令show engine innodb status來(lái)查看。這個(gè)命令是看 InnoDB 整體的狀態(tài)的, Buffer Pool 相關(guān)的監(jiān)控指標(biāo)包含在了其中,在Buffer Pool And Memory模塊中。

樣例如下。

  1. ---------------------- 
  2. BUFFER POOL AND MEMORY 
  3. ---------------------- 
  4. Total large memory allocated 137428992 
  5. Dictionary memory allocated 972752 
  6. Buffer pool size   8191 
  7. Free buffers       4596 
  8. Database pages     3585 
  9. Old database pages 1303 
  10. Modified db pages  0 
  11. Pending reads      0 
  12. Pending writes: LRU 0, flush list 0, single page 0 
  13. Pages made young 1171, not young 0 
  14. 0.00 youngs/s, 0.00 non-youngs/s 
  15. Pages read 655, created 7139, written 173255 
  16. 0.00 reads/s, 0.00 creates/s, 0.00 writes/s 
  17. No buffer pool page gets since the last printout 
  18. Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s 
  19. LRU len: 3585, unzip_LRU len: 0 
  20. I/O sum[0]:cur[0], unzip sum[0]:cur[0] 

解釋一些關(guān)鍵的指標(biāo)所代表的含義:

  • Total memory allocated:分配給 Buffer Pool 的總內(nèi)存
  • Dictionary memory allocated:分配給 InnoDB 數(shù)據(jù)字典的總內(nèi)存
  • Buffer pool size:分配給 Buffer Pool 中頁(yè)的內(nèi)存大小
  • Free buffers:分配給 Buffer Pool 中 Free List 的內(nèi)存大小
  • Database pages:分配給 LRU 鏈表的內(nèi)存大小
  • Old database pages:分配給 LRU 子鏈表的內(nèi)存大小
  • Modified db pages:當(dāng)前Buffer Pook中被更新的頁(yè)的數(shù)量
  • Pending reads:當(dāng)前等待讀入 Buffer Pool 的頁(yè)的數(shù)量
  • Pending writes LRU:當(dāng)前在 LRU 鏈表中等待被刷入磁盤的臟頁(yè)數(shù)量

都是些很常規(guī)的配置項(xiàng),你可能會(huì)比較好奇什么是 Free List。

Free List 中存放的都是未被使用的頁(yè)。因?yàn)镸ySQL啟動(dòng)的時(shí)候,InnoDB 會(huì)預(yù)先申請(qǐng)一部分頁(yè)。如果當(dāng)前頁(yè)還未被使用,就會(huì)被保存在 Free List 中。

知道了 Free List,那么你也應(yīng)該知道 Flush List,里面保存的是所有的臟頁(yè),都是被更改后需要刷入到磁盤的。

自適應(yīng)哈希索引

自適應(yīng)哈希索引(Adaptive Hash Index)是配合Buffer Pool工作的一個(gè)功能。自適應(yīng)哈希索引使得MySQL的性能更加接近于內(nèi)存服務(wù)器。

如果要啟用自適應(yīng)哈希索引,可以通過(guò)更改配置innodb_adaptive_hash_index來(lái)開啟。如果不想啟用,也可以在啟動(dòng)的時(shí)候,通過(guò)命令行參數(shù)--skip-innodb-adaptive-hash-index來(lái)關(guān)閉。

自適應(yīng)哈希索引是根據(jù)索引Key的前綴來(lái)構(gòu)建的,InnoDB 有自己的監(jiān)控索引的機(jī)制,當(dāng)其檢測(cè)到為當(dāng)前某個(gè)索引頁(yè)建立哈希索引能夠提升效率時(shí),就會(huì)創(chuàng)建對(duì)應(yīng)的哈希索引。如果某張表數(shù)據(jù)量很少,其數(shù)據(jù)全部都在Buffer Pool中,那么此時(shí)自適應(yīng)哈希索引就會(huì)變成我們所熟悉的指針這樣一個(gè)角色。

當(dāng)然,創(chuàng)建、維護(hù)自適應(yīng)哈希索引是會(huì)帶來(lái)一定的開銷的,但是比起其帶來(lái)的性能上的提升,這點(diǎn)開銷可以直接忽略不計(jì)。但是,是否要開啟自適應(yīng)哈希索引還是需要看具體的業(yè)務(wù)情況的,例如當(dāng)我們的業(yè)務(wù)特征是有大量的并發(fā)Join查詢,此時(shí)訪問(wèn)自適應(yīng)哈希索引被產(chǎn)生競(jìng)爭(zhēng)。并且如果業(yè)務(wù)還使用了LIKE或者%等通配符,根本就不會(huì)用到哈希索引,那么此時(shí)自適應(yīng)哈希索引反而變成了系統(tǒng)的負(fù)擔(dān)。

所以,為了盡可能的減少并發(fā)情況下帶來(lái)的競(jìng)爭(zhēng),InnoDB對(duì)自適應(yīng)哈希索引進(jìn)行了分區(qū),每個(gè)索引都被綁定到了一個(gè)特定的分區(qū),而每個(gè)分區(qū)都由單獨(dú)的鎖進(jìn)行保護(hù)。其實(shí)通俗點(diǎn)理解,就是降低了鎖的粒度。分區(qū)的數(shù)量我們可以通過(guò)配置innodb_adaptive_hash_index_parts來(lái)改變,其可配置的區(qū)間范圍為[8, 512]。

Change Buffer

聊完了 Buffer Pool 中索引相關(guān),剩下的就是 Change Buffer 了。Change Buffer是一塊比較特殊的區(qū)域,其作用是用于存儲(chǔ)那些當(dāng)前不在 Buffer Pool 中的但是又被修改過(guò)的二級(jí)索引。

用流程來(lái)描述一下就是,當(dāng)我們更新了非聚簇索引(二級(jí)索引)的數(shù)據(jù)時(shí),此時(shí)應(yīng)該是直接將其在Buffer Pool中的對(duì)應(yīng)數(shù)據(jù)更新了即可,但是不湊巧的是,當(dāng)前二級(jí)索引不在 Buffer Pool 中,此時(shí)將其從磁盤拉取到 Buffer Pool 中的話,并不是最優(yōu)的解,因?yàn)樵摱?jí)索引可能之后根本就不會(huì)被用到,那么剛剛昂貴的磁盤I/O操作就白費(fèi)了。

所以,我們需要這么一個(gè)地方,來(lái)暫存對(duì)這些二級(jí)索引所做的改動(dòng)。當(dāng)被緩存的二級(jí)索引頁(yè)被其他的請(qǐng)求加載到了Buffer Pool 中之后,就會(huì)將 Change Buffer 中緩存的數(shù)據(jù)合并到 Buffer Pool 中去。

當(dāng)然,Change Buffer也不是沒(méi)有缺點(diǎn)。當(dāng) Change Buffer 中有很多的數(shù)據(jù)時(shí),全部合并到Buffer Pool可能會(huì)花上幾個(gè)小時(shí)的時(shí)間,并且在合并的期間,磁盤的I/O操作會(huì)比較頻繁,從而導(dǎo)致部分的CPU資源被占用。

那你可能會(huì)問(wèn),難道只有被緩存的頁(yè)加載到了 Buffer Pool 才會(huì)觸發(fā)合并操作嗎?那要是它一直沒(méi)有被加載進(jìn)來(lái),Change Buffer 不就被撐爆了?很顯然,InnoDB在設(shè)計(jì)的時(shí)候考慮到了這個(gè)點(diǎn)。除了對(duì)應(yīng)的頁(yè)加載,提交事務(wù)、服務(wù)停機(jī)、服務(wù)重啟都會(huì)觸發(fā)合并。

責(zé)任編輯:武曉燕 來(lái)源: SH的全棧筆記
相關(guān)推薦

2009-07-06 16:05:50

JSP特點(diǎn)

2022-03-08 08:44:13

偏向鎖Java內(nèi)置鎖

2010-04-16 11:08:23

2010-11-16 09:55:12

Oracle分區(qū)索引

2011-06-07 11:21:04

JSP隱含對(duì)象

2011-07-28 10:40:40

Cocoa KVO

2021-07-22 06:08:43

SQL.js關(guān)系數(shù)據(jù)庫(kù)數(shù)據(jù)庫(kù)

2011-08-25 15:10:49

LUAWindows環(huán)境配置

2011-07-01 14:34:02

Thread Affinity 信號(hào)

2010-10-25 11:51:05

Oracle單行字符串

2010-10-21 15:26:35

SQL Server字

2010-11-12 14:29:46

Sql Server創(chuàng)

2018-11-27 15:55:21

TCP通訊協(xié)議

2010-09-27 09:31:42

JVM內(nèi)存結(jié)構(gòu)

2023-10-30 13:31:22

Springboot工具Java

2021-06-12 07:38:21

Linkerd 2.Service Mes微服務(wù)

2019-09-18 08:31:47

數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)

2022-09-21 18:06:10

Python內(nèi)存管理

2019-11-04 12:51:48

mysql數(shù)據(jù)庫(kù)nnodb

2009-04-28 13:48:09

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)