第四課(1)——MySQL體系結構


第四課(1)——MySQL體系結構

學習目標

一、MySQL體系結構 
二、MySQL內存結構 
三、MySQL文件結構 
四、Innodb體系結構


MySQL體系結構

一、MySQL體系結構圖

1、Mysql是由SQL接口,解析器,優化器,緩存,存儲引擎組成的(SQL Interface、Parser、Optimizer、Caches&Buffers、Pluggable Storage Engines)

 

(1) Connectors指的是不同語言中與SQL的交互 
(2)Management Serveices & Utilities: 系統管理和控制工具,例如備份恢復、Mysql復制、集群等 
(3)Connection Pool: 連接池:管理緩沖用戶連接、用戶名、密碼、權限校驗、線程處理等需要緩存的需求 
(4)SQL Interface: SQL接口:接受用戶的SQL命令,並且返回用戶需要查詢的結果。比如select from就是調用SQL Interface 
(5)Parser: 解析器,SQL命令傳遞到解析器的時候會被解析器驗證和解析。解析器是由Lex和YACC實現的,是一個很長的腳本, 主要功能: 
a . 將SQL語句分解成數據結構,並將這個結構傳遞到后續步驟,以后SQL語句的傳遞和處理就是基於這個結構的 
b. 如果在分解構成中遇到錯誤,那么就說明這個sql語句是不合理的 
(6)Optimizer: 查詢優化器,SQL語句在查詢之前會使用查詢優化器對查詢進行優化。他使用的是“選取-投影-聯接”策略進行查詢。 
用一個例子就可以理解: select uid,name from user where gender = 1; 
這個select 查詢先根據where 語句進行選取,而不是先將表全部查詢出來以后再進行gender過濾 
這個select查詢先根據uid和name進行屬性投影,而不是將屬性全部取出以后再進行過濾 
將這兩個查詢條件聯接起來生成最終查詢結果 

(7) Cache和Buffer(高速緩存區): 查詢緩存,如果查詢緩存有命中的查詢結果,查詢語句就可以直接去查詢緩存中取數據。 
通過LRU算法將數據的冷端溢出,未來得及時刷新到磁盤的數據頁,叫臟頁。 
這個緩存機制是由一系列小緩存組成的。比如表緩存,記錄緩存,key緩存,權限緩存等 
(8)Engine :存儲引擎。存儲引擎是MySql中具體的與文件打交道的子系統。也是Mysql最具有特色的一個地方。 
Mysql的存儲引擎是插件式的。它根據MySql AB公司提供的文件訪問層的一個抽象接口來定制一種文件訪問機制(這種訪問機制就叫存儲引擎) 
現在有很多種存儲引擎,各個存儲引擎的優勢各不一樣,最常用的MyISAM,InnoDB,BDB 
默認下MySql是使用MyISAM引擎,它查詢速度快,有較好的索引優化和數據壓縮技術。但是它不支持事務。 
InnoDB支持事務,並且提供行級的鎖定,應用也相當廣泛。 
Mysql也支持自己定制存儲引擎,甚至一個庫中不同的表使用不同的存儲引擎,這些都是允許的。

二、MySQL內存結構

Mysql 內存分配規則是:用多少給多少,最高到配置的值,不是立即分配 
 
MySQL中內存大致分為:全局內存(Global buffer)、線程內存(Thread buffer)兩大部分

1、全局內存(Global buffer)

 
(1)innodb_buffer_pool_size:

(1.1) innodb高速緩沖data和索引,簡稱IBP,這個是Innodb引擎中影響性能最大的參數。建議將IBP設置的大一些,單實例下,建議設置為可用RAM的50%~80%。
(1.2)innodb不依賴OS,而是自己緩存了所有數據,包括索引數據、行數據等等,這個和myisam有差別。
(1.3)IBP有一塊buffer用於插入緩沖,在插入時,先寫入內存之后再合並后順序寫入磁盤;在合並到磁盤的時候會引發較大的IO操作,對實際操作造成影響。(看上去的表現是抖動,TPS變低)
(1.4)show global status like ‘innodb_buffer_pool_%’ 查看IBP狀態,單位是page(16kb),其中,Innodb_buffer_pool_wait_free 如果較大,需要加大IBP設置
(1.5)InnoDB會定時(約每10秒)將臟頁刷新到磁盤,默認每次刷新10頁;要是臟頁超過了指定數量(innodb_max_dirty_pages_pct),InnoDB則會每秒刷100頁臟頁
(1.6)innodb_buffer_pool_instances可以設置pool的數量
(1.7)show engine innodb status\G    可以查看innodb引擎狀態

(2)innodb_additional_mem_pool_size:

(2.1)指定InnoDB用來存儲數據字典和其他內部數據結構的內存池大小。缺省值是8M(8388608)。通常不用太大,只要夠用就行,應該與表結構的復雜度有關系。如果不夠用,MySQL會在錯誤日志中寫入一條警告信息。

(3)innodb_log_buffer_size:

(3.1)innodb redo日志緩沖,提高redo寫入效率。如果表操作中包含大量並發事務(或大規模事務),並且在事務提交前要求記錄日志文件,請盡量調高此項值,以提高日志效率。
(3.2)show global status 查看 Innodb_log_waits 是否大於0,是的話,就需要提高 innodb_log_buffer_size,否則維持原樣。
(3.3)show global stauts 查看30~60秒鍾 Innodb_os_log_written 的間隔差異值,即可計算出 innodb_log_buffer_size 設置多大合適。

默認8M,一般設置為16 ~ 64M足夠了。 
 
(4)key_buffer_size:

(4.1)myisam引擎中表的索引 的緩存大小,默認值=16M;單個key_buffer_size最大只有4G(32-bit系統下最大4G,64-bit下可以超過)
(4.2)若主要使用myisam存儲引擎,則設置最高不超過物理內存的20%~50%,
(4.4)即便全是innodb表,沒用MyISAM,也有必要設置key_buffer_size用於緩存臨時表的索引,推薦設置32MB
(4.5)關於臨時表,如果內存tmp_table_size(Created_tmp_tables)不夠的話,內部的臨時磁盤表是MyISAM表(Created_tmp_disk_tables)。show global status like 'Create%'; show variables like 'tmp%';

(5)query_cache_size :

 查詢高速緩沖,緩存結果,減少硬解析(建議關閉,如果真需要查詢緩存可以借助redis等緩存)
     <img src="c5b9e726-adee-410e-9f6b-704d860aab12_files/6ce17d74-b783-4c7b-8f3c-4f99a6f53876.png" border="0" alt="" name="">

(6)table_definition_cache:

   (6.1)表定義文件描述緩存,提高表打開效率。是frm文件在內存中的映射。MySQL需要打開frm文件,並將其內容初始化為Table Share 對象。這里存放與存儲引擎無關的,獨立的表定義相關信息。

(7)table_open_cache:

(7.1)表空間文件描述緩沖,提高表打開效率。
(7.2)增加table_open_cache,會增加文件描述符(ulimit -a查看系統的文件描述符),當把table_open_cache設置的過大時,如果系統處理不了這么多文件描述符,那么就會出現客戶端失效、連接不上。
(7.3)table_open_cache,也就是平時說的table cache。存放當前已經打開的表句柄,與表創建時指定的存儲引擎相關。請注意和table_define_cache參數的區別。

為什么MySQL會出現table_open_cahce和table_define_cache這兩個概念? 
是因為:MySQL支持不同的存儲引擎,每種存儲引擎,數據存儲的格式都是不一樣的,因此需要指定一個存儲引擎相關的handler。這就有了table cache的作用(table_open_cache參數)。另外表的定義也需要存放內存中,而表的定義frm文件每個存儲引擎是通用的,需要另外獨立開來,這就有了table definition cache。

(8)max_heap_table_size和tmp_table_size:

(8.1)max_heap_table_size 參數:定義了MEMORY、HEAP表的最大容量,如果內存不夠,則不允許寫入數據
(8.2)tmp_table_size參數:規定了內部內存臨時表的最大值,每個線程都要分配。(實際起限制作用的是tmp_table_size和max_heap_table_size的最小值。)如果內存臨時表超出了限制,MySQL就會自動地把它轉化為基於磁盤的MyISAM表,存儲在指定的tmpdir目錄下。
(8.3)優化查詢語句的時候,要避免使用臨時表,如果實在避免不了的話,要保證這些臨時表是存在內存中的,否則臨時表超過內存臨時表的限制,會自動轉化為基於磁盤的Myisam表。
2、線程內存(Thread buffer)

每個連接到MySQL服務器的線程都需要有自己的緩沖。大概需要立刻分配256K,甚至在線程空閑時,它們使用默認的線程堆棧,網絡緩存等。 
事務開始之后,則需要增加更多的空間。運行較小的查詢可能僅給指定的線程增加少量的內存消耗。 
如果對數據表做復雜的操作例如掃描、排序或者需要臨時表,則需分配大約read_buffer_size、sort_buffer_size,read_rnd_buffer_size,tmp_table_size大小的內存空間 
不過它們只是在需要的時候才分配,並且在那些操作做完之后就釋放了。 
 
(1)read_buffer_size:

是MySQL讀入緩沖區大小。對表進行順序掃描的請求將分配一個讀入緩沖區,MySQL會為它分配一段內存緩沖區。read_buffer_size變量控制這一緩沖區的大小。如果對表的順序掃描請求非常頻繁,        並且你認為頻繁掃描進行得太慢,可以通過增加該變量值以及內存緩沖區大小提高其性能。

(2)read_rnd_buffer_size:

是MySQL的隨機讀緩沖區大小。當按任意順序讀取行時(例如,按照排序順序),將分配一個隨機讀緩存區。進行排序查詢時,MySQL會首先掃描一遍該緩沖,以避免磁盤搜索,提高查詢速度,如果需要排序大量數據,可適當調高該值。但MySQL會為每個客戶連接發放該緩沖空間,所以應盡量適當設置該值,以避免內存開銷過大。

(3)sort_buffer_size:

是MySQL執行排序使用的緩沖大小。如果想要增加ORDER BY的速度,首先看是否可以讓MySQL使用索引而不是額外的排序階段。如果不能,可以嘗試增加sort_buffer_size變量的大小。

(4)join_buffer_size:

應用程序經常會出現一些兩表(或多表)Join的操作需求,MySQL在完成某些 Join 需求的時候(all/index join),為了減少參與Join的“被驅動表”的讀取次數以提高性能,需要使用到 Join Buffer 來協助完成 Join操作。當 Join Buffer 太小,MySQL 不會將該 Buffer 存入磁盤文件,而是先將Join Buffer中的結果集與需要 Join 的表進行 Join 操作,然后清空 Join Buffer 中的數據,繼續將剩余的結果集寫入此 Buffer 中,如此往復。這勢必會造成被驅動表需要被多次讀取,成倍增加 IO 訪問,降低效率。

(5)binlog_cache_size:

在事務過程中容納二進制日志SQL 語句的緩存大小。二進制日志緩存是服務器支持事務存儲引擎並且服務器啟用了二進制日志(—log-bin 選項)的前提下為每個客戶端分配的內存,注意,是每個Client 都可以分配設置大小的binlog cache 空間。如果系統中經常會出現多語句事務的話,可以嘗試增加該值的大小,以獲得更好的性能。當然,我們可以通過MySQL 的以下兩個狀態變量來判斷當前的binlog_cache_size 的狀況:Binlog_cache_use 和Binlog_cache_disk_use。“max_binlog_cache_size”:和"binlog_cache_size"相對應,但是所代表的是binlog 能夠使用的最大cache 內存大小。當我們執行多語句事務的時候,max_binlog_cache_size 如果不夠大的話,系統可能會報出“ Multi-statement transaction required more than 'max_binlog_cache_size' bytes ofstorage”的錯誤。
其中需要注意的是:table_cache表示的是所有線程打開的表的數目,和內存無關。

(6)tmp_table_size:

是MySQL的臨時表緩沖大小。所有聯合在一個DML指令內完成,並且大多數聯合甚至可以不用臨時表即可以完成。大多數臨時表是基於內存的(HEAP)表。具有大的記錄長度的臨時表 (所有列的長度的和)或包含BLOB列的表存儲在硬盤上。如果某個內部heap(堆積)表大小超過tmp_table_size,MySQL可以根據需要自動將內存中的heap表改為基於硬盤的MyISAM表。還可以通過設置tmp_table_size選項來增加臨時表的大小。也就是說,如果調高該值,MySQL同時將增加heap表的大小,可達到提高聯接查詢速度的效果。

(7)thread_stack :

主要用來存放每一個線程自身的標識信息,如線程id,線程運行時基本信息等等,我們可以通過 thread_stack 參數來設置為每一個線程棧分配多大的內存。 

(8)thread_cache_size:

如果我們在MySQL服務器配置文件中設置了thread_cache_size,當客戶端斷開之后,服務器處理此客戶的線程將會緩存起來以響應下一個客戶而不是銷毀(前提是緩存數未達上限)。

(9)net_buffer_length:

客戶發出的SQL語句期望的長度。如果語句超過這個長度,緩沖區自動地被擴大,直到max_allowed_packet個字節。

(10)bulk_insert_buffer_size:

如果進行批量插入,可以增加bulk_insert_buffer_size變量值的方法來提高速度,但是,這只能對myisam表使用。
3、overhead

(1)自適應哈希索引(Adaptive index hash)

(1.1)哈希索引是一種非常快的等值查找方法(注意:必須是等值,哈希索引對非等值查找方法無能為力),它查找的時間復雜度為常量,InnoDB采用自適用哈希索引技術,它會實時監控表上索引的使用情況,如果認為建立哈希索引可以提高查詢效率,則自動在內存中的“自適應哈希索引緩沖區”中建立哈希索引。
(1.2)之所以該技術稱為“自適應”是因為完全由InnoDB自己決定,不需要DBA人為干預。它是通過緩沖池中的B+樹構造而來,且不需要對整個表建立哈希索引,因此它的數據非常快。
(1.3)InnoDB官方文檔顯示,啟用自適應哈希索引后,讀和寫性能可以提高2倍,對於輔助索引的連接操作,性能可以提高5被,因此默認情況下為開啟,可以通過參數innodb_adaptive_hash_index來禁用此特性。
(1.4)哈希索引總是基於表上已存在的B樹索引來建立的。InnoDB會在為該B樹定義的鍵的一個前綴上建立哈希索引,不管該鍵有多長。哈希索引可以是部分的:它不要求整個B樹索引被緩存在緩沖池中。InnoDB根據需要對被經常訪問索引的那些頁面建立哈希索引。

(2)System dictionary hash 
(3)Locking system 
(4)Sync_array 
(5)Os_events

三、MySQL文件結構

1、參數文件

2、錯誤日志文件

3、二進制日志文件

二進制日志的開啟:log-bin=/httx/run/mysql/data/mysql-bin
設置二進制日志模式為row模式:binlog_format=ROW 

查看row模式日志的方法: 
mysqlbinlog –base64-output=decode-rows -vv mysql-bin.000001 ,其中–base64-output=decode-rows是將原編碼過的日志轉碼;而-vv則時以注釋形式顯示做過的SQL操作

4、慢查詢日志

show variables like 'long_query_time';默認的慢查詢日志的閥值是10秒,也就是查詢時長超過10秒就會記錄到慢查詢日志文件;
慢查詢日志的啟動:mysql5.6開啟慢日志需要同時設置兩個參數:slow_query_log=ON,slow_query_log_file=/path to/slow.log。其中 slow_query_log表示開啟慢日志,如果不設置slow_query_log而只是定義slow_query_log_file慢日志文件路徑,慢日志設置是不生效的。
5、Genaral日志

啟動general log和慢日志類似,需要同時設置兩個參數:general_log=ON開啟general日志;general_log_file=/path to/general.log設置general日志的文件路徑。
6、Redo log

(1)redo log的作用簡介:InnoDB有buffer pool(簡稱bp或IBP)。bp是數據庫頁面的緩存,對InnoDB的任何修改操作都會首先在bp的page上進行,然后這樣的頁面將被標記為dirty(臟頁)並被放到專門的flush list上,后續將由master thread或專門的刷臟線程階段性的將這些頁面寫入磁盤(disk or ssd)。這樣的好處是避免每次寫操作都操作磁盤導致大量的隨機IO,階段性的刷臟可以將多次對頁面的修改merge成一次IO操作,同時異步寫入也降低了訪問的時延。
然而,如果在dirty page還未刷入磁盤時,server非正常關閉,這些修改操作將會丟失,如果寫入操作正在進行,甚至會由於損壞數據文件導致數據庫不可用。為了避免上述問題的發生,Innodb將所有對頁面的修改操作寫入一個專門的文件,並在數據庫啟動時從此文件進行恢復操作,這個文件就是redo log file。這樣的技術推遲了bp頁面的刷新,從而提升了數據庫的吞吐,有效的降低了訪問時延。帶來的問題是額外的寫redo log操作的開銷(順序IO,當然很快),以及數據庫啟動時恢復操作所需的時間。
(2)redo log相關的參數:

innodb_log_file_size參數:定義ib_logfile*文件的大小,默認是50331648(48M),ib_logfile*就是redo log文件;
innodb_log_files_in_group參數:定義redo log的個數,innodb_log_files_in_group=2時,則會有ib_logfile0、ib_logfile1兩個redo log文件,這兩個ib_logfile文件順序寫、循環寫;
innodb_log_group_home_dir參數:定義redo log的存放路徑,也即是ib_logfile的存放路徑。

(3)binlog和redo log的關系(日志記錄流程):
binlog和redo log的正常協同流程:mysql在接收一個SQL請求后,首先記錄binlog,同時binlog會通知innodb准備變更數據(例如,修改t1表的id=100的記錄),innodb在把id=100所在的page拉取到高速緩沖區,此時,redo log中會記錄開始標簽,日志記錄完成后返回到server(此時跟engine沒有關系了),在redo log中記錄結束標簽;
主機宕機下binlog和redo log的協同流程:binlog完整記錄了變更數據,redo log記錄開始標簽,但是日志記錄還沒來得及返回給server就宕機了,主機重啟后,會比較binlog和redo log是否一致,由於redo log沒有結束標簽,數據庫進行恢復操作。redo log根據開始標簽將id=100的記錄所在page拉取到高速緩沖區,然后返回給server,並記錄結束標簽。
7、Pid文件
mysql實例的進程ID文件
8、Socket文件
當用unix套接字方式進行連接時需要的文件
9、MySQL表結構文件
.frm后綴命名的文件都是表結構文件,和存儲引擎類型無關。所有的表都會生成一個.frm文件;
10、innodb數據文件

 
(1)共享表空間:共享表空間文件以.ibdata*來命名; 共享表空間下,innodb所有數據保存在一個單獨的表空間里面,而這個表空間可以由很多個文件組成,一個表可以跨多個文件存在,所以其大小限制不再是文件大小的限制,而是其自身的限制。從Innodb的官方文檔中可以看到,其表空間的最大限制為64TB,也就是說,Innodb的單表限制基本上也在64TB左右了,當然這個大小是包括這個表的所有索引等其他相關數據。 
共享表空間主要存放double write、undo log(undo log沒有獨立的表空間,需要存放在共享表空間) 
(2)獨立表空間:每個表擁有自己獨立的表空間用來存儲數據和索引。 
(3)查看數據庫是否啟用獨立表空間: 
show variables like ‘innodb_file_per_table’;查看,innodb_file_per_table=ON,表示啟用了獨立表空間; 
(4)使用獨立表空間的優點: 
如果使用軟鏈接將大表分配到不同的分區上,易於管理數據文件 
易於監控解決IO資源使用的問題; 
易於修復和恢復損壞的數據; 
相互獨立的,不會影響其他innodb表; 
導出導入只針對單個表,而不是整個共享表空間; 
解決單個文件大小的限制; 
對於大量的delete操作,更易於回收磁盤空間; 
碎片較少,易於整理optimize table; 
易於安全審計; 
易於備份 
如果在innodb表已創建后設置innodb_file_per_table,那么數據將不會遷移到單獨的表空間上,而是續集使用之前的共享表空間。只有新創建的表才會分離到自己的表空間文件。 
(5)共享表空間的數據文件配置: 
innodb_data_file_path參數:設置innoDB共享表空間數據文件的名字和大小,例如innodb_data_file_path=ibdata1:12M:autoextend(初始大小12M,不足自增) 
innodb_data_home_dir參數:innodb引擎的共享表空間數據文件的存放目錄 
目前主要是使用獨立表空間,但是共享表空間也是需要的,共享表空間主要存放double write、undo log等

11、MYISAM文件

四、MySQL存儲結構(Innodb存儲結構)

1、從物理意義上來講,InnoDB表由共享表空間、日志文件組(redo log文件組)、表結構定義文件(.frm文件)組成。若將innodb_file_per_table設置為on,則系統將為每一個表單獨的生成一個table_name.ibd的文件,在此文件中,存儲與該表相關的數據、索引、表的內部數據字典信息。
表結構文件則以.frm結尾,.frm文件和存儲引擎無關。

 

(1)每頁=16Kb(頁類型:數據頁、undo頁、系統頁、事務數據頁、插入緩沖位圖頁、插入緩沖空閑列表頁、未壓縮的二進制大對象頁、壓縮的二進制大對象頁)
(2)區=64個連續的頁=64*16Kb=1MB

2、行模式類型: 
 
3、行溢出 

五、innodb體系結構

 
上圖中,沒有畫出purge thread和page cleaner thread,mysql5.6版本后,這2個線程從master thread里獨立出來,緩解master thread的壓力

1、主要的后台線程

(1)master thread

master thread是一個非常核心的后台線程,主要負責將緩沖池中的數據異步刷新到磁盤,保證數據的一致性,包括:臟頁(dirty page)的刷新、合並插入緩沖(insert buffer merge)、回滾頁回收(undo purge)等。 
 
1、Master thread線程的優先級最高,內部主要是4個循環loop組成:主循環、后台循環、刷新循環、暫停循環。 
2、在master thread線程里,每1秒或每10秒會觸發1oop(循環體)工作,loop為主循環,大多數情況下都運行在這個循環體。loop通過sleep()來實現定時的操作,所以操作時間不精准。負載高的情況下可能會有延遲; 
3、dirty page:當事務(Transaction)需要修改某條記錄(row)時,InnoDB需要將該數據所在的page從disk讀到buffer pool中,事務提交后,InnoDB修改page中的記錄(row)。這時buffer pool中的page就已經和disk中的不一樣了,我們稱buffer pool中的被修改過的page為dirty page。Dirty page等待flush到disk上。 
4、insert buffer merge: 
innodb使用insert buffer”欺騙”數據庫:對於為非唯一索引,輔助索引的修改操作並非實時更新索引的葉子頁,而是把若干對同一頁面的更新緩存起來做合並(merge)為一次性更新操作,轉化隨機IO 為順序IO,這樣可以避免隨機IO帶來性能損耗,提高數據庫的寫性能。 
(1)Insert Buffer是Innodb處理非唯一索引更新操作時的一個優化。最早的Insert Buffer,僅僅實現Insert操作的Buffer,這也是Insert Buffer名稱的由來。在后續版本中,Innodb多次對Insert Buffer進行增強,到Innodb 5.5版本,Insert Buffer除了支持Insert,還新增了包括Update/Delete/Purge等操作的buffer功能,Insert Buffer也隨之更名為Change Buffer。 
(2)insert buffer merge分為主動給merge和被動merge。 
(2.1)master thread線程里的insert buffer merge是主動merge,原理是:a、若過去1秒內發生的IO小於系統IO能力的5%,則主動進行一次insert buffer merge(merge的頁面數為系統IO能力的5%且讀取page采用async io模式)。 b、每10秒,必須觸發一次insert buffer merge(merge的頁面數仍舊為系統IO能力的5%) 
(2.2)被動Merge,則主要是指在用戶線程執行的過程中,由於種種原因,需要將insert buffer的修改merge到page之中。被動Merge由用戶線程完成,因此用戶能夠感知到merge操作帶來的性能影響。例如:a、Insert操作,導致頁面空間不足,需要分裂。由於insert buffer只能針對單頁面,不能buffer page split,因此引起頁面的被動Merge; 
b、insert操作,由於其他各種原因,insert buffer優化返回失敗,需要真正讀取page時,也需要進行被動Merge;c、在進行insert buffer操作時,發現insert buffer已經太大,需要壓縮insert buffer。 
5、check point:

(1)checkpoint干的事情:將緩沖池中的臟頁刷新到磁盤,不同之處在於每次從哪里取多少臟頁刷新到磁盤,以及什么時候觸發checkpoint。
(2)checkpoint解決的問題:
a、縮短數據庫的恢復時間(數據庫宕機時,不需要重做所有的日志,因checkpoint之前的頁都已經刷新回磁盤啦)
b、緩沖池不夠用時,將臟頁刷新到磁盤(緩沖池不夠用時,根據LRU算法算出最近最少使用的頁,若此頁為臟頁,需要強制執行checkpoint將臟也刷回磁盤)
c、重做日志不可用時,刷新臟頁(采用循環使用的,並不是無限增大。當重用時,此時的重做日志還需要使用,就必須強制執行checkpoint將臟頁刷回磁盤)
(2)IO thread

在innodb存儲引擎中大量使用AIO來處理IO請求,這樣可以極大提高數據庫的性能,而IO thread的工作就是負責這些IO請求的回調處理(call back); 
 
小知識 
1、聚集索引: 
聚集索引不是一種單獨的索引類型,而是一種存儲數據方式。其具體細節依賴於實現方式,但是InnoDB的聚集索引實際上在同樣的結構中保存了B+Tree索引和數據行。 
InnoDB的索引屬於聚集索引,就是說表數據文件和索引文件都是同一個,表數據的分布按照主鍵排序,以B+TREE數據格式存儲; 
MyISAM引擎的索引屬於非聚集索引,索引文件跟數據文件是分開的。而索引文件的所指向的是對應數據的物理地址。 
2、輔助索引:非聚集索引: 
3、innodb_change_buffer_max_size:如果是日志類服務,可以考慮把這把這個增值調到50 
4、innodb_change_buffering:默認即可

(3)lock monitor thread
(4)error monitor thread
(5)purge thread

1、事務被提交后,其所使用的undo log可能將不再需要,因此需要purge thread來回收已經使用並分配的undo頁; 
2、從mysql5.5開始,purge操作不再做主線程的一部分,而作為獨立線程。 
3、開啟這個功能:innodb_purge_threads=1。調整innodb_purge_batch_size來優化purge操作,batch size指一次處理多少undo log pages, 調大這個參數可以加塊undo log清理(類似oracle的undo_retention)。 
從mysql5.6開始,innodb_purge_threads調整范圍從0–1到0–32,支持多線程purge,innodb-purge-batch-size會被多線程purge共享 

(6)page cleaner thread

page cleaner thread是在innodb1.2.x中引用的,作用是將之前版本中臟頁的刷新操作都放入到單獨的線程中來完成,其目的是為了減輕master thread的工作及對於用戶查詢線程的阻塞,進一步提高innodb存儲引擎的性能。 
1、將dirty page刷新到磁盤。 
2、兩種算法:

(1)LRU算法:基於lru list(最后訪問的時間排序)的刷新順序;
(2)adaptive算法:基於flush list(最后修改時間的順序)的刷新順序;

3、innodb_adaptive_flushing=1,該值影響每秒刷新臟頁的操作,開啟此配置后,刷新臟頁會通過判斷產生重做日志的速度來判斷最合適的刷新臟頁的數量; 
4、innodb_flush_neighbors=1,InnoDB存儲引摯還提供了flush Neighbor page(刷新鄰接頁)的特性。InnoDB存儲引摯從1.2.x版本開始提供了參數innodb_flush_neighbors,用來控制是否開啟這個特性。 
對於傳統的機械硬盤建議開啟該特性; 
對於固態硬盤有着超高的IOPS性能,則建議將該參數設置為0,即關閉此特性,因為使用順序IO沒有任何性能收益. 在使用RAID的某些硬件上也應該禁用此設置,因為邏輯上連續的塊在物理磁盤上並不能保證也是連續的。 
其工作原理為:當刷新一個臟頁時,InnoDB存儲引摯會檢測該頁所在區(extent)的所有頁,如果是臟頁,那么一起進行刷新。這樣做的好處顯而易見,通過AIO可以將多個IO寫入操作合並為一個IO操作,故該工作機制在傳統的機械硬盤下有着顯著的優勢。

其他注意小點

(1)adaptive hash index(AHI):

提高buffer pool遍歷page的效率O(1) VS O(B+Tree高度);AHI會自動對buffer pool熱點數據創建AHI(非持久化);只支持等值查詢;加入AHI的條件是:索引是否被訪問17次以上+索引中某個頁已經被訪問至少100次。

(2)double write:

innodb的page size一般是16K,在極端情況下(例如斷電)往往並不能保證這一操作的原子性,例如:16K數據,寫入4K時突然發生系統斷電/os crash,只有一部分寫是成功的,這種情況下就是partial page write(部分頁寫入);
為了解決如上問題,當mysql將臟數據flush到data file的時候,先使用memcopy將臟數據復制到內存中的double write buffer,之后通過double wirte buffer再分2次,每次寫入1M到共享表空間,然后馬上調用fsync函數,同步到磁盤上,避免緩沖帶來的問題,這個過程中double write是順序寫,開銷並不大,在完成double write寫入后,將double write buffer寫入各表空間文件,這時是離散寫入。


doubule write在刷臟頁過程中在哪個步驟?當有數據操作時,有如下簡述過程:
將數據頁(page)加載到內存(innodb buffer)——>更新數據產生臟頁(dirty page)——?>使用memcopy將臟數據復制到內存中的double write buffer(size=2M)
———>double wirte buffer再分2次,每次寫入1M到共享表空間(ibdata文件)——>調用fsync函數,同步到磁盤.
其中:使用memcopy將臟數據復制到內存中的double write buffer(size=2M)———>double wirte buffer再分2次,每次寫入1M到共享表空間(ibdata文件)就是double的過程。

 

(3)緩沖池:

(3.1)buffer pool:通過參數innodb_buffer_pool_size來設置buffer pool,是innodb參數優化最重要的參數,也是使用內存最大的區域。用來存放各種數據的緩存包括:索引頁、數據頁、undo頁、插入緩沖、自適應哈希索引、innodb存儲的鎖信息、數據字典信息等。將數據庫文件按頁(page size=16K)讀取到緩沖池,然后按LRU算法來保留在緩沖池中的緩存數據,如果數據文件需要修改,總是先修改在緩沖池中的頁(修改后的即為dirty page),然后按照一定的頻率將緩沖池的臟頁刷新到文件(磁盤)。
(3.2)日志緩存區(redo log buffer):參數innodb_log_buffer_size來設置innodb的log緩存,將redo log日志信息先放入這個緩沖區,然后按照一定頻率刷新到重做日志文件,刷新磁盤的算法由innodb_flush_log_at_trx_commit參數控制。
(3.3)額外內存池(additional memory pool):innodb_additional_mem_poop_size是innodb用來保存數據字典信息和其他內部數據結構的內存池的大小,單位是byte,默認值是8M。

====================================================================================================

補充

undo redo

1、undo

Undo Log 是為了實現事務的原子性(事物里的操作要么都完成,要么都不完成),在MySQL數據庫InnoDB存儲引擎中,還用Undo Log來實現多版本並發控制(簡稱:MVCC)。

(1)undo的原理

為了滿足事務的原子性,在操作任何數據之前,首先將數據備份到一個地方(也就是Undo Log,undo日志存放在共享表空間里),然后進行數據的修改。如果出現了錯誤或者用戶執行了ROLLBACK語句,系統可以利用Undo Log中的備份將數據恢復到事務開始之前的狀態。 
除了可以保證事務的原子性,Undo Log也可以用來輔助完成事務的持久化(事務一旦完成,該事務對數據庫所做的所有修改都會持久的保存到數據庫中)

(2)用Undo Log實現原子性和持久化的事務的簡化過程
  假設有A、B兩個數據,值分別為1,2。
  A.事務開始.
  B.記錄A=1到undo log.
  C.修改A=3.
  D.記錄B=2到undo log.
  E.修改B=4.
  F.將undo log寫到磁盤。
  G.將數據寫到磁盤。
  H.事務提交
  這里有一個隱含的前提條件:‘數據都是先讀到內存中,然后修改內存中的數據,最后將數據寫回磁盤’。

之所以能同時保證原子性和持久化,是因為以下特點:

  A. 更新數據前記錄Undo log(undo log存放在共享表空間里)。
  B. 為了保證持久性,必須將數據在事務提交前寫到磁盤。只要事務成功提交,數據必然已經持久化。
  C. Undo log必須先於數據持久化到磁盤。如果在G,H之間系統崩潰,undo log是完整的,可以用來回滾事務。
  D. 如果在A-F之間系統崩潰,因為數據沒有持久化到磁盤。所以磁盤上的數據還是保持在事務開始前的狀態。
缺陷:每個事務提交前將數據和Undo Log寫入磁盤,這樣會導致大量的磁盤IO,因此性能很低。

2、redo

如果能夠將數據緩存一段時間,就能減少IO提高性能。但是這樣就會喪失事務的持久性。因此引入了另外一種機制來實現持久化,即Redo Log.

(1)redo原理

和Undo Log相反,Redo Log記錄的是新數據的備份,在事務提交前,只要將Redo Log持久化即可,不需要將數據持久化。當系統崩潰時,雖然數據沒有持久化,但是Redo Log已經持久化。系統可以根據Redo Log的內容,將所有數據恢復到最新的狀態。

(2)Undo + Redo事務的簡化過程
  假設有A、B兩個數據,值分別為1,2.
  A.事務開始.
  B.記錄A=1到undo log.
  C.修改A=3.
  D.記錄A=3到redo log.
  E.記錄B=2到undo log.
  F.修改B=4.
  G.記錄B=4到redo log.
  H.將redo log寫入磁盤。
  I.事務提交

 Undo + Redo事務的特點:
  A. 為了保證持久性,必須在事務提交前將Redo Log持久化。
  B. 數據不需要在事務提交前寫入磁盤,而是緩存在內存中。
  C. Redo Log 保證事務的持久性。
  D. Undo Log 保證事務的原子性。
  E. 有一個隱含的特點,數據必須要晚於redo log寫入持久存儲。
  • Undo + Redo的設計主要考慮的是:提升IO性能。雖說通過緩存數據,減少了寫數據的IO,但是卻引入了新的IO,即寫Redo Log的IO。如果Redo Log的IO性能不好,就不能起 到提高性能的目的。*

為了保證Redo Log能夠有比較好的IO性能,InnoDB 的 Redo Log的設計有以下幾個特點:

  A. 盡量保持Redo Log存儲在一段連續的空間上。因此在系統第一次啟動時就會將日志文件的空間完全分配(也即是ib_logfile*文件,初始化實例時就分配好空間了)。以順序追加的方式記錄Redo Log,通過順序IO來改善性能。
  B. 批量寫入日志。日志並不是直接寫入文件,而是先寫入redo log buffer.當需要將日志刷新到磁盤時(如事務提交),將許多日志一起寫入磁盤.
  C. 並發的事務共享Redo Log的存儲空間,它們的Redo Log按語句的執行順序,依次交替的記錄在一起,以減少日志占用的空間。例如,Redo Log中的記錄內容可能是這樣    的:
     記錄1: <trx1, insert …>
     記錄2: <trx2, update …>
     記錄3: <trx1, delete …>
     記錄4: <trx3, update …>
     記錄5: <trx2, insert …>
  D. 因為C的原因,當一個事務將Redo Log寫入磁盤時,也會將其他未提交的事務的日志寫入磁盤。
  E. Redo Log上只進行順序追加的操作,當一個事務需要回滾時,它的Redo Log記錄也不會從Redo Log中刪除掉。
(3)、恢復(Recovery)

(3.1)恢復策略 
未提交的事務和回滾了的事務也會記錄Redo Log,因此在進行恢復時,這些事務要進行特殊的處理。有2中不同的恢復策略: 
A. 進行恢復時,只重做已經提交了的事務。 
B. 進行恢復時,重做所有事務包括未提交的事務和回滾了的事務。然后通過Undo Log回滾那些未提交的事務。

(3.2)InnoDB存儲引擎的恢復機制 
MySQL數據庫InnoDB存儲引擎使用了B策略(進行恢復時,重做所有事務包括未提交的事務和回滾了的事務。然后通過Undo Log回滾那些未提交的事務), InnoDB存儲引擎中的恢復機制有幾個特點: 
A. 在重做Redo Log時,並不關心事務性。 恢復時,沒有BEGIN,也沒有COMMIT,ROLLBACK的行為。也不關心每個日志是哪個事務的。盡管事務ID等事務相關的內容會記入Redo Log,這些內容只是被當作要操作的數據的一部分。 
B. 使用B策略就必須要將Undo Log持久化,而且必須要在寫Redo Log之前將對應的Undo Log寫入磁盤。Undo和Redo Log的這種關聯,使得持久化變得復雜起來。為了降低復雜度,InnoDB將Undo Log看作數據,因此記錄Undo Log的操作也會記錄到redo log中。這樣undo log就可以象數據一樣緩存起來,而不用在redo log之前寫入磁盤了。 
包含Undo Log操作的Redo Log,看起來是這樣的: 
記錄1: <trx1, Undo log insert <undo_insert>> 
記錄2: 
記錄3: <trx2, Undo log insert <undo_update>> 
記錄4: 
記錄5: <trx3, Undo log insert <undo_delete>> 
記錄6: 
C. 到這里,還有一個問題沒有弄清楚。既然Redo沒有事務性,那豈不是會重新執行被回滾了的事務?確實是這樣。同時Innodb也會將事務回滾時的操作也記錄到redo log中。回滾操作本質上也是對數據進行修改,因此回滾時對數據的操作也會記錄到Redo Log中。 
一個回滾了的事務的Redo Log,看起來是這樣的: 
記錄1: <trx1, Undo log insert <undo_insert>> 
記錄2: 
記錄3: <trx1, Undo log insert <undo_update>> 
記錄4: 
記錄5: <trx1, Undo log insert <undo_delete>> 
記錄6: 
記錄7: 
記錄8: 
記錄9: 
一個被回滾了的事務在恢復時的操作就是先redo再undo,因此不會破壞數據的一致性.

==================================================================================================

 


免責聲明!

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



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