這絕對是 ORM 的使用者,開發人員與 DBAs 共同想要問的議題,到底我使用了 ORM 和使用傳統的 ADO.NET 下 SQL 指令的方式會差多少? 這個問題不但會發生在 Entity Framework 上,也會發生在 NHibernate 等 ORM Framework 內,連同我自己在這個系列文中開發的 ORM 機制也會受到影響。

我們在前面的 ORM 1-9 系列文中看到了整個開發 ORM 所需要的技術和方法,然後也實作出了一個完整的 ORM Framework (再簡單不過的版本),我們在這個 ORM Framework 中用了下列技術:

  1. Reflection,這是最重要的技術,沒有它,就無法動態的對應 Property 和 Field。
  2. Attribute,少了它就沒辦法宣告自訂對應 (在 ORM (9) 有程式化的對應)。
  3. Type Converter,沒有它的話就無法轉換資料,尤其是資料中有列舉值時。
  4. SQL Generation,自動產生 SQL 指令,讓開發人員不再需要撰寫 SQL。
  5. ADO.NET,最核心的物件,而且只使用 Connection, Command 與 DataReader。
  6. Provider Pattern,利用它來切換不同的資料提供者。
  7. LINQ Expression,在 ORM (9) 時實作 Coded Map 之用。

而在使用這些技術時,有哪些情況會影響到效能?

1. 動態產生的 SQL

當 ORM Framework 接到物件存取資料庫的要求時,通常會有一個機制來產生 SQL 指令,再交由 ADO.NET 存取資料,但是你知道 ORM Framework 產生的 SQL 指令的樣貌嗎?因為 SQL 指令的下法會影響 DBMS 存取資料的效能,而 SQL 指令若是由 ORM Framework 產生的話,表示開發人員沒辦法自己去修改 SQL 來做這件事。最常見的解決方法,就是自己寫 SQL,而多數的 ORM Framework 都還是會提供一個讓開發人員直接執行 SQL 的方法,而回傳的可以是 DataReader 或是由開發人員指定的 DTO (POCO) 物件。

ORM 被 DBA 攻擊的其中一個點,就是 DBA 無法介入寫 SQL 這件事,對於一些具有規模的系統來說,SQL 都是 DBA 在寫的,但卻無法相容於 ORM Framework 或是開發人員所寫的 POCO 物件,這時就會有問題,而且多數的大系統,通常不會允許由開發人員自己寫 SQL 存取表格,而是用檢視表或是預存程序或函數等資料庫物件來存取,所以主流的 ORM Framework 都有提供可存取資料庫物件的功能,這樣 DBA 就能將 SQL 指令的效能影響降低 (或是把效能責任推給開發人員…)。但對於小型專案而言,SQL 指令要由開發人員來處理,因此若要用 ORM 來替代寫 SQL 的工作,就等於要承受 ORM 產生的 SQL 的效能問題,但還是可以透過自己寫 SQL 來處理,所以最後的問題還是會落在 SQL 的寫法好不好。

Entity Framework 和 NHibernate 等主流 ORM 都會花不少的心思來調校自動產生的 SQL,也會提供輸出它所產生的 SQL 的方式,例如 EF 有 ObjectQuery.ToTraceString(), NH 也有提供方法捕捉。雖然我自己寫的 ORM Framework 沒有提供這功能,但仍可以改寫它來提供這樣的能力。而 DBA 或開發人員就可以利用得到的 SQL 來做調校工作,例如調整索引或是更新統計資料等工作,來調整 SQL 執行的效能,或是自己寫 SQL 來做。

2. 撈取資料時的方式

使用 ADO.NET 撈取資料的話,用 DataReader 一定是最快的,搭配上 Sequential Access 那幾乎等於無敵,除非開發人員要自己處理 SQL protocols (ex: SQL Server 的 TDS),否則以 ADO.NET 內建的資料存取方式來說,DataReader 己經是最快的作法了,若是轉成 DataTable 再讀入,會有兩段的 Type Casting 負擔,不符合 ORM Framework 的基本要求。

Sequential Access 的好處是會逐項依目前游標的位置來讀取,不必推算資料流的位置,所以速度很快,但因為它不會推算資料流,所以它也不能捲回前面的資料流位置,這點在使用上要特別注意。

3. 對應欄位與屬性時的處理

這點就是 ORM 最大的罩門了,要做到程式化的 Property <-> Field 對應,一定得依賴 Reflection,而 Reflection 最花時間的地方莫過於取得 Property Metadata 這一塊,所以如果可以事先快取 Property Metadata 的話,在讀取資料時就可以快近三四倍 (這是我自己實證的經驗)。

再來是 Type Casting 的問題,這個其實很難避免,數值型別的話還好有 Convert 物件可用,但若是自訂型別的話,就只能自己寫一個 Type Converter 來做,這時用泛型會比用 object 來得優,因為可以省下 box/unbox 的效能損耗。

4. 延遲載入

延遲載入 (Lazy Loading) 在前面的 ORM 文章有提到,在有關聯性的資料模型內,有時使用 ORM 產生物件時,不一定會用到關聯的資料,所以我們可以選擇要用時再載入,如此可以省下一次 SQL 的負載,而且就算是物件本身,也可以只載入像 ID/Name 這種必要資料,若需要更多資料時再進一步載入即可,但負面的問題就是它需要好幾個 SQL 才能完全載入必要的資料,但如果一次載入大資料和分多次載入小資料相比,我想後者的效能會好些。

5. 其他的考量

懂得整個資料存取與資料庫處理流程的開發人員,而且是在特定的 DBMS 之下時,可以針對該 DBMS 做更進一步的最佳化,例如像 SQL 查詢計畫快取 (Query Plan Caching),SQL 快取 (SQL Caching),物件快取 (Object Caching) 等,簡化 SQL 和 DBMS 的查詢負擔,並且加快整個 ORM Framework 的速度,另外像是語法的下法 (ex: LINQ to Entities 的查詢下法) 和關聯的使用技巧等都會間接影響到 ORM 的效能。

說了這麼多,我所要強調的是,ORM 是好用的資料存取方式,但它一定會有某種程度不可避免的效能損耗,我們可以做的事,就是將這些損耗降到最低,讓整體的資料存取速度加快,所以當看到了 ORM 的好處時,也不能忘了它也有缺點,而事先了解它的缺點並予以注意或改善,再來好好的享受它的優點,也不錯。

Reference:

http://msdn.microsoft.com/en-us/data/hh949853.aspx

===================================

 

[Data Access] ORM 原理 (11): 效能議題的更多相关文章

  1. DevExpress v18.1新版亮点——Data Access篇

    用户界面套包DevExpress v18.1日前正式发布,本站将以连载的形式为大家介绍各版本新增内容.本文将介绍了DevExpress Data Access v18.1 的新功能,快来下载试用新版本 ...

  2. .NET Core Data Access

    .NET Core was released a few months ago, and data access libraries for most databases, both relation ...

  3. Spring.NET的中间数据层(Middle Tier Data Access)——事务管理(Transaction management)

    简介 Spring.NET为事务管理提供了一个持久化抽象(consistent abstraction ),其优点如下: 为不同事务API,例如ADO.NET,Enterprise Services, ...

  4. FunDA(0)- Functional Data Access accessible to all

    大数据.多核CPU驱动了函数式编程模式的兴起.因为函数式编程更适合多线程.复杂.安全的大型软件编程.但是,对许多有应用软件开发经验的编程者来说,函数式编程模式是一种全新的.甚至抽象的概念,可能需要很长 ...

  5. Top 10 steps to optimize data access in SQL Server

    2009年04月28日 Top 10 steps to optimize data access in SQL Server: Part I (use indexing) 2009年06月01日 To ...

  6. [翻译]比较ADO.NET中的不同数据访问技术(Performance Comparison:Data Access Techniques)

    Performance Comparison: Data Access Techniques Priya DhawanMicrosoft Developer Network January 2002 ...

  7. Apache Cloudstack Development 101 -- Data Access Layer

    刚接触CloudStack,也是第一次翻译英文文档,限于水平有限,不当之处欢迎拍砖! 原文地址:https://cwiki.apache.org/confluence/display/CloudSta ...

  8. Data access between different DBMS and other txt/csv data source by DB Query Analyzer

        1 About DB Query Analyzer DB Query Analyzer is presented by Master Genfeng,Ma from Chinese Mainl ...

  9. DevExpress v17.2新版亮点——Data Access

    用户界面套包DevExpress v17.2日前终于正式发布,本站将以连载的形式为大家介绍各版本新增内容.本文将介绍了Data Access v17.2 的新功能,快来下载试用新版本! 新的API可在 ...

随机推荐

  1. 【BZOJ1502】[NOI2005]月下柠檬树 Simpson积分

    [BZOJ1502][NOI2005]月下柠檬树 Description 李哲非常非常喜欢柠檬树,特别是在静静的夜晚,当天空中有一弯明月温柔地照亮地面上的景物时,他必会悠闲地坐在他亲手植下的那棵柠檬树 ...

  2. 2-sat入门(tarjan)hdu(3062)

    hdu3062 Party Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) To ...

  3. 号称简明实用的django上手教程

    1 几个基本概念 前置条件:假设读者基本Python语言基础,或者具备某种编程语言的基础.你还熟悉web开发环境,懂些css,js,db等. Django是什么? Django是一个开放源代码的Web ...

  4. XTU 1267 - Highway - [树的直径][2017湘潭邀请赛H题(江苏省赛)]

    这道题可能有毒……总之一会儿能过一会儿不能过的,搞的我很心烦…… 依然是上次2017江苏省赛的题目,之前期末考试结束了之后有想补一下这道题,当时比较懵逼不知道怎么做……看了题解也不是很懂……就只好放弃 ...

  5. epub格式的电纸书

    epub格式是电纸书的一种标准,epub格式电纸书采用Zip压缩格式来来包裹书籍内容. 所以我们可以把epub格式的文件改成zip格式. 利用zip压缩工具解压文件. epub格式的文件阅读器 win ...

  6. 图论——最小生成树_prim

    今天是最小生成树的prim的算法,因为本人水平有限所以堆优化都不是很会啊,但邻接表好像出了点小差错所以上邻接矩阵比较好一点,尽管比Kruskal慢了很多很多但这种贪心思想还是要学习的.从第一条边开始取 ...

  7. python 时间字符串和时间戳之间的转换

    https://blog.csdn.net/qq_37193537/article/details/78987949   1.将字符串的时间转换为时间戳    方法:        a = " ...

  8. Redis主从同步及哨兵原理

    1.复制过程 复制过程大致分为6个过程: 流程图如下: 1)保存主节点信息 执行slaveof后从节点只保存主节点的地址信息便直接返回,这时建立复制流程还没有开始,在从节点执行info replica ...

  9. kubernetes实战(十一):k8s使用openLDAP统一认证

    1.基本概念 为了方便管理和集成jenkins,k8s.harbor.jenkins均使用openLDAP统一认证. 2.部署openLDAP 此处将openLDAP部署在k8s上,openLDAP可 ...

  10. 01_Python 基础课程安排

    Python 基础课程安排 目标 明确基础班课程内容 课程清单 序号 内容 目标 01 Linux 基础 让大家对 Ubuntu 的使用从很 陌生 达到 灵活操作 02 Python 基础 涵盖 Py ...