1. 程式人生 > 其它 >Java效能優化面試題集錦

Java效能優化面試題集錦

Java效能優化面試題集錦

InnoDB總體結構

首先我們來看官網的一張圖(圖片來源於MySQL官網):

從上圖中可以看出其主要分為兩部分結構,一部分為記憶體中的結構(上圖左邊),一部分為磁碟中的結構(上圖右邊)

記憶體結構

InnoDB記憶體中的結構主要分為:Buffer Pool,Change Buffer和Log Buffer三部分。

Buffer Pool

Buffer Pool是InnoDB快取表和索引的一塊主記憶體區域,Buffer Pool允許直接從記憶體中處理經常使用的資料,從而加快處理速度,帶來一定的效能提升。 但是快取總有放滿的時候,當快取滿了新來的資料怎麼處理呢?Bufer Pool中採用的是LRU(least recently used,最近最少使用)演算法,LRU列表中最前面存的是高頻使用頁,尾部放的是最少使用的頁。當有新資料過來而快取滿了就會覆蓋尾部資料。

假如我們有一條查詢語句非常大,返回的結果集直接就超過了Buffer Pool的大小,而這種語句使用場景又是極少的,可能查詢這一次之後很久不會查詢,而這一次就將快取佔滿了,將一些熱點資料全部覆蓋了。為了避免這種情況發生,InnoDB對傳統的LRU演算法又做了改進,將LRU列表分拆分為2個,如下圖(圖片來源於MySQL官網):

該演算法在new子列表中保留大量頁面(5/8),old子列表包含較少使用的頁面(3/8);old子列表中資料可能會被覆蓋,該演算法具體操作如下:

  • 3/8的Buffer Pool空間用於old子列表

  • 列表的中點是new子列表的尾部與old子列表的頭部之間的邊界

  • 當InnoDB將一個頁面讀入緩衝池時,它首先將它插入到中間點(old子列表的頭)。讀取的頁面是由使用者發起的操作(比如SQL查詢)或InnoDB自動執行的預讀操作

  • 訪問old子列表中的頁面使其“young”,並將其移動到new子列表的頭部。如果讀取的頁是由使用者發起的操作,那麼就會立即進行第一次訪問,並使頁面處於young狀態;如果讀取的頁是由預讀發起的操作,那麼第一次訪問不會立即發生,而且可能直到覆蓋都不會發生。

  • 操作資料時,Buffer Pool中未被訪問的頁會逐漸移到尾部,最終會被覆蓋。

預設情況下,查詢讀取的頁面會立即移動到新的子列表中,這意味著它們在緩衝池中停留的時間更長。

Change Buffer

Change Buffer是一種特殊的快取結構,用來快取不在Buffer Pool中的輔助索引頁, 支援insert, update,delete(DML)操作的快取(注意,這個在MySQL5.5之前叫做Insert Buffer,僅支援insert操作的快取)。當這些資料頁被其他查詢載入到Buffer Pool後,則會將資料進行merge到索引資料葉中。

InnoDB在進行DML操作非聚集非唯一索引時,會先判斷要操作的資料頁是不是在Buffer Pool中,如果不在就會先放到Change Buffer進行操作,然後再以一定的頻率將資料和輔助索引資料頁進行merge。這時候通常都能將多個操作合併到一次操作,減少了IO操作,尤其是輔助索引的操作大部分都是IO操作,可以大大提高DML效能。

如果Change Buffer中儲存了大量的資料,那麼可能merge操作會需要消耗大量時間。

為什麼Change Buffer只能針對非聚集非唯一索引

因為如果是主鍵索引或者唯一索引,需要判斷資料是否唯一,這時候就需要去索引頁中載入資料判斷而不能僅僅只操作快取。

Change Buffer什麼時候會merge

總體來說,Change Buffer的merge操作發生在以下三種情況:

  • 輔助索引頁被讀取到Buffer Pool時。 當執行一條select語句時,會去檢查當前資料頁是否在Change Buffer中,如果在,就會把資料merge到索引頁

  • 該輔助索引頁沒有可用空間時。 InnoDB內部會檢測輔助索引頁是否還有可用空間(至少有1/32頁),如果檢測到當前操作之後,當前索引頁剩餘空間不足1/32時,會進行一次強制merge操作

  • 後臺執行緒Master Thread定時merge。 Master Thread是一個非常核心的後臺執行緒,主要負責將緩衝池中的資料非同步重新整理到磁碟,保證資料的一致性。

Adaptive Hash Index

Adaptive Hash Index,自適應雜湊索引。InnoDB引擎會監控對索引頁的查詢,如果發現建立雜湊索引可以帶來效能上的提升,就會建立雜湊索引,這種稱之為自適應雜湊索引,InnoDB引擎不支援手動建立雜湊索引。

Log Buffer

日誌緩衝區是儲存要寫入磁碟日誌檔案的一塊資料記憶體區域,大小由變數innodb_log_buffer_size 控制,預設大小為16MB(5.6版本是8MB):

SHOW VARIABLES LIKE 'innodb_log_buffer_size';-- global級別,無session級別

上文講述update語句更新流程一文中,我們只提到了Buffer Pool用來代替快取區,通過本文對記憶體結構的分析,實際上Buffer Pool中嚴格來說還有Change Buffer,Log Buffer和Adaptive Hash Index三個部分,DML操作會快取在Change Buffer區域,而寫redo log之前會先寫入Log Buffer,所以Log Buffer又可以稱之為redo Log Buffer。

Log Buffer什麼時候寫入redo log

一個大的Log Buffer空間大允許執行大型事務,而無需在事務提交之前將redo log資料寫入磁碟。Log Buffer中的資料會定期重新整理到磁碟,那麼Log Buffer的資料又是如何寫入磁碟的呢?Log Buffer資料flush到磁碟有三種方式,通過變數innodb_flush_log_at_trx_commit 控制,預設為1。 |value|描述|

  • 當設定為0時,由於資料還在記憶體,所以崩潰後資料基本會被丟失

  • 當設定為2時,由於資料已經實時寫到redo log了,如果磁碟檔案沒有被損壞,還是可以恢復的

另外,Mast Thread預設1s進行一次刷盤操作,這個可以通過變數innodb_flush_log_at_timeout控制,預設1s。

SHOW VARIABLES LIKE 'innodb_flush_log_at_timeout';-- global級別,無session級別

磁碟結構

InnoDB引擎的磁碟結構,從大的方面來說可以分為Tablespace和redo log兩部分

Tablespace

Tablespace可以分為4大類,分別是:System Tablespace,File-Per-Table Tablespaces,General Tablespaces,Undo Tablespaces

System Tablespace

系統表空間中包括了 InnoDB data dictionary,doublewrite buffer, change buffer, undo logs 4個部分,預設情況下InnoDB儲存引擎有一個共享表空間ibdata1,如果我們建立表沒有指定表空間,則表和索引資料也會儲存在這個檔案當中,可以通過一個變數控制(後面會介紹)。

ibdata1檔案預設大小為12MB,可以通過變數innodb_data_file_path來控制,改變其大小的最好方式就是設定為自動擴充套件。

innodb_data_file_path=ibdata1:12M:autoextend

上面表示預設表空間ibdata1大小為12MB,支援自動擴充套件大小。

當我們的檔案達到一定的大小之後,比如達到了998MB,我們就可以另外開啟一個表空間檔案:

innodb_data_home_dir=
innodb_data_file_path=/ibdata/ibdata1:988M;/disk2/ibdata2:50M:autoextend

關於上面的設定有3點需要注意:

  • innodb_data_home_dir如果不設定的話,那麼就預設所有的表空間檔案都在datadir目錄下,而我們上面指定了2個不同路徑,所以需要把innodb_data_home_dir設為空

  • autoextend這個屬性,只能放在最後一個檔案

  • 指定新的表空間檔名的時候,不能和現有表空間檔名一致,否則啟動MySQL時會報錯

當然,表空間可以增大,自然也可以減少,但是一般我們都不會去設定減少,而且減少表空間也相對麻煩,在這裡就不展開敘述了。

InnoDB Data Dictionary

InnoDB資料字典由內部系統表組成,其中包含用於跟蹤物件(如表、索引和表列)的元資料。元資料在物理上位於InnoDB系統表空間中。由於歷史原因,資料字典元資料在某種程度上與儲存在InnoDB表元資料檔案(.frm檔案)中的資訊重疊。

Doublewrite Buffer

Doublewrite Buffer,雙寫緩衝區,這個是InnoDB為了實現double write而設定的一塊緩衝區,double write和上面的change buffer一個確保了可靠性,一個確保了效能的提升,是InnoDB中非常重要的兩大特性。

我們先來看下面一張圖:

InnoDB預設頁的大小是16KB,而作業系統是4KB,如果儲存引擎正在寫入頁的資料到磁碟時發生了宕機,可能出現頁只寫了一部分的情況,比如只寫了 4K,這種情況叫做部分寫失效(partial page write),可能會導致資料丟失。

可能有人會說,可以通過redo log來恢復,但是注意,redo log恢復資料有一個前提,那就是頁沒有損壞,如果頁本身已經被損壞了,那麼是沒辦法恢復的,所以為了確保萬無一失,我們需要先儲存一個頁的副本,如果出現了上面的極端情況,可以用頁的副本結合redo log來恢復資料,這就是double write技術。

double write也是由兩部分組成,一部分是記憶體中的double write buffer,大小為2MB,另一部分是物理磁碟上的共享表空間中的連續128個頁,大小也是2MB,寫入流程如下圖(圖片來源於《MySQL技術內幕 InnoDB儲存引擎》):

double write機制會使得資料寫入兩次磁碟,但是其並不需要兩倍的I/O開銷或兩倍的I/O操作。通過對作業系統的單個fsync()呼叫,資料以一個大的順序塊的形式寫入到雙寫入緩衝區。

在大多數情況下預設啟用了doublewrite緩衝區。要禁用doublewrite緩衝區,可通過將變數innodb_doublewrite設定為0即可。

最後

手繪了下圖所示的kafka知識大綱流程圖(xmind檔案不能上傳,匯出圖片展現),但都都可提供原始檔給每位愛學習的朋友,獲取連結:戳這裡免費下載