Mysql

優化表和選擇儲存引擎

  • August 31, 2012

具有以下 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)

引用自:https://dba.stackexchange.com/questions/22761