本文絕大部份內容來源《MySQL技術內幕:InnoDB存儲引擎》一書,部分圖片來源網絡。#我是搬運工#html
InnoDB存儲引擎是多線程模型,其後臺有多個不一樣的後臺線程,負責處理不一樣的任務。mysql
Master Thread 主要負責將緩存池中的數據異步刷新到磁盤,保證數據的一致性,包括髒頁的刷新、合併插入緩衝(INSERT BUFFER)、UNDO頁的回收。算法
IO Thread 主要負責 Async IO 請求的回調處理,包含 write、read、insert buffer 和 log IO thread。sql
Purge Thread 負責回收已經使用並分配的 undo 頁,減輕 Master Thread 的工做。shell
Page Cleaner Thread 做用是將以前版本中髒頁的刷新操做都放入到單獨的線程中來完成,減輕 Master Thread 的工做及對於用戶查詢線程的阻塞。數據庫
一塊內存區域,經過內存的速度來彌補磁盤速度較慢對數據庫性能的影響;讀取數據時,首先將從磁盤讀到的數據存放在緩衝池中,下一次讀取直接從緩衝池中取。更新數據時,先更顯緩衝池的數據,而後經過後臺線程按期將有過更新的緩衝數據刷新到磁盤。從而減小磁盤IO的讀寫。緩存
數據庫緩衝池經過 LRU (Last Recent Used) 算法管理,LRU List 用來管理已經讀取的頁,數據庫啓動時,LRU List 爲空列表,沒有任何的頁。此時頁都存放在 Free List 中,當須要從緩衝池中分頁時,首先從 Free List 中查找是否有可用的空閒頁,如有空閒頁則將該頁從 Free 列表中刪除並可以放入到 LRU List 中,不然淘汰 LRU List 中末尾的頁。在 LRU List 中的頁被修改後,稱該頁爲髒頁(dirty page)。髒頁存儲於 Flush List,表示緩衝池中的頁與磁盤頁不一致,等待被調度刷新。髒頁同時存在於 Flush List 與 LRU List 中。服務器
InnoDB 將重作日誌首先寫入 redo buffer cache,以後經過必定頻率寫入到重作日誌(redo logo)中。redo buffer cache 不須要設置太大,重作日誌緩衝在一下狀況下被刷入到重作日誌文件中:
(1) Master Thread 每一秒將重作日誌緩衝刷到重作日誌文件
(2) 每一個事務提交時會將重作日誌緩衝刷新到重作日誌文件
(3) 當重作日誌緩衝池剩餘空間小於50%時,重作日誌緩衝刷新到重作日誌網絡
InnoDB 對內存的管理是經過一種稱爲內存堆的方式進行的,對一些數據結構進行內存分配時,須要從額外的內存池中申請,當該區域不夠時,會從緩衝池中進行申請。數據結構
對於【非彙集索引】的更新或插入操做,不是直接插入到索引頁中,而是先判斷插入的非彙集索引頁是否在緩衝池中,若在,則直接插入,不然先放入到一個 Insert Buffer 中。再以必定頻率和狀況進行 Insert Buffer 和輔助索引頁子節點的merge操做,合併插入操做,提升非彙集索引的插入性能。
Insert Buffer 的升級,InnoDb 1.0.x 版本開始引入,一樣適用對象爲非惟一的輔助索引。能夠對 DML 操做進行緩衝:insert、delete、update。
double write 帶給 InnoDB 存儲引擎的是數據頁的可靠性。
當數據庫發生宕機時,可能InnoDB存儲引擎正在寫入某個頁到列表中,而這個頁只寫了一部分,,好比16KB的頁,只寫了4KB,以後發生宕機,此時次出現【部分寫失效】(頁斷裂)的狀況,InnoDB 經過 double write 解決出現這種狀況時形成的數據丟失而且沒法恢復的問題。
double write 工做流程:髒頁刷新時,先拷貝至內存的 double write buffer,從緩衝區分兩次接入磁盤共享表空間紅,順序寫,緩衝區中的髒頁數據寫入實際的各個表空間,離散寫。
頁斷裂數據恢復流程:經過頁的 checksum,校驗 double write 在磁盤中的數據塊,經過 double write 緩衝區數據來修復。
InnoDB 會監控對錶上各索引頁的查詢。若是觀察到建議哈希索引能夠帶來速度的提高,則創建哈希索引,稱之爲自適應哈希索引(AHI)。AHI 是經過緩衝池的 B+ 樹構造來的,所以創建的速度很是快,並且不須要對整張表構建哈希索引,InnoDB 會根據訪問頻率和模式來自動建立自適應哈希索引,無需人爲設置干預。
自適應哈希索引只適用於等值查詢,好比 where smsId = 'XXXXXX',不支持範圍查找。
InnoDB 採用異步IO(AIO)的方式來處理磁盤操做,進而提升對磁盤的操做性能。InnoDB 存儲引擎中,read ahead 方式的讀取是經過 AIO 完成,髒頁的刷新,即磁盤的寫入操做也是由 AIO 完成。
當刷新一個髒頁到磁盤時,InnoDB 會檢測該頁所在區的全部頁,若是是髒頁,則一塊兒進行刷新。經過 AIO 合併多個 IO 寫入,減小磁盤的 IO,可是可能形成將不怎麼髒的頁的磁盤寫入,對於 SSD 磁盤,自己有着較高的 IOPS,則建議關閉該特性,InnoDB 1.2.x 版本提供參數 innodb_flush_neighbors,設置爲 0 可關閉該特性。而對於普通磁盤,建議開啓。
Master Thread 以每秒或每十秒的速度從緩衝池的髒頁列表中刷新必定比例的頁到磁盤,異步進行,用戶查詢線程不會阻塞。
InnoDB 存儲引擎需保證差很少 100 個空閒頁可用,空閒也不足時,InnoDB 會將 LRU 列表尾端的頁移除,若是尾端頁存在髒頁,則須要進行 Checkpoint。
重作日誌不可用時進行,強制將一些頁刷新回磁盤,從髒頁列表中選取。根據不一樣的狀態使用不一樣的刷新方式(同步或異步)。
髒頁數量太多,好比佔據緩衝池比例大於 75% 時,強制進行刷新,比例可調。
告訴 MySQL 實例啓動時在哪裏能夠找到數據庫文件,而且指定某些初始化參數,這些參數定義了某種內存結構的大小等設置。在默認狀況下,MySQL 實例會按照必定的順序在指定的位置進行讀取,經過如下命令能夠尋找:
mysql --help | grep my.cnf
記錄了影響 MySQL 數據庫的各類類型活動,常見的日誌文件有:
對 MySQL 的啓動、運行、關閉過程進行記錄,可根據錯誤日誌定位問題。不只記錄錯誤信息,同時也記錄一些告警信息或正確的信息。
# 查看日誌文件存儲路徑 mysql> show variables like 'log_error'; +---------------+--------------------------------+ | Variable_name | Value | +---------------+--------------------------------+ | log_error | /data/mysql_data/data/r002.err | +---------------+--------------------------------+
幫助查找存在問題的 SQL 語句,記錄執行時間超過某個時間長度的 SQL 語句。
# 查詢記錄執行時間長度(秒) mysql> show variables like 'long_query_time' \g; +-----------------+----------+ | Variable_name | Value | +-----------------+----------+ | long_query_time | 1.000000 | +-----------------+----------+ # 慢查詢記錄開關 mysql> show variables like 'log_slow_queries' \g; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | log_slow_queries | ON | +------------------+-------+
慢查詢日誌文件可經過 mysqldumpslow 解析結果並查看。
查詢日誌記錄了全部對 MySQL 數據庫請求的信息,不管這些請求是否獲得了正確的執行。默認文件名爲:主機名.log。
二進制日誌(binary log)記錄了對 MySQL 數據庫執行更改的全部操做,不包含只讀操做。二進制文件主要有如下幾種做用:
在 UNIX 系統下本地鏈接 MySQL 能夠採用 UNIX 域套接字方式。
# 查看套接字文件地址 mysql> show variables like 'socket'; +---------------+-----------------------+ | Variable_name | Value | +---------------+-----------------------+ | socket | /var/mysql/mysql.sock | +---------------+-----------------------+
在 MySQL 實例啓動時,生成的進程ID會被寫入到一個文件中,即 pid 文件。
# 查看 pid 文件 mysql> show variables like 'pid_file'; +---------------+--------------------------------+ | Variable_name | Value | +---------------+--------------------------------+ | pid_file | /data/mysql_data/data/r002.pid | +---------------+--------------------------------+
MySQL 數據的存儲是根據表進行的,每一個表都有與之對應的文件,是以 frm 爲後綴名的文件,記錄了表的表結構定義。
InnoDB 存儲引擎獨有的文件,與InnoDB 存儲引擎密切相關,包括表空間文件、重作日誌文件。
InnoDB 採用將存儲的數據按表空間進行存放的設計。默認配置下有一個初始化大小爲 10MB 的 ibdata1 文件,可自動增加。能夠經過參數 innodb_data_file_path 對其進行設置。若設置了參數 innodb_file_per_table,則用戶能夠將每一個基於 InnoDB 存儲引擎的表產生一個獨立表空間。獨立表空間命名規則:表名.ibd
# 查看是否開啓獨立表空間存儲 mysql> show variables like 'innodb_file_per_table'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | innodb_file_per_table | ON | +-----------------------+-------+
須要注意的是,這些單獨的表空間文件僅存儲該表的數據、索引和插入緩衝 BITMAP 等信息,其他信息仍是存放在默認表空間中。下圖顯示了 InnoDB 存儲引擎對於文件的存儲方式:
默認狀況下,InnoDB 存儲引擎的數據目錄下會有兩個名爲 ib_logfile0 和 ib_logfile1 的文件,即 InnoDB 存儲引擎的重作日誌文件(redo log file),記錄了對於 InnoDB 存儲引擎的事務日誌。
當實例或介質存儲失敗時,例如因爲主機斷電致使實例失敗,InnoDB 存儲引擎會使用重作日誌恢復到斷電前的時刻,一次來保證數據的完整性。InnoDB 存儲引擎會逐個循環寫日誌文件,當前寫的日誌文件被寫滿後,切到下一個日誌文件,當下一個日誌文件也被寫滿後,循環寫前一個日誌文件。日誌文件數量及大小可配置(innodb_log_files_in_group、innodb_log_file_size)。
關於重作日誌文件的大小設置:
(1) 不能設置太大,若是設置得很大,在恢復時可能須要很長的時間
(2) 不能設置太小,可能會致使一個事務的日誌須要屢次切換重作日誌文件,也會致使頻繁的發生 async checkpoint,致使性能抖動。
寫入重作日誌文件的操做不是直接寫,而是先寫入一個重作日誌緩衝(redo log buffer)中,而後按照必定的順序寫入日誌文件:
MySQL 二進制文件記錄 MySQL 數據庫執行的更新操做。包含二進制日誌文件和二進制索引文件。
mysql-bin.index mysql-bin.000001 mysql-bin.000002 mysql-bin.XXXXXX
mysql-bin.000001 即爲二進制日誌文件,日誌文件超過必定大小(根據 max_binlog_size 肯定)時生成新的文件,後綴名 +1。binlog 相關的參數有以下:
max_binlog_size # 單個 binlog 日誌文件的最大值 binlog_cache_size # 事務提交時的二進制日誌寫入的緩衝區大小 sync_binlog # 表示每寫入多少次緩衝區就同步至磁盤 binlog-do-db # 表示須要寫入哪些庫的日誌 binlog-ignore-db # 表示忽略寫入哪些庫的日誌 bin_log_format # 表示二進制日誌的記錄格式,包含 STATEMENT、ROW、MIXED
STATEMENT:MySQL 5.1 以前的存儲格式,5.1 版本之後可選格式,記錄日誌的邏輯 SQL 語句。
ROW:二進制日誌再也不是簡單的 SQL 語句,而是記錄錶行的更改狀況,包含一行數據更改前與更改後列的內容。
MIXED:默認採用 STATEMENT 格式進行二進制日誌文件的記錄,可是在一些狀況下回使用 ROW 格式,如如下狀況:
1)表的存儲引擎爲 NDB
2)使用了 UUID()、USER()、CURRENT_USER()、FOUND_ROWS()、ROW_COUNT() 等不肯定函數
3)使用了 INSERT DELAY 語句
4)使用了用戶定義函數(UDF)
5)使用了臨時表
從 InnoDB 存儲引擎的邏輯存儲結構看,全部數據都被邏輯的存放在表空間(tablespace),表空間又分爲段(segment)、區(extend)、頁(page)組成。而表的行數據則存儲在頁中,一個頁存儲多個行。
MySQL 數據庫在 5.1 版本時添加了對分區的支持。分區功能並非在存儲引擎層完成的,所以不只 InnoDB 存儲引擎支持分區,MyISAM、NDB 等都支持。也並非全部存儲引擎都支持分區,如 CSV、MERGE、FEDORATED 等就不支持。
對一部分 SQL 語句性能帶來明顯的提升,可是分區主要用於數據庫高可用性的管理。
行數據基於屬於一個給定連續區間的列值被放入分區。
mysql> create table test_range (id int) ENGINE = INNODB PARTITION BY RANGE (id)( -> PARTITION P0 VALUES LESS THAN (10), -> PARTITION P1 VALUES LESS THAN (20)); Query OK, 0 rows affected (0.03 sec)
建立分區後,存儲文件的獨立表空間將根據分區存儲,以下圖所示:
-rw-rw---- 1 _mysql _mysql 96K Jan 28 17:05 test_range#P#P0.ibd -rw-rw---- 1 _mysql _mysql 96K Jan 28 17:05 test_range#P#P1.ibd -rw-rw---- 1 _mysql _mysql 8.4K Jan 28 17:05 test_range.frm -rw-rw---- 1 _mysql _mysql 28B Jan 28 17:05 test_range.par
對錶添加數據時,會根據指定的列將數據存儲到對應的分區存儲文件中,如列對應的值不在對應範圍內,將寫入失敗:
mysql> insert into test_range values (30); ERROR 1526 (HY000): Table has no partition for value 30
LIST 分區與 RANGE 分區很是類似,只是分區列的值是離散的,而非連續的。以下:
mysql> create table test_list (a INT, b INT) ENGINE = INNODB PARTITION BY LIST (b)( -> PARTITION P0 VALUES IN (1, 3 ,5, 7, 9), -> PARTITION P1 VALUES IN (0, 2, 4, 6, 8)); Query OK, 0 rows affected (0.03 sec)
一樣的,添加數據時,對應的列必須在指定的範圍內,不然將寫入失敗:
mysql> insert into test_list (a, b) values (1, 11); ERROR 1526 (HY000): Table has no partition for value 11
HASH 分區的目的是將數據均勻的分佈到預先定義的各個分區中,保證各分區的數據量大體同樣。用於須要對將要進行哈希分區的列值指定一個列值或表達式,以及指定被分區的表將要被分割成的分區數量:
mysql> create table test_hash (a INT, b DATETIME) ENGINE = INNODB -> PARTITION BY HASH (YEAR(b)) -> PARTITIONS 4; Query OK, 0 rows affected (0.03 sec)
KEY 分區與 HASH 分區類似,HASH 分區使用用戶定義的函數進行分區,KEY 分區使用 MySQL 數據庫提供的函數進行分區。
mysql> create table test_key (a INT, b DATETIME) ENGINE = INNODB -> PARTITION BY KEY (b) -> PARTITIONS 4; Query OK, 0 rows affected (0.04 sec)
以上四種區分方式均存在同樣的分區條件:數據必須是整型(INT)的,若是不是整型,須要將對應的值轉化爲整型,如 YEAR(),TO_DAYS() 等函數。MySQL 5.5 版本開始支持 COLUMNS 分區,可視爲 RANGE 分區和 LIST 分區的一種進化。
能夠直接使用非整形的數據進行分區,如全部的整型類型 SMALLINT、BIGINT,日期類型 DATE、DATETIME,字符串類型 CHAR、VARCHAR,相應的 FLOAT、DECIMAL、BLOB、TEXT、TIMESTAMP 不予支持。
數據庫的應用分爲兩類:OLTP(在線事務處理)和 OLAP(在線分析處理)。
對於 OLAP 的應用,分區的確能夠很好地提升查詢性能。體如今掃描表時不須要掃描全表,掃描單個分區時效率最高;同時也依賴於應用的處理以及分區方式,如不合理的分區,將帶來巨大的性能降低。好比對主鍵進行 HASH 分區,查詢的時候經過非主鍵字段匹配查詢,則一樣是全量數據掃描,可是因爲分區的數量較多,會大量增長系統的 IO 調用。
對於 OLTP 的應用,分區並不會明顯的下降 B+ 樹索引高度,通常的 B+ 樹須要 2~3 次的磁盤 IO,分區並不能明顯提高寫入速度。可是設計很差的分區會帶來嚴重的性能問題。
InnoDB 存儲引擎支持如下幾種常見索引:
B+ 樹的概念在此不作介紹,B+ 樹的操做演示地址:https://www.cs.usfca.edu/~gal...
MySQL 並非經過 B+ 樹索引直接找到數據行,而是找到數據行所在的頁,將頁加載到內存,最後查找到行數據。一個數據頁包含多行數據。
B+ 樹索引包含數據頁與索引頁。數據頁存放完整的行數據,索引頁存放鍵值以及指向數據頁的偏移量,而非完整的行記錄。
InnoDB 存儲引擎是索引組織表,即表中數據按照主鍵順序存放,彙集索引就是按照主鍵構造一顆 B+ 樹,同時葉子節點存放表的行記錄數據,也將彙集索引的葉子節點稱爲數據頁。簡而言之,數據是索引的一部分。
MySQL 經過索引構造數據,因此一張數據表中只能有一個彙集索引。
也成爲非彙集索引,葉子節點並不包含數據行的所有數據。葉子節點中的索引行中包含一個書籤,該書籤就是相應行數據的彙集索引鍵,所以經過非彙集索引查找行數據須要通過兩級索引才能查找到具體的數據內容。好比,非主鍵索引查找行數據,先經過非主鍵索引查找到主鍵,再經過主鍵查找行數據。
MySQL 中的 HASH 索引爲自適應的,無需人工干擾,MySQL 內部會針對查詢業務自動建立 HASH 索引,以提升業務的查詢效率。
HASH 索引僅適用的等值匹配查詢,對於範圍查找無能爲力。
InnoDB 1.2.x 版本開始,InnoDB 存儲引擎開始支持全文索引,經過倒排索引來實現。由於業務極少使用 MySQL 的全文索引,一般若是須要作全文搜索,可選擇 Elasticsearch。
Cardinality 值對列建立索引的選擇性提供了較好的參考,Cardinality 爲一個預估值,非準確值,某一個列的 Cardinality 值表明該列在整張表中不重複值的數量。
忽略業務因素以及數據類型,表中某個列是否適合建立索引,體如今該列全部的值是否相對分散,重複數據越少,相對來講越適合添加索引。所以,當某個列 Cardinality值/錶行數 約接近 1,表明重複數據越少,爲該列建索引的選擇性便越高。
InnoDB 存儲引擎對於 Cardinality 的更新是非實時的,而且獲取到的值爲預估值,經過採樣統計來獲取該值。一般具體業務只需關心該值是否接近於表的行數,以判斷某個列是否適合建立索引。
行級鎖
共享鎖(S Lock):容許事務讀一行數據
排他鎖(X Lock):容許事務刪除或更新一行數據
表級鎖
意向共享鎖(IS Lock):事務想要得到一張表中某幾行的共享鎖
意向排他鎖(IS Lock):事務想要得到一張表中某幾行的排他鎖
InnoDB 存儲引擎中鎖的兼容性:
IS | IX | S | X | |
---|---|---|---|---|
IS | 兼容 | 兼容 | 兼容 | 不兼容 |
IX | 兼容 | 兼容 | 不兼容 | 不兼容 |
S | 兼容 | 不兼容 | 兼容 | 不兼容 |
X | 不兼容 | 不兼容 | 不兼容 | 不兼容 |
以下圖所示,當 InnoDB 須要作細粒度加鎖時,好比對某一行加 X 鎖,須要先對該行所在的表、頁加 IX 鎖。若其中任何一個部分致使等待,那麼該操做須要等待粗粒度鎖的完成。
InnoDB 存儲引擎經過行多版本控制的方式來讀取當前執行時間數據庫中行的數據。若是讀取的行正在執行 UPDATE 或 DELETE 操做,這時讀取操做不會所以等待行上鎖的釋放。相應的,InnoDB 存儲引擎會去讀取行的一個快照數據。
非鎖定讀機制極大的提升了數據庫的併發性,在 InnoDB 存儲引擎的默認設置下,讀取不會佔用和等待表上的鎖。快照數據即當前行記錄的歷史版本,每行記錄可能有多個版本,由此帶來的併發控制,稱之爲多版本併發控制(Multi Version Concurrency Control,MVCC)。
在某些狀況下,用戶須要顯示的對數據庫讀取操做進行加鎖以保證數據邏輯的一致性。這要求數據庫支持加鎖語句,即便對於 SELECT 的支付操做。InnoDB 存儲引擎對於 SELECT 語句支持兩種一致性的鎖定讀操做:
# 對讀取的行記錄添加一個 X 鎖 SELECT ... FOR UPDATE; # 對讀取的行記錄添加一個 S 鎖 SELECT ... LOCK IN SHARE MODE;
1)Record Lock:單個行記錄上的鎖
# 鎖定 id = 5 的行記錄 SELECT ... FROM ... WHERE id = 5 FOR UPDATE;
2)Gap Lock:間隙鎖,鎖定一個範圍,單不包含記錄自己
# 鎖定 id < 5 的全部記錄 SELECT ... FROM ... WHERE id < 5 FOR UPDATE;
3)Next-Key Lock:Gap Lock + Record Lock,鎖定一個範圍,而且鎖定記錄自己
# 鎖定 id <= 5 的全部記錄 SELECT ... FROM ... WHERE id <= 5 FOR UPDATE;
Phantom Problem 是指同一事務下,連續執行兩次一樣的 SQL 語句可能致使不一樣的結果,第二次的 SQL 語句可能會返回以前不存在的行。好比:
# 表 t 中存在 id 爲 1,2,3,4 四條數據,事務隔離級別爲 READ-COMMITTED BEGIN; SELECT * FROM t WHERE id > 2; # 此時查詢結果有id爲 3,4 的記錄 # 此時其餘線程增長一條數據 id = 5 SELECT * FROM t WHERE id > 2; # 此時查詢結果有id爲 3,4,5 的記錄
上述查詢結果,在一個事務中出現同一個查詢返回不一樣的結果,違反了事務的隔離性,即當前事務可以看到其餘事務的結果。
InnoDB 存儲引擎默認的事務隔離級別是 REPEATABLE READ,在該事務隔離級別下采用 Next Key Locking 的方式來加鎖解決。同時應用也能夠經過 InnoDB 存儲引擎的 Next Key Locking 機制在應用層面實現惟一性的檢查。例如:
SELECT * FROM t WHERE col = xxx LOCK IN SHARE MODE;
髒讀指一個事務能夠讀到另外一個事務中未提交的修改數據,違反了數據庫的隔離性。
髒讀發生的條件是事務隔離級別是 READ UNCOMMITTED,目前大部分數據庫都至少設置成 READ COMMITTED。
不可重複讀指在一個事務內屢次讀取同一數據集合,出現了不一樣的數據結果。
不可重複讀發生在事務隔離級別爲 READ COMMITTED,事務 A 讀取一個結果集,事務 B 一樣讀取到該結果集並對其進行修改,提交事務,事務 A 再次讀取結果集時,兩次結果不一致。通常狀況下,不可重複的問題是可接受的,由於讀取的是已經提交的數據,自己不會帶來很大問題。
InnoDB 存儲引擎的隔離級別爲 READ REPEATABLE 時,採用 Next Key Lock 算法,避免了不可重複讀的現象。
一個事務的更新操做結果被另外一個事務的更新操做結果所覆蓋,從而致使數據的不一致。
數據庫層面能夠阻止丟失更新問題的發生,可是應用中存在一個邏輯意義的丟失更新問題。例如,多個線程同時讀取到某條數據,以後均對數據進行修改再更新庫,此時會出現最後一個線程的更新結果覆蓋了先執行的更新結果。應用層面能夠經過對查詢的數據進行加鎖,如前文提到的一致性鎖定讀方式,對須要更新的數據進行加鎖,其餘線程即會出現阻塞串行等待。
死鎖是指兩個或兩個以上的事務在執行過程當中,因搶奪鎖資源而形成的互相等待的現象。
解決死鎖的方式:
1)超時
當一個等待時間超過設置的某一閾值時,對該事務進行回滾,InnoDB 中經過參數 innodb_lock_wait_timeout 設置超時時間。
超時處理機制簡單,但不判斷事務所佔權重,好比一個事務更新的行很是多,回滾也須要佔用更多的時間,同時與該事務搶佔資源的事務可能僅更新少許數據,回滾該事務應當更合理。
2) wait-for graph(等待圖)死鎖檢測
主動檢測死鎖,判斷事務之間的等待狀態是否存在閉環。若檢測到存在死鎖的狀況,InnoDB 存儲引擎選擇回滾 undo 量最小的事務。
鎖升級指將當前鎖的粒度下降。好比數據庫把一個表的 1000 個行鎖升級爲一個頁鎖,或者將頁鎖升級爲表鎖。從而避免鎖的開銷。
InnoDB 存儲引擎根據頁進行加鎖,並採用位圖方式, 開銷由頁的量決定,所以 InnoDB 引擎不會產生鎖升級的問題。
InndoDB 是事務的存儲引擎,其經過 Forece Log at Commit 機制實現事務的持久性,即當事務提交時,必須先將該事務的全部日誌寫入到重作日誌文件進行持久化,待事務的 COMMIT 操做完成纔算完成。這裏的日誌是指重作日誌,由兩部分組成,即 redo log 和 undo log。
事務的 ACID 特性實現:隔離性,經過鎖實現;原子性、一致性、持久性,經過數據庫的 redo log 和 undo log 實現。
redo 和 undo 的做用均可以視爲是一種恢復操做,redo 恢復提交事務修改的頁操做,而 undo 回滾行記錄到某個特定的版本,用來幫助事務回滾及 MVCC 的功能。所以二者記錄的內容不一樣,redo 一般是物理日誌,記錄的是頁的物理修改操做。redo log 基本上都是順序寫的,undo 是邏輯日誌,根據每行記錄進行記錄。undo log 是須要進行隨機讀寫的。
重作日誌用來實現事務的持久性,即事務 ACID 的 D。其由兩部分組成:一是內存中的重作日誌緩衝(redo log buffer),其是容易丟失的;二是重作日誌文件(redo log file),其是持久的。
爲了確保每第二天志都寫入重作日誌文件,在每次將重作日誌緩衝寫入重作日誌文件後,InnoDB 存儲引擎都須要調用一次 fsync 操做。所以磁盤的性能決定了事務提交的性能,也就是數據庫的性能。
參數 innodb_flush_log_at_trx_commit 用來控制重作日誌刷新到磁盤的策略,其參數含義以下:
1 -> 表示事務提交時必須調用一次 fsync 0 -> 表示事務提交是不進行寫入重作日誌操做,這個操做盡在 master thread 中完成,而 master thread 中每 1 秒進行一次重作日誌文件的 fsync 操做 2 -> 表示事務提交時將重作日誌寫入重作日誌文件,但僅寫入文件系統的緩存中,不進行 fsync 操做。
MySQL 默認參數爲 1,保證最高的數據可靠性,爲 0 或 2 時能夠提供更好的事務性能,可是存在數據庫宕機時數據丟失風險。
重作日誌記錄了事務的行爲,能夠很好的經過其對頁進行「重作」操做。可是事務有時還須要進行回滾操做,這時就須要 undo。在對數據庫進行修改時,InnoDB 存儲引擎不但會產生 redo,還會產生必定量的 undo,這樣若是執行的事務或語句因爲某種緣由失敗了,又或者是用戶主動 ROLLBACK 請求回滾,就能夠利用 undo 進行數據回滾到修改以前的樣子。
delete 和 update 操做並不直接刪除原有的數據,執行 delete 語句時,受影響的數據被標記爲邏輯刪除,真正刪除這行記錄的操做在 purge 操做中完成。
purge 用於最終完成 delete 和 update 操做。這樣設計是由於 InnoDB 存儲引擎支持 MVCC,因此記錄不能再事務提交時當即進行處理。而是否能夠刪除該條記錄經過 purge 來判斷,若該行記錄已再也不被任務其餘事務引用,那麼就能夠進行真正的 delete 操做。
若事務爲非只讀事務,則每次事務提交時須要進行一次 fsync 操做,以保證重作日誌都已經寫入磁盤。爲了提升磁盤 fsync 的效率,當前數據庫提供了 group commit 的操做,即一次 fsync 能夠刷新確保多個事務日誌被寫入文件。對於 InnoDB 存儲引擎來講,事務提交時會進行兩個階段的操做:
1)修改內存中事務對應的信息,而且將日誌寫入重作日誌緩衝
2)調用 fsync 確保日誌都從重作日誌緩衝寫入磁盤
步驟 2)相對步驟 1)是一個較慢的過程,當有事務進行步驟 2)時,其餘事務能夠進行步驟 1)的操做,正在提交的事務完成提交操做後,再次進行步驟 2)時,能夠將多個事務的重作日誌經過一次 fsync 刷新到磁盤,這樣就大大的減小了磁盤的壓力,從而提升了數據庫的總體性能。
根據不一樣的類型來劃分:
按照備份後文件的內容劃分:
按照備份數據庫的內容劃分:
對於 InnoDB 存儲引擎的冷備,只須要備份 MySQL 數據庫的 frm 文件,共享表空間文件,獨立表空間文件(*.ibd),重作日誌文件。另外建議按期備份 MySQL 數據庫的配置文件 my.cnf,有利於恢復的操做。
冷備的優勢:
冷備的缺點:
用來完成轉存數據庫的備份及不一樣數據庫之間的移植,如從 MySQL 低版本數據庫升級到 MySQL 高版本數據庫,又或者從 MySQL 移植到 Oracle,SQL Server 等。
mysqldump [arguments] > file_name mysqldump --all-databases > dump.sql mysqldump --databases db1 db2 db3 > dump.sql
邏輯備份方法,更準確的說是導出一張表中的數據。
SELECT * INTO OUTFILE '/home/yw/a.txt' FROM test;
mysqldump 的恢復操做簡單,僅需執行導出的 SQL 語句便可。
source /home/yw/dump.sql
恢復經過 SELECT INTO OUTFILE 導出的數據
LOAD DATA INTO TABLE test IGNORE 1 LINES INFILE '/home/yw/a.txt'
二進制日誌很是關鍵,用戶能夠經過它完成 point-in-time 的恢復工做,MySQL 的 replication 一樣須要二進制日誌,在默認狀況下並不開啓二進制日誌,要使用二進制日誌必須啓用它。InnoDB 存儲引擎推薦的二進制日誌的服務器配置以下:
[mysqld] log-bin = mysql-bin sync_binlog = 1 innodb_support_xa = 1
在備份二進制日誌文件前,能夠經過 FLUSH LOGS 命令生成一個新的二進制日誌文件,而後備份以前的二進制日誌。
恢復二進制日誌也很是簡單,經過 mysqlbinlog 便可:
mysqlbinlog [options] log_file mysqlbinlog binlog.0000001 | mysql -uroot -p test
也能夠先將二進制文件導出到一個文件,而後經過 source 進行導入:
shell > mysqlbinlog binlog.0000001 > /home/yw/binlog.sql ... mysql > source /home/yw/binlog.sql
複製(replication)是 MySQL 數據庫提供的一種高可用高性能的解決方案,replication 的工做原理分爲如下 3 個步驟:
複製的工做原理以下圖所示: