[NewLife.XCode]擴展屬性(替代多表關聯Join提升性能)

NewLife.XCode是一個有10多年曆史的開源數據中間件,支持nfx/netstandard,由新生命團隊(2002~2019)開發完成並維護至今,以下簡稱XCode。

整個系列教程會大量結合示例代碼和運行日誌來進行深入分析,蘊含多年開發經驗於其中,代表作有百億級大數據實時計算項目。

開源地址:https://github.com/NewLifeX/X (求star, 743+)

 

爲何需要擴展屬性

XCode不支持多表關聯查詢,單表查詢利於優化以及分表分庫,一切Join都可以藉助擴展屬性實現,配合緩存使用可以達到更好的效果!

(XCode前期支持多表關聯,直到2008年才正式廢除)

“擴展屬性”是2007年起XCode特有叫法,不同於其它任何場景的意義(如Silverlight/WPF)

 

前文《實體類詳解》中有提到一個學生班級的實體類模型,一個典型需求是查詢學生列表時希望暫時班級名稱或者其它信息。於是有:

select s.*, c.name where student s left join class c on s.classid=c.id

sql語法千變萬化,如果要支持多表關聯join,就很難做到統一查詢風格,更是難以優化。

於是XCode放棄支持多表關聯,寧可拆分爲多次查詢。令人驚訝的是,不僅性能沒有下降,反而大大提升了,主要因爲單表小查詢有多級緩存的加持!

 

擴展屬性用法

使用擴展屬性來實現關聯查詢,本質上就是多次查詢!

如上,這是一個經典的多表關聯場景,學生錶帶有班級ID字段,同樣還有產品和分類表等等。

這是XCode根據模型文件自動生成的代碼,因爲字段名ClassID剛好是Class表加上它的主鍵ID,並且都是整型。

對於實體對象來說,student.Name是學生名稱,student.ClassName是班級名稱。

看起來它們就像是一張表的屬性字段,這就是擴展屬性的由來,不僅僅是多表關聯屬性,還可以是其它屬性,爲區別於數據字段屬性,統稱爲擴展屬性!

 

擴展屬性先準備一個Class屬性,再加一個ClassName,主要是爲了方便某些場合使用 student.Class。

當然,執行一次查詢得到student後,不論是訪問student.Class還是訪問student.ClassName,都會觸發一次Class.FindByID,可以理解爲執行一次查詢(不一定是數據庫)。

在Web頁面上,如果每頁顯示20個學生,那麼先要執行 select * from student limit 20,然後展示學生列表時,因爲需要班級名稱,觸發擴展屬性查詢。

可以認爲,理論上這個頁面需要查詢1+20次。

 

擴展屬性爲什麼不寫成 public Class Class => Class.FindByID(ClassID) 呢?

其實雖然看起來簡單,但是還得考慮一個可能,同一個student對象可能多次訪問student.ClassName,這麼寫豈不是每次訪問都會執行Class.FindByID?

因此,XCode設計了擴展集合Extends,可以認爲是一個字典,每個擴展屬性都經過它走一遭,如果查詢過一次就緩存起來,避免反覆查詢。

Extends.Get第一個屬性是擴展屬性名,決定是否有緩存,第二個是沒有緩存時要執行的委託。

這就是擴展屬性緩存,默認緩存時間10秒,足夠抗住短期內成千上萬次重複調用。

 

擴展屬性優化

儘管有Extends擴展屬性緩存支持,但每個對象還是要執行一次Class.FindByID查詢,損耗還是不小的。

在XCode裏面,根據主鍵而設計的查詢(如FindByID)往往帶有很好的緩存優化。

如上,這是XCode默認生成的代碼,當Class表數據不足1000行時,走實體緩存。

也就是說,Meta.Cache時執行一次 select * from student 返回所有行,並緩存起來。後面的Find實際上是在緩存中查找。實體緩存有效期默認10秒。

只有數據表達到1000行,才走 Find(_.ID==id) 數據庫查詢 select * from class where id=? 。然而XCode下層還有一個數據層緩存,相同select查詢默認緩存10秒

此外,也可以根據業務特點採用單對象緩存,例如跨境電商的產品種類特別多(10萬+),可以採用字典式的單對象緩存。

 

因此,在學生類那邊看起來訪問屬性會觸發多次Class.FindByID,殊不知它內部別有洞天,三級緩存(實體緩存、對象緩存、數據緩存)等着伺候!(後續專文介紹緩存)

 

回到開頭的例子,一個列表頁顯示20個學生,理論查詢次數1+20次,在多級緩存加持的擴展屬性下,99.99%的時候只會查詢1次,而班級表的關聯,完全在內存緩存中進行。

一次簡單的單表查詢,顯然要比join班級表的查詢要快得多!

 

魔方的特別支持

在上述擴展屬性中,注意到ClassName屬性上有一個Map特性。

它表示映射,本對象的ClassID字段,映射到Class類的ID字段。

在魔方列表頁中,本來顯示冷冰冰ClassID的地方,就會變爲顯示友好的ClassName。

在魔方表單頁中,本來顯示數字框ClassID的地方,也會變成顯示下拉列表框。

如果下拉列表庫內容很多,可以精簡Map特性,只要第一個參數指明本地字段,而不需要第二第三字段表示的目標字段。此時在魔方表單頁會顯示數字框,但是後面顯示ClassName

 

到此,你還認爲多次查詢一定比單次Join慢嗎?

 

系列教程

NewLife.XCode教程系列[2019版]

  1. 增刪改查入門。快速展現用法,代碼配置連接字符串
  2. 數據模型文件。建立表格字段和索引,名字以及數據類型規範,推薦字段(時間,用戶,IP)
  3. 實體類詳解。數據類業務類,泛型基類,接口
  4. 功能設置。連接字符串,調試開關,SQL日誌,慢日誌,參數化,執行超時。代碼與配置文件設置,連接字符串局部設置
  5. 反向工程。自動建立數據庫數據表
  6. 數據初始化。InitData寫入初始化數據
  7. 高級增刪改。重載攔截,自增字段,Valid驗證,實體模型(時間,用戶,IP)
  8. 髒數據。如何產生,怎麼利用
  9. 增量累加。高併發統計
  10. 事務處理。單表和多表,不同連接,多種寫法
  11. 擴展屬性。多表關聯,Map映射
  12. 高級查詢。複雜條件,分頁,自定義擴展FieldItem,查總記錄數,查彙總統計
  13. 數據層緩存。Sql緩存,更新機制
  14. 實體緩存。全表整理緩存,更新機制
  15. 對象緩存。字典緩存,適用用戶等數據較多場景。
  16. 百億級性能。字段精煉,索引完備,合理查詢,充分利用緩存
  17. 實體工廠。元數據,通用處理程序
  18. 角色權限。Membership
  19. 導入導出。Xml,Json,二進制,網絡或文件
  20. 分表分庫。常見拆分邏輯
  21. 高級統計。聚合統計,分組統計
  22. 批量寫入。批量插入,批量Upsert,異步保存
  23. 實體隊列。寫入級緩存,提升性能。
  24. 備份同步。備份數據,恢復數據,同步數據
  25. 數據服務。提供RPC接口服務,遠程執行查詢,例如SQLite網絡版
  26. 大數據分析。ETL抽取,調度計算處理,結果持久化

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章