注意:以下文章是我经过实践后写的(一些测试可能存在偏差),并总结出我对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 | ||
| 准备时间(秒): |
|
| ||
| 测试持续时间: | <?xml:namespace prefix = st1 />00:00:05:00 | 00:00:05:00 | ||
| 测试迭代次数: | 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。迟下有时间我把操作对象缓存池的实现写一编文章。