InnoDB表存儲優化
- 適時的使用
OPTIMIZE TABLE
語句來重組表,壓縮浪費的表空間。這是在其它優化技術不可用的情況下最直接的方法。
OPTIMIZE TABLE
語句通過拷貝表數據並重建表索引,使得索引數據更加緊湊,減少空間碎片。語句的執行效果會因表的不同而不同。過大的表或者過大的索引及初次添加大量數據的情況下都會使得這一操作變慢。
InnoDB
表,如果主鍵過長(長數據列做主鍵,或者多個列組合做主鍵)會浪費很多空間。同時,二級索引也包含主鍵。這種情況,可以考慮創建自增列作為主鍵,或者使用前綴索引。- 對於需要存儲長度不定或者包含很多NULL值的字符串列,使用
VARCHAR
代替CHAR
。在小表應用上,緩存使用及磁盤I/O消耗會更小。 - 對於包含大量重復文本或者數字的大表,可以考慮采用壓縮的行格式存儲。這樣數據加載會減少對緩存及I/O的需求。在使用壓縮行格式前,需要考慮壓縮行格式
COMPRESSED
和的不同性能影響。
InnoDB 事務管理優化
優化InnoDB
事務處理,主要需要找到事務特性和服務器負載間的某個平衡點。例如,一秒需要提交幾千事務的,或者每隔2-3個小時提交一次事務的不同應用表現。
- MySQL的默認設置
AUTOCOMMIT=1
會限制繁忙數據庫的性能。如果可以的話,可以在應用中使用SET AUTOCOMMIT=0
或者START TRANSACTION
,然后將多個相關的數據變更操作添加到同一事務中,然后執行COMMIT
語句來提交事務,提交數據變更。
InnoDB
對於引發數據庫變更的操作,必須將其進行日志刷盤。
- 對於只包含
SELECT
語句的事務,啟用AUTOCOMMIT
,使得InnoDB
能夠識別只讀事務,然后進行相應的優化。 - 避免對大數據量操作插入,更新和刪除之后的回滾操作。如果一個大的事務拖慢了服務器,那么回滾將是服務器性能變得更糟。可以分批處理大數據量操作。通過殺進程方式終止的回滾操作會在服務器啟動時重新啟動。
可以通過如下策略減少此類問題發生:
- 增大緩存,避免頻繁磁盤I/O。
- 設置
innodb_change_buffering=all
,這樣update和delete操作也會和insert一樣進行緩存,回滾也更快。 - 手動commit,分割大數據操作。
為了避免時空的回滾。增大緩存,使得回滾進程可以應用到最大的資源以便快速執行。或者殺掉回滾進程,然后使用innodb_force_recovery=3
選項重啟。
對於較多執行耗時inserts, updates, 及deletes 操作的服務器,確保innodb_change_buffering=all
開啟。
InnoDB
如會每秒刷盤一次日志,如果可以承受最新事務崩潰的數據損失,可以設置innodb_flush_log_at_trx_commit
= 0。雖然日志的刷盤操作也不是保證的,同時也可以設置innodb_support_xa
= 0,減少磁盤和二進制日志的同步操作。
Note
innodb_support_xa
已被棄用,將來版本會被移除。MySQL 5.7.10版本,InnoDB
XA事務的兩階段提交是默認支持的,不能設置禁用innodb_support_xa
。
- 行修改或刪除后,行數據及undo logs在物理上並沒有立刻被變更。即使在事務立刻提交后。舊數據會保持直到之前啟動的事務或者並發執行的事務完成后。這樣,這些食物可以一直訪問到相關的舊數據。所以耗時的事務會阻止
InnoDB
清除其它相關事務的數據。 - 如果一個耗時的事務修改或者刪除了某些行。那么其它使用這些數據的事務,如果事務級別設置在
READ COMMITTED
作者REPEATABLE READ
級別,則需要額外的處理來重建舊數據。 - 當一個耗時的事務修改了某個表,其它使用此表的事務將不會使用覆蓋索引。如果二級索引包含比較新的
PAGE_MAX_TRX_ID
,或者某些記錄被標記為已刪除,InnoDB
可能需要使用聚簇索引來查詢相應的記錄。
覆蓋索引查詢(使用二級索引即可獲得所需的數據,而不需要訪問表數據)
InnoDB只讀事務優化
InnoDB
可以避免給只讀事務賦transaction ID (TRX_ID
)。事務ID只對執行寫操作,或者含鎖讀操作,如 SELECT ... FOR UPDATE
有用。去除不必要的事務ID,有助於減少每次讀寫操作必須訪問的內部數據結構大小。
InnoDB
在一下情景能夠識別只讀操作:
- 事務以語句
START TRANSACTION READ ONLY
開始,這種情況下,數據變更操作會引發錯誤,事務仍會以只讀性質運行:
ERROR 1792 (25006): Cannot execute statement in a READ ONLY transaction.
對於事務中的臨時表可以進行任何操作。
autocommit
= on,並且事務只包含一個語句,且語句為沒有使用FOR UPDATE
或者LOCK IN SHARED MODE
的SELECT
語句。- 事務以
READ ONLY
選項開始。
這樣,對於讀繁忙的應用,如報表應用,可以將一系列的查詢語句綜合到一個只讀的事務中,或者在執行查詢前設置 autocommit
= on,或者在應用中避免將變更操作和查詢操作相互影響。.
重做日志(redo log)優化
可以考慮遵循以下優化指引:
- 確保重做日志足夠大,即使和緩存池(buffer pool)一樣大。當
InnoDB
寫滿redo log時,服務器會基於一個檢查點(checkpoint)就會將日志中的變更內容寫到磁盤。如果redo log 文件過小,那么就會引發服務器頻繁的寫盤。雖然之前,設置過大的redo log 會引起恢復時間的過長,但是現在,恢復機制已經在速度上有很大的優化,因此不用再考慮此因素。
The size and number of redo log 文件的大小和數量可以使用: innodb_log_file_size
和 innodb_log_files_in_group
進行設置。
- 可以考慮增大日志緩存(log buffer)。大的日志緩存可以容納更大的事務執行,避免不必要的寫盤操作。設置變量:
innodb_log_buffer_size
。 - 配置
innodb_log_write_ahead_size
變量以避免 “read-on-write”(就是當修改的字節不足一個洗系統block時,需要將整個block讀進內存,修改對應的位置,然后再寫進去;如果我們以block為單位來寫入的話,直接完整覆蓋寫入即可)。這個配置定義了redo log的write-ahead塊大小。設置innodb_log_write_ahead_size
的大小以匹配操作系統或者文件系統的緩存塊大小。Read-on-write 的產生是因為在write-ahead 塊大小和操作系統或者文件系統的緩存塊大小不匹配的情況下,redo log 塊無法完全的寫入到操作系統,或者文件系統引起的。
innodb_log_write_ahead_size
的值可以設置為InnoDB
日志文件塊大小的倍數(2n)。最小的值為(512)。設置為最小值時Write-ahead不會發生。最大值為innodb_page_size
。如果設置的值大於innodb_page_size
,那么服務器會使用innodb_page_size
值。
innodb_log_write_ahead_size
值設置的太小,會導致read-on-write;設置過大,則會影響fsync
性能,因為一次需要些多個數據塊。
InnoDB表的大數據載入
快速插入通用指引:
- 導入數據時,關閉autocommit 模式,避免每次行插入導致的日志刷盤。在執行開始及結束使用
SET autocommit
及COMMIT
語句:
mysqldump 選項 --opt
(默認啟用)會創建dump轉儲 文件,以執行快速數據導入,避免將所有的數據載入內存引發問題。即使不使用SET autocommit
和 COMMIT
。
- 如果在二級索引鍵上有
UNIQUE
限制,可以在載入時暫時關閉比檢查:
對於較大的表,此操作可以節省大量的磁盤I/O,因為InnoDB
可以使用它的 change buffer(change buffer的主要目的是將對二級索引的數據操作緩存下來,以此減少二級索引的隨機IO,並達到操作合並的效果)來批量寫二級索引記錄。確保數據不包含重復鍵。
- 如果表鍵包含
FOREIGN KEY
限制。可以再導入期間關閉此限制: - 使用批量多行插入,以減少不必要客戶端服務器間通信:
適用於任何類型表。
- 當批量插入涉及自增列時,設置
innodb_autoinc_lock_mode
= 2 (默認1,0:traditional;1:consecutive;2:interleaved)。 - 以主鍵的順序進行批量插入會更快。
InnoDB
表主鍵索引未聚簇索引(clustered index, 以主鍵的順序訪問會很快)。特別是對於無法完全載入緩存的大表。 - 全文索引導入:
- 表創建時定義新列
FTS_DOC_ID
,
類型BIGINT UNSIGNED NOT NULL
,,列上定義索引FTS_DOC_ID_INDEX
,如下: - 載入數據。
- 數據載入后,在相應的列上創建全文索引。
FTS_DOC_ID BIGINT unsigned NOT NULL AUTO_INCREMENT,
title varchar(255) NOT NULL DEFAULT '',
) ENGINE=InnoDB DEFAULT CHARSET=latin1;CREATE UNIQUE INDEX FTS_DOC_ID_INDEX on t1(FTS_DOC_ID);
在innodb存儲引擎中,為了支持全文檢索,必須有一個列與word進行映射,在innodb中這個列被命名為FTS_DOC_ID,其類型必須為BIGINT UNSIGNED NOT NULL,並且innodb存儲引擎會在該列上加上一個名為FTS_DOC_ID_INDEX的唯一索引。上述操作由innodb存儲引擎自己完成,用戶也可以在創建表時手動添加,主要對應的約束條件。
常規的索引是文檔到關鍵詞的映射:文檔——>關鍵詞
倒排索引是關鍵詞到文檔的映射:關鍵詞——>文檔
全文索引通過關鍵字找到關鍵字所在文檔,可以提高查詢效率
表創建時添加FTS_DOC_ID
列,確保FTS_DOC_ID
列跟隨全文索引列同步更新(FTS_DOC_ID
必須隨着相應的 INSERT
或者 UPDATE
變更單調遞增)。如果不添加FTS_DOC_ID
列,而讓InnoDB
來管理DOC IDs,InnoDB
也會在執行CREATE FULLTEXT INDEX
時添加隱藏列FTS_DOC_ID
。這樣,則需要進行額外的表重建過程,造成不必要的性能影響。
InnoDB查詢優化
創建適當的索引以優化查詢,通用指引如下:
- 將關鍵查詢最常用的的列包含近表主鍵中。
- 主鍵列不要使用過多的列或者過長的列。因為二級索引包含主鍵,過大的主鍵會造成磁盤I/O及內存的浪費。
- 不要在每個列上創建二級索引,一個查詢只能使用一個索引。對於極少使用的列及列選擇性不大的列創建索引對於查詢優化不會有太大幫助。如果針對一個表的查詢非常多,則需要找到能夠有助於最多查詢的多列主鍵。如果索引列能夠覆蓋所需要查詢的數據列,那么就可以只使用索引進行數據查詢,而不需要從表中獲取數據。
- 如果某一列的數據不能為NULL,那么在創建表的時候將其生命為
NOT NULL
。優化器以此可以更高的決定最優使用索引。 - 可以針對但查詢事務進行相應的優化。
InnoDB DDL操作優化
- 許多DDL操作,如表和索引的(
CREATE
,ALTER
, 及DROP
語句) 可以在線之行。 - 使用
TRUNCATE TABLE
代替DELETE FROM
tbl_name
來清空表,外鍵限制可以使得TRUNCATE
語句如普通的DELETE
語句般操作。這種情景下,一系列如DROP TABLE
及CREATE TABLE
語句會執行的很快。 - 因為主鍵InnoDB表的存儲結構是高度整合的,主鍵的變更會引起整張表的重構。最好將逐漸定義包含在表創建語句中,避免不必要的后期更改。
InnoDB磁盤I/O優化
如果數據庫的設計及sql操作優化都遵循了最佳實踐,數據庫依然因為I/O負載而反應非常慢,那么就需要針對I/O進行專門的優化。可以通過Unix 的top
工具,或者Windows 的任務管理器來查看工作負載,如果低於70%,那么負載則主要在磁盤。
- 增大緩存:
InnoDB
緩存中的數據訪問不需要磁盤I/O,使用innodb_buffer_pool_size
設置。建議設置為系統內存的50 ~ 75%。
- 調整刷盤策略:
某些版本GNU/Linux 系統,使用fsync()
或者相關方法進行刷盤時,速度會非常慢。這時,如果影響到數據庫性能,那么可以通過設置innodb_flush_method
= O_DSYNC
來變更刷盤策略。
- 調整Linux系統AIO磁盤調度策略為noop(單隊列) 或者deadline(讀、寫隊列):cfg(公平隊列,比較復雜):
InnoDB
在Linux系統上使用異步I/O 子系統(本地AIO)通過預讀和寫請求來執行數據文件讀寫。配置變量innodb_use_native_aio
默認啟用。磁盤調度策略對本地 AIO印象比較大。通常建議設置為noop 或者 deadline。
- Solaris 10 x86_64架構建議使用direct I/O策略:
- RAID配置。
- non-rotational 存儲:
Non-rotational 存儲適用於隨機讀寫;rotational存儲相反適用於順序讀寫。不同的存儲設備對數據及日志的操作類型不同。
數據庫隨機讀寫類文件包括:file-per-table 和general tablespace數據文件, undo tablespace文件和temporary tablespace 文件。順序讀寫類文件包括:InnoDB
system tablespace 文件(基於 doublewrite buffering and change buffering) 及日志文件( binary log 文件和redo log 文件等)。
使用non-rotational 存儲時,需要對一下配置進行優化:
crc32
算法使用了一種更快的一致性檢查算法,對於高速存儲設備,推薦使用。
針對rotational存儲設備優化。對於non-rotational設備或者混用情景,則需禁用。
默認的200設定對於低端non-rotational存儲設備已經足夠。其它,酌情設置。
默認2000 針對non-rotational 存儲。
如果redo logs存儲在non-rotational設備,可以開率禁用詞選項來減少日志。
如果redo logs 存儲在non-rotational 存儲設備,設置此選項最大讀寫緩存。
設置此值以匹配磁盤internal sector size。早期的SSD設備為4KB,一些新版本的SSD能夠支持到16KB。默認的額InnoDB
也大小為16KB。盡量是的數據庫也大小和存儲設備的塊大小接近,減少無法一次寫入磁盤的數據大小。
binary logs 存儲在non-rotational 設備情況下,如果所有的表都有主鍵,那么可以將此變量設置為最小來減少日志。
Ensure that TRIM support is enabled for your operating system. It is typically enabled by default.
- 增大I/O容量以減少backlogs負載:
如果吞吐量會因為檢測點操作而不間斷的降低,那么可以開率增加 innodb_io_capacity
的值。值越大,數據庫刷盤頻率會增大,從而避免了因為backlog的操作帶來的吞吐量的影響。
- 調整數據庫I/O容量。
如果系統能夠滿足nnoDB
刷盤操作。可以考慮減小innodb_io_capacity
配置。通常需要將此變量盡量設置低一些。(SHOW ENGINE INNODB STATUS
)
- 將系統表空間文件存儲在Fusion-io(做存儲系統的公司) 設備。
如果使用支持原子寫的Fusion-io設備存儲系統表空間文件(“ibdata files”) ,那么可以對doublewrite buffer-related I/O進行相應的優化。這種情況下,會自動使用Fusion-io設備的原子寫替代doublewrite buffering (innodb_doublewrite
)進行數據的讀寫。這種特性只支持Fusion-io硬件設備及Fusion-io NVMFS Linux應用。可以通過變量innodb_flush_method
= O_DIRECT
進行配置。
Note
設置是全局性的,影響所有設備上的數據讀寫。
- 禁用壓縮數據頁日志:
使用 InnoDB
表壓縮特性時,重新壓縮的圖片數據頁,如果數據有變化,則會寫入redo log。配置變量innodb_log_compressed_pages
默認啟用,防止數據庫恢復期間,因為zlib算法的變化引發數據庫崩潰。如果可以確認zlib版本不會發生變化,那么可以關閉innodb_log_compressed_pages
變量來減少重壓縮產生的redo log 負載。