Index

我應該規範化這個數據庫嗎?

  • January 10, 2016

我有兩種用於在使用者之間儲存消息(即時消息)的數據庫設計。這將是一個群聊系統。

我想到了兩種桌子設計。

一個是這樣的:

CREATE TABLE `mssg_grp_user` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `user_id` int(10) unsigned NOT NULL,
 `grp_id` int(10) unsigned NOT NULL,
 `body` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
 `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
 PRIMARY KEY (`created_at`,`grp_id`,`user_id`,`id`),
 KEY `mssg_grp_user_user_id_foreign` (`user_id`),
 KEY `mssg_grp_user_grp_id_foreign` (`grp_id`),
 KEY `mssg_id_is` (`id`),
 CONSTRAINT `mssg_grp_user_grp_id_foreign` FOREIGN KEY (`grp_id`) REFERENCES `groups` (`id`) ON UPDATE CASCADE,
 CONSTRAINT `mssg_grp_user_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=350201 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

另一個是這樣的: 在此處輸入圖像描述

在第二個中: Messages 表具有以下鍵

alter table messages 
drop primary key ,
add primary key (created_at, id),
add INDEX mssg_id_ix (id)    ; 

並且在mssg_grps表中有一個主鍵(grp_id,mssg_id)。

我的主要查詢是檢索組 ID 列表中的最新 50 條消息,並且比某個時間更新。對於第二個,我執行了這個查詢:

SELECT * FROM test.mssg_grp a LEFT JOIN test.messages b ON a.mssg_id = b.id
Where b.created_at >"1970-01-03 19:08:21"
AND a.grp_id IN(8,2,1,2,5,6,3)
order by created_at limit 50;

這花了很長時間。一個查詢(有 20 萬條消息)需要 0.1 秒,100 個並髮使用者大約需要 2 分鐘。

然後我決定合併表(即更改我的數據庫模式)並切換到第一個。

為了獲得相同的數據,我執行了這個查詢:

Select sql_no_cache * from test.mssg_grp_user where  grp_id IN (8,1,5,6,7,8,9) and created_at>888880   
order by  created_at desc limit 50;

對於100 個並髮使用者,這需要 0.1 秒。這顯然要快得多。我的問題是:

  • 為什麼加入+訂單需要這麼多時間?
  • 我應該切換到第一個設計嗎?(正文欄位最多可以有 500 個字元)

第一個解決方案顯然比第二個更有效,因為您不需要執行連接(每個連接都會減慢查詢速度,或多或少取決於系統生成的訪問計劃)。

更多,我認為第二種解決方案有一些問題,因為從架構看來,您使用id裡面的列mssg_grp作為引用表的外鍵mssg,而在該表中,您使用的幾列created_at和作為主鍵id,這與外鍵的定義,從中可以假設它id已經是mssg.

因此,如果從圖中看起來,和之間的關係mssgmssg_grp1:1,那麼您應該選擇仍然標準化且更有效的第一個解決方案

1)左連接比內連接慢。這是第一個原因。二是為優化查詢添加特殊索引。使用查詢的解釋計劃

2)如果你想使用結果表,那麼我建議你使用材料視圖。它會更快,你可以不考慮欄位的長度。

ps View 不是表格,因此您將節省時間,而不是在第三個表格中插入

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