這絕對是 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. [APP] Android 开发笔记 001-环境搭建与命令行创建项目

    1. 安装JDK,SDK JDK http://www.oracle.com/technetwork/java/javase/downloads/index.html Android SDK http ...

  2. vue之用法

    一.安装 对于新手来说,强烈建议大家使用<script>引入 二. 引入vue.js文件 我们能发现,引入vue.js文件之后,Vue被注册为一个全局的变量,它是一个构造函数. 三.使用V ...

  3. CBV流程之View源码解析

    CBV是基于反射实现根据请求方式不同,执行不同的方法. 请求流程:view源码解析 1.urls.py :请求一定来执行视图下的as_view方法.也可以直接点击as_view()来找源码. 2.vi ...

  4. Linux--抓取Appium网站命令

    # 获取各命令url地址 curl http://appium.io/docs/en/commands/device/app/is-app-installed/ 2>/dev/null | gr ...

  5. GlusterFS实战

    预装glusterfs软件包 yum -y install centos-release-gluster37.noarch yum --enablerepo=centos-gluster*-test ...

  6. System.Web.UI.Page.Cache 页面 缓存 清除

    这个也是网上查询到方法,不错记录一下! /// <summary> /// 清空所有的Cache /// </summary> public static void Clear ...

  7. POJ_2533_Longest Ordered Subsequence

    Longest Ordered Subsequence Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 55490   Acc ...

  8. Linux的Application 内存模型---

    Linux的内存模型,一般为: 现在的每个进程使用了全部4G线性空间.在加载程序时内核把程序加载到线性地址0x08048000开始的位置.这个位置当然>128MB.2G开始是共享库,3G开始是内 ...

  9. 有关线程安全的探讨--final、static、单例、线程安全

    我的代码中已经多次使用了线程,然后还非常喜欢使用据说是线程不安全的静态方法,然后又看到很多地方最容易提的问题就是这个东西线程不安全   于是我不免产生了以下几个亟待解决的问题: 什么样的代码是天生线程 ...

  10. Android开发之改动屏幕方向

    有的场景下.我们须要把手机屏幕方向改变,以下是我写的一个样例. xml页面文件: <RelativeLayout xmlns:android="http://schemas.andro ...