優化表和選擇儲存引擎
具有以下 MyISAM 表:
+----------------------+-----------------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------------------+-----------------------+------+-----+---------+-------+ | player_id | int(11) | NO | PRI | NULL | | | date | date | NO | PRI | NULL | | | time | int(2) | NO | PRI | NULL | | | minutes_online | decimal(5,0) | NO | | 0 | | | minutes_playing | decimal(5,0) | NO | | 0 | | | minutes_chatting | decimal(5,0) | NO | | 0 | | | minutes_away | decimal(5,0) | NO | | 0 | | +----------------------+-----------------------+------+-----+---------+-------+
在哪裡:
date
將按以下格式保存日期YYYY-MM-DD
time
將保持小時,從 0 到 23。只有小時,而不是分鐘minute_[...]
將保存在給定一天的給定小時內花費的分鐘數。自然地,從 0 到 60 並且連續所有分鐘的總和不能高於 60。每分鐘都會檢查每個玩家的狀態,並相應地更新表格。線上玩家的數量通常在 500 到 1200 之間。這意味著每分鐘寫入一次數據(使用
INSERT ... ON DUPLICATE KEY
),並且每分鐘可能有數百或數千行。**讀音呢?**任何人(玩家或客人)都可以訪問任何玩家的統計數據(只要玩家不隱藏它)。該網站每天的點擊量在 8,000 到 10,000 次之間。為了避免讀取不斷寫入的表,我將主查詢(從過去 6 個月中提取統計數據)記憶體 1 小時。使用 PHP 記憶體,將數組保存到文件中。這意味著要訪問給定玩家的最後 6 個月,每小時只能讀取一次。
但是……有AJAX。玩家,而不是客人,可以通過 AJAX 選擇任何一天或任何天數,甚至超過 6 個月。這些查詢不會保存到文件中。這意味著,如果有人決定多次檢查範圍或日期,那麼他每次選擇日期/範圍時都會碰上桌子。
該表有 1600 萬行。還有其他像這樣的表有更多的行,但讓我們繼續使用這個。查詢表有時需要很長時間(大約 15 秒)。這就是我記憶體查詢的原因。顯然,由於這是 MyISAM,每次查詢命中表時,表都會被鎖定。
所以,考慮到這一切,我想出了以下優化版本:
+----------------------+-----------------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------------------+-----------------------+------+-----+---------+-------+ | player_id | mediumint(8) unsigned | NO | PRI | NULL | | | date | date | NO | PRI | NULL | | | time | tinyint(2) | NO | PRI | NULL | | | minutes_online | tinyint(2) | NO | | 0 | | | minutes_playing | tinyint(2) | NO | | 0 | | | minutes_chatting | tinyint(2) | NO | | 0 | | | minutes_away | tinyint(2) | NO | | 0 | | +----------------------+-----------------------+------+-----+---------+-------+
它將保存相同的資訊,但使用更少的空間。希望至少查詢可以快一點。
這是更好的版本嗎?你會建議一個不同的架構嗎?
使用 InnoDB 會更好嗎?
編輯
範例查詢
這裡有兩個一直在執行的查詢。
選擇過去 3 個月中按天分組的所有活動。當訪問者選擇自定義範圍時,也會使用此查詢。
SELECT Stats.date, SUM(Stats.minutes_online), SUM(Stats.minutes_playing), SUM(Stats.minutes_chatting), SUM(Stats.minutes_away) FROM Stats WHERE Stats.player_id = '99999999' AND Stats.date BETWEEN '2012-05-31' AND '2012-08-31' GROUP BY Stats.date
如果訪問者想查看某一天的活動:
SELECT Stats.date, Stats.minutes_online, Stats.minutes_playing, Stats.minutes_chatting, Stats.minutes_away FROM Stats WHERE Stats.player_id = '99999999' AND Stats.date = '2012-05-31' AND Stats.time = '11'
在改用 InnoDB 之前,這些查詢,主要是第一個,過去需要很長時間。當然,桌子大部分時間都是鎖著的。更改後,查詢速度要快得多。插入/更新也更快。
我知道如果有數百名訪問者同時檢查同一玩家的統計數據,數據庫可能會變慢。但考慮到流量,這種情況不太可能發生,因為我記憶體了第一個查詢。
你的第二個版本肯定更好。為進一步提高…
TINYINT –> TINYINT UNSIGNED(是的,這並沒有什麼區別;只是更乾淨)
DATE + TIME –> DATETIME 或 TIMESTAMP …
- 拆分 DATE 和 TIME 幾乎總是一個壞主意;拆分 DATETIME 比將它重新組合起來要容易得多。
- 大小:DATE:3,時間為 TIMYINT:1,DATETIME:8,TIMESTAMP:4
- 您可以玩遊戲將日期和時間轉換為代表小時開始的 DATETIME 或 TIMESTAMP(您似乎需要)。
MyISAM 與 InnoDB –
- InnoDB 從崩潰中恢復得更好
- 與十年前的“智慧”不同,InnoDB 可能會更快。
- InnoDB 將佔用 2-3 倍的磁碟空間。16M 行 –> 在 GB 下?打哈欠。
長查詢,長鎖定時間——好吧,讓我們看看那些頑皮的查詢。聽起來他們會從“匯總表”中受益。順便說一下,我建議每天進行小計。這樣的表將小 24 倍以上,因此如果您需要“表掃描”,則速度會更快。(TINYINT –> SMALLINT)