MySQL中的change buffer


參考:

https://blog.csdn.net/weixin_38304221/article/details/88635432

https://blog.csdn.net/nanjingitany/article/details/102856472

https://www.cnblogs.com/abclife/p/7625281.html

https://www.cnblogs.com/ging/p/13467833.html

 

 

 

 

 

MySQL中的change buffer

MySQL的一條語句,大致流程查看內存→讀取磁盤數據頁→返回數據。

當比如查找一個 a=5的記錄的時候,並不是只查找出這一條數據,它所在的整個數據頁都會查找出來(每個數據頁16KB)。

下次查找a=6的記錄的時候,發現該頁已經在內存中了,直接返回,不需要磁盤IO。

但是當時增、刪、改操作時,並不會每一次操作都進行一次磁盤IO,使用change buffer可以降低磁盤隨機IO。

change buffer首先是可持久化的數據。

當更新某個數據頁時,該頁在內存中,那么直接更新。

如果該頁不在內存中,那么先將更新操作記錄在change buffer中,這時不需要從磁盤中讀出數據頁。

將操作記錄到redo log中,防止機器意外關閉導致數據丟失。

這時已經可以返回給客戶端更新成功了,因為即使機器意外重啟,也可以通過redo log找回數據。

change buffer 使用的是 buffer pool里的內存,不能無限增大,可以通過參數 innodb_change_buffer_max_xize來動態設置,這個參數為50的時候,標識change buffer 的大小最多只能占用 buffer pool 的50%。

什么時候將change buffer中的數據更新到磁盤中?

當下一次查詢命中這個數據頁的時候,會先從磁盤中讀取數據頁到內存中,然后先執行change buffer的merge操作,保證數據邏輯的正確性。除了查詢操作外,系統有后台線程會定期merge,數據庫正常關閉(shutdown)的時候,也會進行merge操作。

 

change buffer 和 redo log 對於磁盤的隨機IO影響。

redo log是減少 隨機寫磁盤IO 的消耗。每個操作先記錄redo log,系統空閑時或redo log滿時進行磁盤IO。

change buffer是減少 隨機讀磁盤IO 的消耗。更新時如果內存中不存在該數據頁,也不需要馬上進行磁盤IO,而是先記錄在change buffer中,等待時機統一merge

 

所以根據業務場景選擇是否使用change buffer。

當寫少讀多的情況下:change buffer的利用率不高,因為可能剛一更新完可能就觸發merge操作,change buffer沒有起到減少隨機IO,還多了一個維護change buffer的成本。

當寫多讀少的情況下:change buffer的提升效果較明顯,可能很多條更新后,也沒有一個查詢線程來觸發merge操作,可以大幅減少磁盤的隨機IO。

 

唯一索引和 普通索引(在業務邏輯層保證數據唯一)的選擇

命中唯一索引和普通索引時MySQL篩選數據的邏輯不同。

比如一個查詢語句 select * from table where k=5;先通過B+樹從樹根開始,按層搜索到葉子節點,也就是數據頁。

唯一索引 k:由於索引定義了唯一性,所以在數據頁中找到第一個滿足條件的記錄后,直接返回。

普通索引 k:沒有定義索引的唯一性,所以在查找到第一條滿足k=5后,還會繼續遍歷下一條,直到查到的k != 5的時候,結束遍歷。

那么兩種索引的效率差距有多大呢?

其實是很小,幾乎可忽略。普通索引相對於唯一索引多的一步操作就是"查找並判斷下一條記錄",因為都存在於同一個數據頁,所以內存遍歷對於CPU的開銷可忽略。一個數據頁可以存在上千個key,剛好下一條數據在下一個數據頁而觸發再次讀取數據頁的概率很低。所以這兩種索引的效率幾乎一樣。

但是當進行增刪改操作時,兩種索引的效率就有明顯的差距了。

因為唯一索引每個插入或者更新都要判斷是否違反唯一約束,所以每次更新都要從內存中找到這個記錄對應的數據頁,沒有的話需要從磁盤中讀出,反正都要從磁盤讀數據頁了,還不如直接更新內存/磁盤,那么也就意味着change buffer失去意義,所以唯一索引使用change buffer 的話反倒會降低效率,所以只有普通索引能使用到change buffer。

 

 

 

 

 

 

 

淺談mysql的change buffer

1.數據的更新過程

當mysql執行更新操作時,需要先更新一個數據頁。如果這個數據頁在內存中,則會直接更新,如果
這個數據頁不在內存中,InnoDB會將這個更新操作緩存在change buffer中,當下次需要查詢訪問這個數據頁的時候,會將數據頁讀入內存,然后執行change buffer中和這個數據頁相關的操作,這樣就保證了數據的邏輯正確性。

2.merge觸發

將change buffer中的操作應用到原數據頁,得到最新結果的過程稱為merge。除了訪問這個數據頁會觸發merge外,系統有后台線程會定期merge。在數據庫正常關閉(shutdown)的過程中,也會執行merge操作。

3.使用change buffer的場景

只有普通索引的更新會使用change buffer,用來提高內存利用率,但是如果被更新的字段是唯一索引,就不會使用change buffer,因為更新操作會判斷是否違反唯一約束的條件,必須將數據頁讀入內存才能判斷,因此唯一索引不會使用change buffer,只有普通索引才能使用。
在日常業務場景中,對於寫多讀少的操作,使用change buffer的效果最好,例如賬單類、日志類的操作,如果是讀寫操作都是很多的情況下,寫完后將操作記錄保存在change buffer中然后立即查詢,這樣會頻繁觸發merge,增加了IO的操作頻率,這種場景使用change buffer並不能帶來性能的提升,這種業務場景應該關閉change buffer。

4.change buffer的大小設置

在InnoDB中,可以通過參數innodb_change_buffer_max_size來設置,默認值是25,最大是50。例如當這個參數值是50 的時候,表示change buffer最多只能占用buffer pool的50%

5.索引的選擇

唯一索引和普通索引在查詢上並無區別,但是在更新操作上兩者對性能的影響是不一樣的,所以建議盡量使用普通索引。

 

 

 

 

 

 

MySQL-對Change Buffer的理解

Change Buffer的處理過程

非唯一普通索引的新增或更新操作,如果索引B+樹的需要新增或更新的數據頁不在內存中,則直接更新change buffer,等到后面需要使用這個數據頁(真正讀到內存中來)的時候,再根據change buffer在內存中做merge合並操作

Change Buffer有什么好處?

先想想沒有change buffer時候,在緩沖池中沒有對應數據頁時會怎么更新。概括來說,有兩個步驟:

  1. 首先需要從磁盤中讀取對應的數據頁到內存中
  2. 然后更新內存中的數據頁。

首先分析主鍵索引或者非主鍵索引中的唯一索引,插入或者更新的操作。

  • 主鍵如果是自增的,只需要讀取順序讀取磁盤中的頁,然后插入最新的行即可。主鍵如果是非自增的或者是自己設置的值,那么可能需要做一次隨機磁盤IO操作,讀取到對應的頁,做一下唯一性判斷,然后插入數據即可。
  • 針對非主鍵索引中的唯一索引,大概率需要做隨機磁盤IO讀取,然后判斷唯一性,再插入對應的行。

所以對於主鍵索引和非主鍵的唯一索引,因為有唯一性判斷,所以更新操作時,必須要從磁盤中讀取數據頁,判斷唯一性,然后才能確定這個更新操作是否成功,即這個磁盤的IO操作是不可避免的。

對於非唯一索引來說,其實步驟也是類似的。但是因為不需要做唯一性判斷,所以為了提高更新的性能,Mysql給出的解決方案就是使用change buffer來保存對非唯一索引的更新。也就是說,當需要更新非唯一索引時,直接操作change buffer,成功即可返回。

那么什么時候會真正更新數據頁呢?有兩種情況會觸發:

  1. 被動:在后續的真正需要讀這個非唯一索引時,把索引的數據頁從磁盤讀取到內存中,再通過change buffer做一個merge操作,merge操作以后,內存中的數據頁就是最新的了。
  2. 主動:innoDB引擎中有線程會主動的定期做merge操作
業務實踐
  1. 利用普通索引的change buffer特性,當業務場景中的寫遠大於讀時,常見場景為日志表,當某些列必須建立索引時,可以考慮建立普通索引,提高寫入性能
  2. 如果業務場景的寫之后立即伴隨讀,如果列的值是唯一的
  • 那么其實建立普通索引是不合適的,因為寫的過程,雖然利用了change buffer暫時提高了寫的性能,但是在讀的時候還是需要磁盤IO。可以考慮建立唯一索引,在索引寫的時候,就提前讀取數據到緩沖池中,提高讀的性能。

 

 

 

 

 

MySQL -- Innodb中的change buffer

change buffer是一種特殊的數據結構,當要修改的輔助索引頁不在buffer pool中時,用來cache對輔助索引頁的修改。對輔助索引頁的操作可能是insert、update和delete操作。等到相關的索引頁被讀入buffer pool中后,才會使用change buffer中的內容對輔助索引頁進行修改(即merge操作)。

和聚集索引不同,輔助索引通常是不唯一的,插入輔助索引通常也是隨機的。同樣,對輔助索引的刪除、更新也通常是不連續的。

等到相關的索引頁被讀入buffer pool中后,才會使用change buffer中的內容對輔助索引頁進行修改(即merge操作)可以避免大量的磁盤隨機訪問I/O。

間歇性的,在系統空閑或關閉過程中,會執行purge操作,將新的索引頁寫入磁盤。purge操作一次寫多個索引值會比每次修改后就立即寫入磁盤的效率高。

對change buffer的merge可能需要好幾個小時,如果被更新的輔助索引行比較多。在merge過程中,磁盤的I/O會增加,可能會引起其他查詢的性能的降低。
merge操作也可能發生在事務提交后。事實上,即使在實例重啟后,還會可能發生merge操作。

在內存中,change buffer會占用buffer pool的空間;在物理磁盤上,change buffer是system tablespace的一部分,所以對索引的修改在數據庫重啟后仍然存在change buffer中。

change buffer包含的特性也叫作change buffering,包含insert buffering、delete buffering、purge buffering。

change buffer中數據類型和總量由參數innodb_change_buffering和innodb_chagne_buffer_max_size配置。查看change buffer中數據的信息,可以通過show engine innodb status查看。

change buffer在老版本中被稱作insert buffer。

 

1.監控change buffer 

1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> show engine innodb status\G
...
-------------------------------------
INSERT  BUFFER  AND  ADAPTIVE HASH  INDEX
-------------------------------------
Ibuf:  size  1,  free  list len 0, seg  size  2, 0 merges
merged operations:
  insert  0,  delete  mark 0,  delete  0
discarded operations:
  insert  0,  delete  mark 0,  delete  0
Hash  table  size  4425293, used cells 32, node heap has 1 buffer(s)
13577.57 hash searches/s, 202.47 non-hash searches/s
...

2.查看information_schema
information_schema.innodb_metrics提供了change buffer的統計信息名稱和說明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
mysql>  select  name , comment  from  information_schema.innodb_metrics  where  name  like  '%ibuf%' ;
+ -----------------------------------------+-------------------------------------------------------------+
name                                     | comment                                                     |
+ -----------------------------------------+-------------------------------------------------------------+
| buffer_page_read_index_ibuf_leaf        | Number  of  Insert  Buffer  Index  Leaf Pages  read                |
| buffer_page_read_index_ibuf_non_leaf    | Number  of  Insert  Buffer  Index  Non-Leaf Pages  read            |
| buffer_page_read_ibuf_free_list         | Number  of  Insert  Buffer  Free  List Pages  read                 |
| buffer_page_read_ibuf_bitmap            | Number  of  Insert  Buffer Bitmap Pages  read                    |
| buffer_page_written_index_ibuf_leaf     | Number  of  Insert  Buffer  Index  Leaf Pages written            |
| buffer_page_written_index_ibuf_non_leaf | Number  of  Insert  Buffer  Index  Non-Leaf Pages written        |
| buffer_page_written_ibuf_free_list      | Number  of  Insert  Buffer  Free  List Pages written             |
| buffer_page_written_ibuf_bitmap         | Number  of  Insert  Buffer Bitmap Pages written                |
| ibuf_merges_insert                      | Number  of  inserted records merged  by  change buffering       |
| ibuf_merges_delete_mark                 | Number  of  deleted records merged  by  change buffering        |
| ibuf_merges_delete                      | Number  of  purge records merged  by  change buffering          |
| ibuf_merges_discard_insert              | Number  of  insert  merged operations discarded                |
| ibuf_merges_discard_delete_mark         | Number  of  deleted merged operations discarded               |
| ibuf_merges_discard_delete              | Number  of  purge merged  operations discarded                |
| ibuf_merges                             | Number  of  change buffer merges                              |
| ibuf_size                               | Change buffer  size  in  pages                                 |
| innodb_ibuf_merge_usec                  |  Time  ( in  microseconds) spent  to  process change buffer merge |
+ -----------------------------------------+-------------------------------------------------------------+
17  rows  in  set  (0.00 sec)
 
mysql>

information_schema.innodb_buffer_page提供了buffer pool中每個頁的元數據,包含change buffer 索引頁和change buffer位圖頁。
change buffer頁中page_type.ibuf_index表示change buffer索引頁;page_type.ibuf_bitmap表示change buffer位圖頁。

提醒:查看innodb_buffer_page會有很大的性能開銷。最好是在空閑時間或測試環境執行。

比如,可以查看change buffer的索引頁和位圖頁占據buffer pool的比例:

1
2
3
4
5
6
7
8
9
10
11
mysql>  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 | total_pages | change_buffer_page_percentage |
+ ---------------------+-------------+-------------------------------+
|                  14 |        8192 |                        0.1709 |
+ ---------------------+-------------+-------------------------------+
1 row  in  set  (0.05 sec)

performance_schema還為高級性能監控提供了change buffer mutex的等待指令:

1
2
3
4
5
6
7
8
9
10
mysql>  SELECT  FROM  performance_schema.setup_instruments
     ->         WHERE  NAME  LIKE  '%wait/synch/mutex/innodb/ibuf%' ;
+ -------------------------------------------------------+---------+-------+
NAME                                                   | ENABLED | TIMED |
+ -------------------------------------------------------+---------+-------+
| wait/synch/mutex/innodb/ibuf_bitmap_mutex             |  NO       NO     |
| wait/synch/mutex/innodb/ibuf_mutex                    |  NO       NO     |
| wait/synch/mutex/innodb/ibuf_pessimistic_insert_mutex |  NO       NO     |
+ -------------------------------------------------------+---------+-------+
rows  in  set  (0.00 sec)

3.配置change buffer

可以通過參數innodb_change_buffering來控制是否啟用change buffer。

1
2
3
4
5
6
--all:      默認值。開啟buffer inserts、delete-marking operations、purges
--none: 不開啟change buffer
--inserts:  只是開啟buffer insert操作
--deletes:  只是開delete-marking操作
--changes:  開啟buffer insert操作和delete-marking操作
--purges:   對只是在后台執行的物理刪除操作開啟buffer功能

從5.6.2開始,參數innodb_change_buffer_max_size設置了change buffer可以占用buffer pool的百分比,默認是25,最大可以設置為50。

1
2
3
4
5
6
7
8
9
mysql> show variables  like  'innodb_change_buffer_max_size' ;
+ -------------------------------+-------+
| Variable_name                 | Value |
+ -------------------------------+-------+
| innodb_change_buffer_max_size | 25    |
+ -------------------------------+-------+
1 row  in  set  (0.00 sec)
 
mysql>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM