运用O/R Mapping技术处理数据性能并不低
注意:以下文章是我经过实践后写的(一些测试可能存在偏差),并总结出我对O/R Mapping的一些看法,并不能代表实际中所有情况。
相信大家对O/R Mapping不感到陌生,运用O/R Mapping模式去开发数据库应用可以节省大量的工作量,特别是烦琐的SQL语句编写。虽然O/R Mapping有它的好处,但也有人说它不好有很大性能问题;他们认为反复地将对象和SQL之间的转换和大量的反射操作会带来很大性能问题。我觉得性能问题是相对的,对于Hibernate或NHibernate这样完善的O/R Mapping组件来说,它在数据操作上的性能要比我们普通开发人员写SQL语句操作数据的性能要高。对于编写代码不太严紧的开发人员来,使用好的O/R Mapping组件给系统性能上有较大的提高。
可能有些人看了以上说法不太相信,O/R Mapping组件操作比直接SQL语句操作在运算过程多出了这么多的工作,怎么可能还会提高呢?经过实践,如果很好利用缓存机制的确能做了。
为了证实这一点,大家请看下测试结果(其中所用到O/R Mapping组件是我自己编写)测试的原理是分别用组件和SQL在页面上操作数据库,然后通过Microsoft Application Center Test来测试页面的并发数。为了证实并发数的有效性我核对了请求数和数据库插入的记录数,在整个测试过程中两者是一致的。
测试的环境
P4 2.4G,1G(DDR333)内存,40G硬盘
SQLSERVER2000+SP3
Windows2003Server+sp1
.NetFrameWork1.1
测试代码一:
private void Page_Load(object sender, System.EventArgs e)
{
// 在此处放置用户代码以初始化页面
Test.Entitys.User user = new Test.Entitys.User();
user.ID = Guid.NewGuid().ToString();
user.Name = "henry";
user.Age = 27;
user.Brityday = DateTime.Parse("1979-1-28");
user.Remark ="kfc";
using(HFSoft.Data.IDataSession session = _Container.OpenSession())
{
session.Open();
session.Save(user);
}
Response.Write(user.ID);
}
测试代码二:
private void Page_Load(object sender, System.EventArgs e)
{
// 在此处放置用户代码以初始化页面
string strSQL = "insert into [User](ID,Name,Age,Brityday,Remark) Values(@p1,@p2,@p3,@p4,@p5)";
System.Data.SqlClient.SqlParameter[] param = new System.Data.SqlClient.SqlParameter[5];
param[0] = new System.Data.SqlClient.SqlParameter("@p1",Guid.NewGuid().ToString());
param[1] = new System.Data.SqlClient.SqlParameter("@p2","henry");
param[2] = new System.Data.SqlClient.SqlParameter("@p3",27);
param[3] = new System.Data.SqlClient.SqlParameter("@p4",DateTime.Parse("1979-1-28"));
param[4] = new System.Data.SqlClient.SqlParameter("@p5","kfc");
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand();
cmd.CommandText = strSQL;
cmd.Parameters.Add(param[0]);
cmd.Parameters.Add(param[1]);
cmd.Parameters.Add(param[2]);
cmd.Parameters.Add(param[3]);
cmd.Parameters.Add(param[4]);
using(System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection("data source=.;initial catalog=NorthWind;user id=sa;pwd=;"))
{
conn.Open();
cmd.Connection = conn;
cmd.ExecuteNonQuery();
conn.Close();
}
}
第一段测试代码看上去简洁,实际上在处理过程要比第二段测试代码多出大量的运算(熟悉O/R Mapping的人很清楚这一点)。接下来看下这两段测试代码的结果。
属性 |
| ||||
|
|
|
(1) |
(2) | |
|
测试类型: |
动态 |
动态 | ||
|
浏览器同时连接数: |
1 |
1 | ||
|
准备时间(秒): |
|
| ||
|
测试持续时间: |
|
| ||
|
测试迭代次数: |
95,903 |
95,254 | ||
|
生成的详细测试结果: |
是 |
是 | ||
|
|
|
|
| |
摘要 |
|
| |||
|
|
|
(1) |
(2) | |
|
请求总数: |
95,904 |
95,255 | ||
|
连接总数: |
95,903 |
95,254 | ||
|
|
|
|
| |
|
每秒平均请求数: |
319.68 |
317.52 | ||
|
首字节平均响应时间(毫秒): |
1.98 |
1.98 | ||
|
末字节平均响应时间(毫秒): |
2.03 |
2.03 | ||
|
每次迭代末字节平均响应时间(毫秒): |
2.03 |
2.03 | ||
|
|
|
|
| |
|
测试中的唯一请求数: |
1 |
1 | ||
|
唯一响应代码数: |
1 |
1 | ||
|
|
|
|
| |
错误计数 |
|
| |||
|
|
|
(1) |
(2) | |
|
HTTP: |
|
| ||
|
DNS: |
|
| ||
|
套接字: |
|
| ||
|
|
|
|
| |
其他网络统计数据 |
|
| |||
|
|
|
(1) |
(2) | |
|
平均带宽(字节/秒): |
444,043.52 |
435,323.35 | ||
|
|
|
|
| |
|
发送字节数(字节): |
39,502,048 |
39,806,190 | ||
|
接收字节数(字节): |
93,711,008 |
90,790,815 | ||
|
|
|
|
| |
|
发送字节平均速率(字节/秒): |
131,673.49 |
132,687.30 | ||
|
接收字节平均速率(字节/秒): |
312,370.03 |
302,636.05 | ||
|
|
|
|
| |
|
连接错误数: |
|
| ||
|
发送错误数: |
|
| ||
|
接收错误数: |
|
| ||
|
超时错误数: |
|
| ||
|
|
|
|
| |
响应代码 |
|
| |||
|
|
|
(1) |
(2) | |
|
Response Code: 200 - 请求已成功完成。 | ||||
|
|
计数: |
95,904 |
95,255 |
|
|
|
百分比(%): |
100.00 |
100.00 |
|
|
|
|
|
|
|
从测试结果看到,自己编写的O/R Mapping在处理数据的性能上比实际编写SQL语句操作数据要高,虽然只是一点点。我没有对Hibernate或NHibernate过行过测试但我相信这些完善的O/R Mapping在性能上要比自己编写的好(特别在缓存机制的处理)!
对于自己编写O/R Mapping来说实现缓存机制是很有必要的,主要有两个:
一、反射信息缓存
反射信息缓存是最重要的,因为在O/R Mapping在实现对象和数据库互操作的同时要进行大量的反射操作。如果每次操作都重新创建反射信息,可想而知那性能是十分低下的。反射信息缓存可以通过hashtable实现,在内存中hashtable的操作速度是非常快的。
二、操作命令对象缓存
操作命令对象缓存相对来说比较复杂(效益远远没反射信息缓存高),因为缓存对象操作的线程必须是唯一的;一个缓存对象同时只能服务于一个线程,在处理上还有锁和解锁的问题。在建立缓存池的情况下也要注意,缓存池缓存操作命令对象范围越小越好,这样你就可以避免在缓存池中识别不同命令对象所带来的运算问题,缓存的数量也不是越多越好除了消耗内存还会影响查找效率。
以上是我在写一个O/R Mapping组件的一些心得,希望对大家有帮助。有兴趣的朋友和我交流可以发邮件或MSN。迟下有时间我把操作对象缓存池的实现写一编文章。