InnoDB存儲引擎

InnoDB體系架構

後臺線程

InnoDB 存儲引擎是多線程的模型,所以其後臺有多個不一樣的後臺線程,負責處理不一樣的任務。前端

Master Thread

Master Thread 是個很是核心的後臺線程,主要負責將緩衝池中的數據異步刷新到磁盤,保證數據的一致性,包括髒頁的刷新、合併插入緩衝(INSERT BUFFER ) 、UNDO 頁的回收等。node

I0 Thread

在InnoDB存儲引擎中大量使用了AIO(Async IO)來處理寫IO請求,這樣能夠極大提升數據庫的性能。而IO Thread 的工做主要是負責這些IO 請求的回調(call back)處理。mysql

從InnoDB 1.0.x 版本開始, read threadwrite thread 分別增大到了4 個,而且再也不使用inodb_file_io_threads參數,而是分別使用innodb_read_io_threadsinnodb_write_io_threads參數進行設置。算法

mysql> show variables like 'innodb_version'\G;
*************************** 1. row ***************************
Variable_name: innodb_version
        Value: 5.7.13
1 row in set, 1 warning (0.62 sec)

mysql> show variables like 'innodb_%io_threads'\G;
*************************** 1. row ***************************
Variable_name: innodb_read_io_threads
        Value: 4
*************************** 2. row ***************************
Variable_name: innodb_write_io_threads
        Value: 4
2 rows in set, 1 warning (0.00 sec)

能夠經過命令SHOW ENGINE INNODB STATUS 來觀察InnoDB 中的IO Thread:sql

mysql> show engine innodb status \G;
*************************** 1. row ***************************
  Type: InnoDB
  Name:
Status:
=====================================
2016-08-18 08:42:59 0x34b8 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 13 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 1132 srv_active, 0 srv_shutdown, 3755881 srv_idle
srv_master_thread log flush and writes: 3757011
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 1965
OS WAIT ARRAY INFO: signal count 1673
RW-shared spins 0, rounds 2788, OS waits 1172
RW-excl spins 0, rounds 2275, OS waits 30
RW-sx spins 5, rounds 150, OS waits 5
Spin rounds per wait: 2788.00 RW-shared, 2275.00 RW-excl, 30.00 RW-sx
------------------------
LATEST FOREIGN KEY ERROR
------------------------
2016-07-29 15:13:50 0x2cf4  Cannot drop table `dqd_database`.`news_main`
because it is referenced by `dqd_database`.`news_main_analysis`
------------
TRANSACTIONS
------------
Trx id counter 5149
Purge done for trx's n:o < 5149 undo n:o < 0 state: running but idle
History list length 627
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 281475148613424, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
--------
FILE I/O
--------
I/O thread 0 state: wait Windows aio (insert buffer thread)
I/O thread 1 state: wait Windows aio (log thread)
I/O thread 2 state: wait Windows aio (read thread)
I/O thread 3 state: wait Windows aio (read thread)
I/O thread 4 state: wait Windows aio (read thread)
I/O thread 5 state: wait Windows aio (read thread)
I/O thread 6 state: wait Windows aio (write thread)
I/O thread 7 state: wait Windows aio (write thread)
I/O thread 8 state: wait Windows aio (write thread)
I/O thread 9 state: wait Windows aio (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
 ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
322 OS file reads, 13452 OS file writes, 7016 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 176 merges
merged operations:
 insert 0, delete mark 0, delete 0
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 1 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 1 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 16091420
Log flushed up to   16091420
Pages flushed up to 16091420
Last checkpoint at  16091411
0 pending log flushes, 0 pending chkp writes
4209 log i/o's done, 0.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137297920
Dictionary memory allocated 505092
Buffer pool size   8192
Free buffers       7414
Database pages     776
Old database pages 266
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 346, not young 134
0.00 youngs/s, 0.00 non-youngs/s
Pages read 289, created 1721, written 7650
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 776, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=4504, Main thread ID=4024, state: sleeping
Number of rows inserted 7694, updated 0, deleted 0, read 11083
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

1 row in set (0.37 sec)

IO Thread 0 爲insert buffer thread, IO Thread 1 爲log thread 。以後就是根據參數inodb_read_io_threadsinodb_write_io_threads來設置的讀寫線程,而且讀線程的ID老是小於寫線程。數據庫

Purge Thread

事務被提交後,其所使用的undolog可能再也不須要,所以須要PurgeThread來回收已經使用並分配的undo頁。緩存

從InnoDB1.2版本開始,InnoDB支持多個purge Thread,進一步加快undo頁回收。同時因爲Purge Thread 須要離散地讀取undo頁,這樣也能更進一步利用磁盤的隨機讀取性能。如用戶能夠設置4 個Purge Thread:bash

mysql> select version() \G;
*************************** 1. row ***************************
version(): 5.7.13
1 row in set (0.11 sec)


mysql> show variables like 'innodb_purge_threads'\G;
*************************** 1. row ***************************
Variable_name: innodb_purge_threads
        Value: 4
1 row in set, 1 warning (0.02 sec)
Page Cleaner Thread

Page Cleaner Thread 是在InnoDB 1.2.x 版本中引入的,其做用是將以前版本中髒頁的刷新操做都放入到單獨的線程中來完成,而其目的是爲了減輕原Master Thread 的工做及對於用戶查詢線程的阻塞,進一步提升InnoDB存儲引擎的性能。數據結構

內存

緩衝池

InnoDB 存儲引擎是基於磁盤存儲的,並將其中的記錄按照頁的方式進行管理。在數據庫系統中,因爲CPU速度與磁盤速度之間的鴻溝,基於磁盤的數據庫系統一般使用緩衝池技術來提升數據庫
的總體性能。多線程

讀取:緩衝池是一塊內存區域,經過內存的速度來彌補磁盤速度較慢對數據庫性能的影響。在數據庫中進行讀取頁的操做,首先將從磁盤讀到的頁存放在緩衝池中,這個過程稱爲將頁咋「FIX」在緩衝池中。 下一次再讀相同的頁時,首先判斷該頁是否在緩衝池中。若在緩衝池中,稱該頁在緩衝池中被命中,直接讀取該頁。不然,讀取磁盤上的頁。
修改:首先修改在緩衝池中的頁。而後再以必定的頻率刷新到磁盤上。這裏須要注意的是,頁從緩衝池刷新回磁盤的操做並非在每次頁發生更新時觸發,而是經過Checkpoint的機制刷新回磁盤。

緩衝池的配置經過參數innodb_buffer_pool_size來設置。在個人電腦上緩衝池大小是128MB。

mysql> show variables like 'innodb_buffer_pool_size'\G;
*************************** 1. row ***************************
Variable_name: innodb_buffer_pool_size
        Value: 134217728
1 row in set, 1 warning (0.00 sec)

緩衝池中緩存的數據頁類型有:索引頁、數據頁、undo 頁、插入緩衝(insert buffer)、自適應哈希索引(adaptive bash index)、InnoDB存儲的鎖信息(lock info),數據字典信息(data dictionary)等。

mysql> show variables like 'innodb_buffer_pool_instances'\G;
*************************** 1. row ***************************
Variable_name: innodb_buffer_pool_instances
        Value: 1
1 row in set, 1 warning (0.00 sec)

經過information_schema數據庫下的表innodb_buffer_pool_stats來觀察緩衝池的狀態。

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| dqd_database       |
| employees          |
| mysql              |
| performance_schema |
| spider             |
| sys                |
| temp               |
| test               |
+--------------------+
9 rows in set (0.13 sec)

mysql> use information_schema
Database changed
mysql> select * from innodb_buffer_pool_stats\G;
*************************** 1. row ***************************
                         POOL_ID: 0
                       POOL_SIZE: 8192
                    FREE_BUFFERS: 7414
                  DATABASE_PAGES: 776
              OLD_DATABASE_PAGES: 266
         MODIFIED_DATABASE_PAGES: 0
              PENDING_DECOMPRESS: 0
                   PENDING_READS: 0
               PENDING_FLUSH_LRU: 0
              PENDING_FLUSH_LIST: 0
                PAGES_MADE_YOUNG: 346
            PAGES_NOT_MADE_YOUNG: 134
           PAGES_MADE_YOUNG_RATE: 0
       PAGES_MADE_NOT_YOUNG_RATE: 0
               NUMBER_PAGES_READ: 289
            NUMBER_PAGES_CREATED: 1721
            NUMBER_PAGES_WRITTEN: 7650
                 PAGES_READ_RATE: 0
               PAGES_CREATE_RATE: 0
              PAGES_WRITTEN_RATE: 0
                NUMBER_PAGES_GET: 120146
                        HIT_RATE: 0
    YOUNG_MAKE_PER_THOUSAND_GETS: 0
NOT_YOUNG_MAKE_PER_THOUSAND_GETS: 0
         NUMBER_PAGES_READ_AHEAD: 0
       NUMBER_READ_AHEAD_EVICTED: 0
                 READ_AHEAD_RATE: 0
         READ_AHEAD_EVICTED_RATE: 0
                    LRU_IO_TOTAL: 0
                  LRU_IO_CURRENT: 0
                UNCOMPRESS_TOTAL: 0
              UNCOMPRESS_CURRENT: 0
1 row in set (0.05 sec)
LRU List 、Free List和Flush List

數據庫中的緩衝池是經過LRU(Latest Recent Used ,最近最少使用)算法來進行管理的。即最頻繁使用的頁在LRU列表的前端,而最少使用的頁在LRU列表的尾端。當緩衝池不能存放新讀取到的頁時,將首先釋放LRU列表中尾端的頁。

在InnoDB存儲引擎中,緩衝池中頁的大小默認爲16KB ,一樣使用LRU算法對緩衝池進行管理。InnoDB存儲引擎對傳統LRU算法進行優化,經過使用midpoint insertion strategy在LRU列表中加入了midpoint位置。默認配置在LRU列表的5/8處。midpoint位置可由參數innodb_old_blocks_pct控制。

mysql> show variables like 'innodb_old_blocks_pct'\G;
*************************** 1. row ***************************
Variable_name: innodb_old_blocks_pct
        Value: 37
1 row in set, 1 warning (0.01 sec)

在上面的innodb_old_blocks_pct默認參數爲37,表示新讀取的頁插入到LRU列表尾端的37%的位置(近似3/8)。在InnoDB中,midpoint以後的列表成爲old列表,以前的列表成爲new列表。new列表中的頁都是最爲活躍的熱點數據。

某些SQL操做如索引或數據的掃描操做可能會使緩衝池中的熱點數據頁從LRU列表中移除,在下次須要讀取該頁時,InnoDB存儲引擎須要再次訪問磁盤。經過使用innodb_old_blocks_time參數,用於表示頁讀取到mid位置後須要等待多久時間被加入到LRU列表的熱端,儘量使LRU列表中熱點數據不被刷出。

mysql> set global innodb_old_blocks_time=1000;
Query OK, 0 rows affected (0.06 sec)

mysql> set global innodb_old_blocks_time=0;
Query OK, 0 rows affected (0.00 sec)

若預估活躍的熱點數據不止67%(5/8),可經過設置參數減小可能被刷出的機率。

mysql> set global innodb_old_blocks_pct=20;
Query OK, 0 rows affected (0.07 sec)

LRU列表用來管理已經讀取的頁,但當數據庫剛啓動時, LRU 列表是空的,即沒有任何的頁。這時頁都存放在Free列表中。當須要從緩衝池中分頁時,首先從Free列表中查找是否有可用的空閒頁, 如有則將該頁從Free列表中刪除,放入到LRU 列表中。不然,根據LRU算法,淘汰LRU列表末尾的頁,將該內存空間分配給新的頁。當頁從LRU 列表的old 部分加入到new 部分時,稱此時發生的操做爲page made young。而由於innodb_old_blocks_time的設置而致使頁沒有從old部分移動到new部分的操做稱爲page not made young。能夠經過命令SHOW ENGINE INNODB STATUS 來觀察LRU 列表及Free 列表的使用悄況和運行狀態.

----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137297920
Dictionary memory allocated 505092
Buffer pool size   8192 -- 緩衝池總大小,8192*16K
Free buffers       7414 -- Free列表中的頁的數量
Database pages     776 -- LRU列表中頁的數量
Old database pages 266
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 346, not young 134
0.00 youngs/s, 0.00 non-youngs/s   -- 每秒這兩類操做的次數
Pages read 289, created 1721, written 7650
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 776, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------

由於緩衝池中的頁還可能會被分配給自適應哈希索引、Lock 信息、Insert Buffer 等頁,而這部分頁不須要LRU 算法進行維護,所以不存在於LRU 列表中。因此可能的狀況是Free buffer與Database pages的數量之和不等於Buffer pool size。

觀察變量--Buffer pool hit rate。表示緩衝池的命中率,一般該值不該該小子95%。若發生Buffer pool hit rate 的值小於95% 這種狀況,用戶須要觀察是不是因爲全表掃描引發的LRU 列表被污染的問題。

在LRU 列表中的頁被修改後,稱該頁爲髒頁(dirty page) ,即緩衝池中的頁和磁盤上的頁的數據產生了不一致。這時數據庫會經過CHECKPOINT機制將髒頁刷新回磁盤,而Flush列表中的頁目即爲髒頁列表。髒頁既存在於LRU 列表中,也存在於Flush 列表中。LRU列表用來管理緩衝池中頁的可用性。Flush 列表用來管理將頁刷新回磁盤,兩者互不影響。

重作日誌緩衝

如下三種狀況會將重作日誌緩衝中的內容刷新到重作日誌文件中:

  • Master Thread 每一秒將重作日誌緩衝刷新到重作日誌文件
  • 每一個事務提交時會將重作日誌緩衝刷新到重作日誌文件
  • 當重作日誌緩衝池剩餘空間小於1/2 時,重作日誌緩衝刷新到重作日誌文件。
額外的內存池

InnoDB存儲引擎經過內存堆的方式對內存進行管理。在對一些數據結構自己的內存進行分配時,須要從額外的內存池中進行申請,當該區域的內存不夠時,會從緩衝池中進行申請。

Checkpoint技術

緩衝池設計目的是爲了協調CPU速度與磁盤速度。所以,頁的操做是在緩衝池中完成的。DML語句,如update或delete改變頁中的記錄,那麼此時頁是髒的,即緩衝池中的頁的版本要比磁盤的新。數據庫須要將新版本的頁從緩衝池刷新到磁盤。

爲避免數據丟失的問題,當前事務數據庫廣泛採用Write Ahead Log策略,即當事務提交時,先寫重作日誌,再修改頁。當發生宕機致使數據丟失時,經過重作日誌來完成數據的恢復。(ACID中D特性)

Checkpoint技術的目的是解決一下問題:

  • 數據庫宕機對Checkpoint後的重作日誌進行恢復,縮短數據庫恢復時間
  • 緩衝池不夠用時,將髒頁刷新到磁盤根據LRU算法會溢出最近最少使用的頁,若此頁爲髒頁,強制執行Checkpoint,將髒頁刷新到磁盤
  • 重作日誌不可用時,刷新髒頁重作日誌出現不可用的狀況是由於當前事務數據庫系統對重作日誌的設計都是循環使用的,並非讓其無限增大。重作日誌能夠被重用的部分是指重作日誌已經再也不須要,當數據庫發生宕機時,數據庫恢復操做不須要這部分的重作日誌,所以就能夠被覆蓋重用。若此時重作日誌還須要使用,必須強制產生Checkpoint,將緩衝池中的頁至少刷新到當前重作日誌的位置。

兩種Checkpoint:

  • Sharp Checkpoint:發生在數據庫關閉時將全部的髒頁都刷新回磁盤(默認工做方式),參數innodb_fast_shutdown=1
  • Fuzzy Checkpoint:只刷新一部分髒頁,不刷新全部的髒頁回磁盤
相關文章
相關標籤/搜索