這絕對是 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. Unix/Linux 查看文件大小

    ls -l help.html-rw-r--r--  1 william  wheel  40960 Jul 18 00:59 development.sqlite3 40960 就是文件的大小. d ...

  2. 【BZOJ4355】Play with sequence 线段树

    [BZOJ4355]Play with sequence Description 维护一个长度为N的序列a,现在有三种操作: 1)给出参数U,V,C,将a[U],a[U+1],...,a[V-1],a ...

  3. 【BZOJ2310】ParkII 插头DP

    [BZOJ2310]ParkII Description Hnoi2007-Day1有一道题目 Park:给你一个 m * n 的矩阵,每个矩阵内有个权值V(i,j) (可能为负数),要求找一条回路, ...

  4. SOA架构商城二 框架搭建

    1.创建父工程 创建Maven工程pingyougou-parent,选择packaging类型为pom ,在pom.xml文件中添加锁定版本信息dependencyManagement与plugin ...

  5. 如何使用Jquery 引入css文件

    如何使用Jquery 引入css文件: $("head").append("<link>");var toolbarCss = $("he ...

  6. Python3设置在shell脚本中自动补全功能的方法

    本篇博客将会简短的介绍,如何在ubuntu中设置python自动补全功能. 需求:由于python中的内建函数较多,我们在百纳乘时,可能记不清函数的名字,同时自动补全功能,加快了我们开发的效率. 方法 ...

  7. POJ-1952 BUY LOW, BUY LOWER(线性DP)

    BUY LOW, BUY LOWER Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 9244 Accepted: 3226 De ...

  8. Python Cookbook 笔记--12章并发编程

    <Python Cookbook(第3版)中文版> 1.队列queue的有些方法是线程不安全的,在多线程中最好别用 2.需要限制一段代码的并发访问量时,用信号量.不要把信号量当做普通的锁来 ...

  9. AOP 详解

    1. 需求:统计方法执行的性能情况(来源:<精通Spring 4.x>) // 性能监视类 PerformanceMonitor package com.noodles.proxy; pu ...

  10. 看病要排队--hdu1873

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1873 运用优先队列写就行了 #include<stdio.h> #include< ...