MySQL 5.7 - B-Tree 索引、性能和碎片
我有一個帶有時間戳列的大表,它包含大量數據(幾百萬行)。
每當在此表的時間戳列上執行按範圍過濾的查詢時,根據范圍的大小,它需要相當長的時間(2 秒以上)。這個列有一個為它創建的索引,這會讓我相信查詢返回應該更快。
我注意到通過銷毀索引並重新添加它,查詢的性能輕鬆提高了 40%。
Bellow 是一個簡單的表格結構範例:
create table foo ( id int(11) not null auto_increment, fk_id int(11) not null, datecolumn timestamp, primary key (id, fk_id), key idx_timestamp (datecolumn) constraint fk_fk_id foreign key (fk_id) references bla (id) ) engine = InnoDB;
重新創建索引會使查詢執行得更快的任何原因以及如何將其作為維護的一部分?我知道 MySQL InnoDB 預設索引是 BTree,它可能會變得混亂,但它應該有一種優化或重新組織這些索引的方法,因此查詢性能還不錯。
注意:我跑了
optimize table foo; flush tables;
這些似乎並沒有解決與索引相關的任何問題。
當您使用解釋時,它會顯示一些正在使用的索引,但主要是顯示類型範圍,並且在這種類型下,大多數行數被分析為只返回其中的一些。
fk_id
是 where 子句的一部分,必須是,它也是複合鍵的一部分,這意味著返回結果是確定性的。查詢基本上是這樣的:
select id, fk_id, datecolumn, bar.id, bar.name, bar.type from foo join bar on bar.id = foo.fk_id where foo.datecolumn between '2016-01-01 05:00:00' and '2017-01-01 04:59:59' and foo.fk_id in (1, 2, 4)
即使查詢是一個簡單的選擇 where datecolumn between range 我得到相同的結果。
在大量 random 之後,BTree 塊固有地從 100% 滿(實際上大約 15/16)到大約 69% 滿
INSERTs
。這可能佔您看到的 40%。但請注意,它不會隨著時間的推移而變慢。因此,我認為頻繁OPTIMIZE TABLE
是浪費時間,特別是如果您需要始終可以訪問該表。而且,您的 40% 速度將迅速下降。
我希望
FLUSH TABLES
實際上會受到傷害——因為它會從 RAM 中刪除記憶體的資訊。為了討論查詢,請向我們展示查詢、它
EXPLAIN SELECT ...
的和可能SHOW CREATE TABLE bla
的。
foo
本質上是兩張表,一張是有序(id, fk_id, datecolumn)
的,另一張是有序的(datecolumn, id, fk_id)
。另外,我認為,(fk_id, id)
(否datecolumn
)的二級索引。如果
fk_id
是WHERE
子句的一部分,則它們都可能不是最佳的。附加物
(來自OP的評論:)
select id, fk_id, datecolumn, bar.id, bar.name, bar.type from foo join bar ON bar.id = foo.fk_id where foo.datecolumn between '2016-01-01 05:00:00' AND '2017-01-01 04:59:59' and foo.fk_id in (1, 2, 4)
最優指標是
foo: INDEX(fk_id, datecolumn) -- 3 disjoint ranges bar: PRIMARY KEY(id) -- I assume it is already the PK; if not, then INDEX(id)
請注意,您目前沒有
foo
. 添加它,您的需求OPTIMIZE TABLE
應該會減少。至於為什麼優化似乎有幫助——可能是記憶體。你有多少記憶體?的價值是
innodb_buffer_pool_size
多少?我更喜歡這種模式:
where foo.datecolumn between '2016-01-01 05:00:00' AND '2016-01-01 05:00:00' + INTERVAL 1 YEAR
(沒有閏年、數據類型等的麻煩)
更多關於索引。
對我來說,桌子設計看起來很糟糕。
PK 是 (id, fk_id),它允許每個“id”多行。真的想要嗎?
時間戳是一個完全動態的時間戳(DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP),需要嗎?
‘datecolumn’ 這個名字表明它是一個’date’,那麼為什麼不使用’date’ 類型呢?