不使用外鍵的性能影響(許多專用 1:許多鍵鍵表與非 fk 通用鍵鍵表)
我們使用大量關聯表來管理系統中各種不同對象之間的一對多關係。
為了說明這個問題,兩個例子是:
users
,events
,ass_users_events
. 將ass_users_events
僅包含User_ID
和Event_ID
列,兩者都具有外鍵關係。projects
,tasks
,ass_projects_tasks
. 將ass_projects_tasks
僅包含Project_ID
和Task_ID
列,兩者都具有外鍵關係。NB1:每個對象表實際上都使用了一個自動遞增整數主鍵的組合,加上一個帶有唯一索引的 UUID 列,該唯一索引是實際的記錄 ID。出於這個問題的目的,我們只使用 UUID,所以沒有發生衝突的機會。
NB2:我們使用這種格式而不是直接的外鍵列/索引的原因是,現實比這個例子復雜得多,有許多不同的連接到許多不同的表,我們不希望 ORM 做很多每次載入記錄時進行不必要的工作。
問題是我們開始為系統中許多其他現有對象的系統中幾乎所有新對像類型創建這些關聯表,從長遠來看,這似乎不可持續,我們最終會得到數百種關聯類型。
我們正在考慮的一個潛在解決方案是擺脫所有目前的關聯表,而是創建一個具有以下結構的表:
obj_1_id
,obj_1_type
,obj_2_id
,obj_2_type
。每列都會被索引,可能是複合索引(即INDEX object_1 (obj_1_type,obj_1_id)
和INDEX object_2 (obj_2_type,obj_2_id)
)。上面 ass 表中的範例將變為:
abc,user,123,event
和def,project,456,task
。該解決方案使我們能夠靈活地在不同對象之間建構任意數量的關係類型,並且在 ass 表上有足夠的索引來保持性能。我的問題是**,僅在連接期間使用索引與在較小的表中定義外鍵關係(但可能有數百個)是否有缺點?**
編輯: 我認為下面有一些誤解,所以這可能會澄清一些事情:
- 我使用多態對象結構,但每個對像都儲存在它自己的表中,即使用者、產品、類別、事件等。
- 至於建議的 ass 表,它將只有 4 個功能欄位,具有非常簡單的數據類型(加上一個
ai_col
作為主要)。cols的數據類型為 varchar(10),type
id 的數據類型為 CHAR(32) / BINARY(16)。SELECT 只會有一個連接到 ass 表,即
SELECT event.* FROM event INNER JOIN ass ON ass.obj_1_type = 'event' AND ass.Object_ID_1 = event.Event_ID AND ass.obj_2_type='location'
我沒有理由一次要求其中兩個對象,我只會通過存在連結來過濾來自一個表的結果。
- UUID 在這裡是真正的唯一 ID,但每個表都使用
ai_col
一個 INNODB 構造來提高其聚集索引的性能。- 這是一個讀取繁重的環境,我不太關心插入/刪除性能。
- 我們使用 handlersocket 進行簡單的讀寫查詢,消除了 SQL 成本。
- 在此特定案例中,該表的完整性在最終一致時是可以接受的。
**編輯 2:**為了清楚地說明這一點,這就是為什麼……如果我在自己的表中有 200 個對象,我希望能夠以所有可能的方式連結,我要麼必須有 199 個外鍵關係每個對象表,否則我必須創建 40,000 個表,其中包含所有可能的組合,然後是所有業務邏輯。使用我們正在考慮的方法,有一個表,我在應用程序級別處理了 RI,我表示最終一致是可以的。
不使用外鍵不會影響性能。外鍵約束通過約束可以插入外鍵列的值來簡單地強制引用完整性。
如果您使用 UUIDS 作為標識符,為什麼需要關聯表中的類型列?
正如您提到的那樣,以傳統方式對此進行建模會產生大量表。例如,刪除使用者將導致 RDBMS 檢查每個外鍵約束以確保不違反非鍵約束,這可能需要一些時間!使用您提出的方法,RDBMS 將沒有要檢查的外鍵約束,並且使用者將被更快地刪除。因此,您實際上可以在刪除對象時獲得更好的性能。正如您所指出的,您必須有一個使關聯表最終保持一致的後台作業。關於這一點,您可以考慮保留已刪除對象的日誌並針對關聯表重放它。這將比對每一行執行存在性檢查以查看引用的對像是否仍然存在更好。
我已經使用這種方法幾年了,以允許任何與其他任何事物相關的東西,並且沒有遇到任何問題。