加快大型數據庫查詢
我正在使用 MySQL 和 InnoDB 執行一個大型本地數據庫。數據大小約為 30GB,密鑰大小約為 6GB。該機器具有 16GB 的 RAM。
桌子看起來像這樣
symbol VARCHAR(10), iso_datestamp VARCHAR(14), -- a lot more columns with values with mostly DECIMAL(26,6)s
我在符號和 iso_datesamp 上有 BTREE 索引。
我執行的操作大多是這樣執行的更新:
DELETE FROM table WHERE symbol = 'XXX'
INSERT INTO table (columns) VALUES (...)
需要這樣做以避免數據不一致,而不是僅僅附加數據。
我也經常執行分析,如
SELECT FROM table WHERE symbol in ('XXX', 'YYY', ...) [AND iso_datestamp > '2015...']
每個符號我有大約 3000 個條目。
性能問題:一旦表開始增長超過 10GB,
DELETE
andINSERT
語句就會從通常不到 0.4 秒到超過 1 秒甚至超過 5 秒甚至更多,因為表不斷增長。RAM 沒有限制(根據top
)。我需要平均 1 秒左右(當然更快也可以)
DELETE
…INSERT
我現在有兩個想法:
- 使用雜湊索引而不是 btree 索引,因為我只做相等操作
- 使用符號和範圍對錶進行分區
這些是可行的策略嗎?兩者都做,只有一個?我應該做的其他事情來提高速度?
iso_datestamp
到 MySQLTIMESTAMP
,以便它可以是 5 個字節而不是 15 個。- 使用 InnoDB,而不是 MyISAM。(這對以下某些項目至關重要。)
- 使用
CHARACTER SET ascii
(或latin1
),除非您需要多字節 utf8/utf8mb4。PRIMARY KEY(symbol, datestamp)
- auto_increment 浪費空間和索引。以上是一個“自然PK” *,*它提供了一個有效的索引。
PARTITION BY RANGE(TO_DAYS(datestamp))
– 這將顯著加快刪除“舊”行的速度。- 有了那個 PK 和分區,您可能不需要任何二級索引,從而完全消除了您提到的 6GB。(如果您有未充分處理的查詢,請告訴我。)
- 拍攝不超過50個分區;不要打擾子分區。(見下面的連結)
- 用於
DROP PARTITION
按日期非常有效地刪除。(見下面的連結)如果您要按符號刪除,請告訴我。DECIMAL(26,6)
佔用 12 個字節。考慮縮小它和/或移動到 8-byteDOUBLE
。注意:Double 的 ~16 位有效數字和二進制格式可能會導致舍入問題。- 考慮使用 3 字節
MEDIUMINT INT
的 asymbol_id
並將實際symbol
名稱標準化為另一個(小)表。- 指標(例如,“alpha”)可能應該是 4-byte
FLOATs
。它的 7 位有效數字對於這種情況來說太過分了。- 設置
innodb_buffer_pool_size
為大約 70% 的可用記憶體(在考慮了應用程序、Web 伺服器等之後)- 以 100-1000 個為一組插入新數據。這實際上是一次執行速度的 10 倍。
這些更改應該有助於解決您的各種問題。
目前,由於多種原因,性能正在跌落懸崖。上面的提示會使懸崖延遲大約兩倍,並使懸崖不那麼陡峭。在某些情況下,懸崖被消除了。
此外,我的技巧將減少所需的 I/O——通過集群、引用位置、更好的記憶體等。
DELETE 序列化非常糟糕。您可以通過按符號雜湊對錶進行分區來緩解這種情況。這將允許您在這些 DELETE 上實現更好的並發性。
對於 INSERT,可能有幫助的事情是:
- 將事務隔離級別設置為已送出讀
- 確保你有足夠的(innodb_log_file_size x innodb_log_files_in_group)。這些應該乘以浸泡您的峰值寫入時間所需的數量。
- 根據您的表上是否有 auto_increment 列,innodb_autoinc_lock_mode=2 將有助於高並發。
- 確保您的 innodb_io_capacity_max 設置為您的儲存堆棧可以通過 16KB IOPS 提供的值。
通過優化調整將MySQL 數據目錄放在ZFS 上將提高您的寫入性能。
此外,請確保您的 innodb_buffer_pool_size 設置正確(經驗法則是 RAM 的 50-80%,具體取決於您擁有多少 RAM)。您最熱門的索引和主鍵應該適合 RAM。在您的情況下,如果它是專用數據庫伺服器,12GB 似乎是一個合理的數量。