1. 程式人生 > >InnoDB體系架構總結(二)

InnoDB體系架構總結(二)

原子性 val 新版本 insert logfile net row syn 數據頁

事務

確保事務內的SQL都可以同步執行 要麽一起成功 要麽一起失敗。事務有四個特性原子性 一致性,隔離性,持久性

實現方式

開始事務的時候回家記錄記錄一個LSN日誌序列 當事務執行的時候 會首先在Innodb_log_buffer 日誌緩沖區插入事務日誌 redo log;當事務提交的時候 再根據不同的策略將緩沖日誌刷新到日誌文件和磁盤中。 
  • 將數據寫入InnoDB buffer pool 並加上獨占鎖
  • 將UNDO信息寫入undo表的回滾段 以備回滾數據的時候使用
  • 更改緩存頁中的數據 並將更新記錄寫入redo buffer
  • 提交時 根據innodb_flush_log_at_trx_commit的設置 用不同的方式將redo buffer中的數據刷新到 redo log file中 然後釋放獨占鎖
  • 最後 後臺IO線程根據將緩存中的數據刷新到磁盤

innodb_flush_log_at_trx_commit

默認設置為1  既在每次事務提交的時候 都會將緩沖池中的數據寫入到日誌文件中 並立即調用系統fsync 刷新日誌文件到磁盤
設置為0  每秒鐘執行數據寫入日誌並調用系統fsync
設置為2 只在事務提交的時候 屬性buffer中的redo 數據寫入日誌文件中 但是將日誌文件寫入到磁盤 則由系統配置確認

innodb_log_buffer_size

決定了重做日誌緩存的大小 如果是無語中有大量的插入或者更新數據 則需要調整默認配置 以提高性能 

Redo log 重做日誌緩沖

主要是解決 提交的事務沒有執行完成但是數據庫奔潰了,當數據庫恢復之後,可以完整的恢復數據。
InnoDB存儲引擎會首先將重做日誌信息放到這個緩沖區 redo log buffer,然後按照不同的策略和頻率將buffer中的數據刷新到重做日誌中。

redo log 在磁盤中保存的名稱為 ib_logfile0 and ib_logfile1

Checkpoing技術

當事務提交時 需要先寫重做日誌 然後再修改頁。如果數據庫由於未知原因崩潰 而導致數據丟失的時候 需要通過重做日誌來完成

當數據庫發生宕機的時候 數據的恢復 不需要重做所有的日誌 這個時候就用到checkpoint。數據恢復的時候 只要恢復Checkpoint這個點,之後的數據即可。

對於InnoDB存儲引擎來來說 有一個LSN的數字 來標記版本的

數據庫並發

相對於串行處理 支持並發可以提高數據庫的利用率 但是數據庫並發可能會帶來下面的問題
  • 更新丟失 例如同一條數據 被不同事物更新 導致 有一個事務更新錯誤
  • 臟讀 事務更新的數據 還未提交 就被另外一個事務讀取 讀取的數據 就有可能是臟數據
  • 不可重復讀 前後兩次讀取數據的過程中 數據被另外的事務修改 導致數據不一致
  • 幻讀 解決了不可重復讀 但是如果另外一個事務同時提交了新的數據

innodb隔離級別

MariaDB [(none)]> show variables like "%tx_isolation%";
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
在同一個事務內的查詢 與事務開始時刻的數據 保持一致 ,但是存在幻讀

幻讀演示

session1 開啟事務

mysql> begin;
Query OK, 0 rows affected

mysql> select *  from phone;    ##事務開始的時候 電話是123456的數據只有一條數據
+----+----------+
| id | phone    |
+----+----------+
|  3 | 123456   |
+----+----------+

session2 如果是事務添加的話 也是一樣的結果

## 這裏插入一條新的數據 電話也是 123456
INSERT INTO `tmp`.`phone` (`id`, `phone`) VALUES (‘2‘, ‘123456‘);

session1

mysql> select *  from phone;    ##這裏再次讀取數據 和事務開始的時候 數據相同 電話是123456的數據只有一條數據
+----+----------+
| id | phone    |
+----+----------+
|  3 | 123456   |
+----+----------+


## 這裏更新數據的時候 顯示有2行數據被更新了
mysql> update phone set phone=111111 where phone=123456;
Query OK, 2 rows affected
Rows matched: 2  Changed: 2 

## 這個時候 數據庫有兩條數據 被更新成111111了 出現了幻讀
mysql> select *  from phone;
+----+----------+
| id | phone    |
+----+----------+
|  2 | 111111   |
|  3 | 111111   |

解決方式 加鎖

Session1 開啟一個事務 並通過for update的方式加鎖

mysql> begin;

mysql> select *  from phone for update;

Session2 開啟一個事務

mysql> INSERT INTO `tmp`.`phone` (`id`, `phone`) VALUES (‘2‘, ‘123456‘);

1205 - Lock wait timeout exceeded; try restarting transaction

## 鎖等待超時 退出事務

MVCC

多版本並發控制協議 優點是 讀不加鎖 讀寫不沖突。InnoDB通過Undo Log實現了數據的多版本。而並發控制則是通過鎖來實現。讀操作可以分為兩種 一種是快照讀另外一種是當前讀

快照讀:讀取的是記錄的可見版本 也有可能是歷史版本 不用加鎖。
當前讀:讀取的是記錄的最新版本

InnoDB實現的MVVC沒有解決幻讀 可以通過加鎖的方式解決這個問題

innodb buffer pool

參考博客 :https://michael.bouvy.net/blog/en/2015/01/18/understanding-mysql-innodb-buffer-pool-size/

查看包含的數據類型及大小

SELECT
    page_type AS Page_Type,
    sum(data_size) / 1024 / 1024 AS Size_in_MB
FROM
    information_schema.innodb_buffer_page
GROUP BY
    page_type
ORDER BY
    Size_in_MB DESC;

結果

+-------------------+------------+
| Page_Type         | Size_in_MB |
+-------------------+------------+
| INDEX             | 0.13445091 |
| UNKNOWN           | 0.00000000 |
| INODE             | 0.00000000 |
| IBUF_INDEX        | 0.00000000 |
| TRX_SYSTEM        | 0.00000000 |
| SYSTEM            | 0.00000000 |
| UNDO_LOG          | 0.00000000 |
| FILE_SPACE_HEADER | 0.00000000 |
| IBUF_BITMAP       | 0.00000000 |
+-------------------+------------+

一些重要的且常用的數據類型解釋

  • INDEX:B-Tree 索引 這裏應該也包含行數據頁的數據 因為聚簇索引中 主鍵索引和數據是放在一起的
  • IBUF_INDEX: 插入緩存索引(下文 innodb_change_buffering)
  • UNKNOWN:未被使用或者不知道狀態的
  • TRX_SYSTEM:系統數據(有可能是指事務 這裏不確定)
  • UNDO_LOG:Undo日誌(下文 Undo log)

innodb_change_buffering

https://dev.mysql.com/doc/refman/5.5/en/innodb-performance-change_buffering.html

大致意思如下

在 insert update 和 delete操作 如果涉及到索引列 特別是 secondary keys 的時候;如果所涉及到的數據不在buffer pool的時候,由於涉及到的數據 是無序的 那麽 頻繁的操作會導致大量的IO消耗。到數據被載入到buffer pool的時候,change buffer 會將數據合並在一起 然後更新到磁盤文件中。在空閑的時候,InnoDB的主線程 會合並buffer changes的數據。

由於change buffer 占用了部分buffer pool,降低了內存中可以緩存的數據頁,如果數據已經載入到buffer pool或者有很少的secondary indexs,最好禁用掉這個特性。

可設置的值如下

  • all
    The default value: buffer inserts, delete-marking operations, and purges.
  • none
    Do not buffer any operations.
  • inserts
    Buffer insert operations.
  • deletes
    Buffer delete-marking operations.
  • changes
    Buffer both inserts and delete-marking operations.
  • purges
    Buffer the physical deletion operations that happen in the background.

Undo log

Undo log是InnoDB MVCC事務特性的重要組成部分,記錄的是老版本的數據。主要作用是回滾數據,也可以根據undo log回溯到某個特別的版本的數據,實現MVCC。undo數據會首先被刷新到undo buffer中 之後在合適的時間 undo buffer中的數據 會被刷新到磁盤中,所有的undo log 會存放在ibd數據文件中(表空間)。Innodb 中存在purge線程,他們會查詢那些無人問津的舊版本數據或者也內容標記為刪除的操作也會被清理掉,從而保證undo log 不至於無限增長。

內存管理算法

通常,數據庫中的緩沖池是通過LRU即最近最少使用算法來管理,最少使用的也在LRU列表的尾端,當緩沖池中的數據滿了之後,會首先釋放LRU列表中的尾端的頁數據。

但是InnoDBd的存儲引擎在讀取到新的數據頁的時候,不是直接放到LRU列表的首部,而是根據midpoint位置。這樣的好處是如果是掃描數據的時候,通常需要操作許多的數據頁,而這些數據又僅僅是在這次查詢中需要,並不屬於活躍的熱數據。防止將最活躍的數據被移動到LRU尾部而被釋放。

InnoDB體系架構總結(二)