Nhibernate的Session和StatelessSession性能比较
作者:邓家海
一个月入30K的大神有一天跟我说:我当年在你现在这个阶段,还在吊儿郎当呢!所以你努力吧!
有时候,一个想法就是1000秒,另一个想法只需要10秒
前言:
近段时间忙着给一个政府机关推送数据到国家数据库,数据量一共加起来有六十几万吧。这么多数据使用人工推送显然是一个不小的工作量,所以必须要使用程序来处理代码。为了方便,我们选择使用Nhibernate框架来进行CURD操作。有人大呼脑残,哈哈哈···为了方便偷懒,就是什么事情都敢做。六十几万,也算是一个大数据了吧。写程序可能才使用了三天左右的时间,然后就是跑数据。结果跑数据跑了两天多。1000条数据使用了1分钟左右。当时时间也很紧急,没有想太多。只好硬着头皮日夜加班跑数据。
这段时间回来公司有空闲,后面还要继续推送数据。于是领导就交代我一个任务,想一个跑数据更快捷的方案。
首先想到的是使用原生的ADO。但是,我又不甘心写太多代码,我在想为什么NHIBERNATE会这么慢?究竟是什么原因。查了一番资料。终于找到了可行的方案。自己顺便做了一个实验。证实此方案可行。原来是NHIBERNATE 的Session和StateLessSession这两个的原因。
测试环境:
Windows7
Access
Hibernate4
数据量:20000数据
首先看Session实现代码
1 using System; 2 3 using System.Collections.Generic; 4 5 using System.Linq; 6 7 using System.Text; 8 9 using System.Threading.Tasks; 10 11 using NHibernate; 12 13 using NHibernate.Cfg; 14 15 16 17 namespace BDC.Framework 18 19 { 20 21 public class DataSourceFactoryStateless 22 23 { 24 25 private static Dictionary<string, IStatelessSession> staticSessionDictionary = new Dictionary<string, IStatelessSession>(); 26 27 private static object lockObject = new object(); 28 29 private const string OracleAssembly = "BDC.ZcServer"; 30 31 private const string AccessAssembly = "BDC.Standard"; 32 33 34 35 public static IStatelessSession GetSession<T>() where T:class 36 37 { 38 39 string assembly = typeof(T).Assembly.GetName().Name; 40 41 IStatelessSession _session = null; 42 43 bool isFind = false; 44 45 if (staticSessionDictionary.Keys.Contains(assembly)) { _session = staticSessionDictionary[assembly]; isFind = true; } 46 47 try 48 49 { 50 51 if(_session == null) 52 53 { 54 55 lock (lockObject) 56 57 { 58 59 ISessionFactory factory = null; 60 61 switch (assembly) { 62 63 case OracleAssembly: 64 65 factory = LoadOracleConfig(); 66 67 break; 68 69 case AccessAssembly: 70 71 factory = LoadAccessConfig(); 72 73 break; 74 75 default: 76 77 throw new Exception("没有找到对应的程序集"); 78 79 } 80 81 // _session = factory.OpenSession(); 82 83 _session=factory.OpenStatelessSession(); 84 85 if (isFind) { staticSessionDictionary[assembly] = _session; } 86 87 else { staticSessionDictionary.Add(assembly, _session); } 88 89 } 90 91 } 92 93 94 95 return _session; 96 97 }catch(Exception ex) 98 99 { 100 101 throw new Exception("数据库初始化失败"); 102 103 } 104 105 } 106 107 108 109 private static ISessionFactory LoadOracleConfig() 110 111 { 112 113 Configuration config = new Configuration(); 114 115 config.SetProperty("dialect", "NHibernate.Dialect.Oracle10gDialect"); 116 117 config.SetProperty("hibernate.show_sql", "true"); 118 119 config.SetProperty("connection.driver_class", "NHibernate.Driver.OracleManagedDataClientDriver"); 120 121 config.SetProperty("connection.provider", "NHibernate.Connection.DriverConnectionProvider"); 122 123 config.SetProperty("proxyfactory.factory_class", "NHibernate.Bytecode.DefaultProxyFactoryFactory, NHibernate"); 124 125 config.SetProperty("connection.isolation", "ReadCommitted"); 126 127 config.SetProperty("connection.release_mode", "auto"); 128 129 config.SetProperty("adonet.batch_size", "500"); 130 131 config.SetProperty("current_session_context_class", "call"); 132 133 config.SetProperty("cache.use_query_cache", "false"); 134 135 config.AddAssembly("BDC.ZcServer"); 136 137 config.SetProperty("connection.connection_string", OracleConnectionString.ConnectionString); 138 139 140 141 return config.BuildSessionFactory(); 142 143 } 144 145 146 147 private static ISessionFactory LoadAccessConfig() 148 149 { 150 151 Configuration config = new Configuration(); 152 153 config.SetProperty("dialect", "NHibernate.JetDriver.JetDialect, NHibernate.JetDriver"); 154 155 config.SetProperty("hibernate.show_sql", "true"); 156 157 config.SetProperty("connection.driver_class", "NHibernate.JetDriver.JetDriver, NHibernate.JetDriver"); 158 159 config.SetProperty("connection.provider","NHibernate.Connection.DriverConnectionProvider"); 160 161 config.SetProperty("proxyfactory.factory_class", "NHibernate.Bytecode.DefaultProxyFactoryFactory, NHibernate"); 162 163 config.SetProperty("connection.isolation", "ReadCommitted"); 164 165 config.SetProperty("connection.release_mode", "auto"); 166 167 config.SetProperty("adonet.batch_size", "500"); 168 169 config.SetProperty("current_session_context_class", "call"); 170 171 config.SetProperty("cache.use_query_cache", "false"); 172 173 config.AddAssembly("BDC.Standard"); 174 175 config.SetProperty("connection.connection_string", AccessConnectionString.ConnectionString); 176 177 178 179 return config.BuildSessionFactory(); 180 181 } 182 183 184 185 186 187 } 188 189 }
Session使用时间
StatelessSession实现代码:
1 using System; 2 3 using System.Collections.Generic; 4 5 using System.Linq; 6 7 using System.Text; 8 9 using System.Threading.Tasks; 10 11 using NHibernate; 12 13 using NHibernate.Cfg; 14 15 16 17 namespace BDC.Framework 18 19 { 20 21 public class DataSourceFactory 22 23 { 24 25 private static Dictionary<string, ISession> staticSessionDictionary = new Dictionary<string, ISession>(); 26 27 private static object lockObject = new object(); 28 29 private const string OracleAssembly = "BDC.ZcServer"; 30 31 private const string AccessAssembly = "BDC.Standard"; 32 33 34 35 public static ISession GetSession<T>() where T:class 36 37 { 38 39 string assembly = typeof(T).Assembly.GetName().Name; 40 41 ISession _session = null; 42 43 bool isFind = false; 44 45 if (staticSessionDictionary.Keys.Contains(assembly)) { _session = staticSessionDictionary[assembly]; isFind = true; } 46 47 try 48 49 { 50 51 if(_session == null) 52 53 { 54 55 lock (lockObject) 56 57 { 58 59 ISessionFactory factory = null; 60 61 switch (assembly) { 62 63 case OracleAssembly: 64 65 factory = LoadOracleConfig(); 66 67 break; 68 69 case AccessAssembly: 70 71 factory = LoadAccessConfig(); 72 73 break; 74 75 default: 76 77 throw new Exception("没有找到对应的程序集"); 78 79 } 80 81 _session = factory.OpenSession(); 82 83 if (isFind) { staticSessionDictionary[assembly] = _session; } 84 85 else { staticSessionDictionary.Add(assembly, _session); } 86 87 } 88 89 } 90 91 92 93 return _session; 94 95 }catch(Exception ex) 96 97 { 98 99 throw new Exception("数据库初始化失败"); 100 101 } 102 103 } 104 105 106 107 private static ISessionFactory LoadOracleConfig() 108 109 { 110 111 Configuration config = new Configuration(); 112 113 config.SetProperty("dialect", "NHibernate.Dialect.Oracle10gDialect"); 114 115 config.SetProperty("hibernate.show_sql", "true"); 116 117 config.SetProperty("connection.driver_class", "NHibernate.Driver.OracleManagedDataClientDriver"); 118 119 config.SetProperty("connection.provider", "NHibernate.Connection.DriverConnectionProvider"); 120 121 config.SetProperty("proxyfactory.factory_class", "NHibernate.Bytecode.DefaultProxyFactoryFactory, NHibernate"); 122 123 config.SetProperty("connection.isolation", "ReadCommitted"); 124 125 config.SetProperty("connection.release_mode", "auto"); 126 127 config.SetProperty("adonet.batch_size", "500"); 128 129 config.SetProperty("current_session_context_class", "call"); 130 131 config.SetProperty("cache.use_query_cache", "false"); 132 133 config.AddAssembly("BDC.ZcServer"); 134 135 config.SetProperty("connection.connection_string", OracleConnectionString.ConnectionString); 136 137 138 139 return config.BuildSessionFactory(); 140 141 } 142 143 144 145 private static ISessionFactory LoadAccessConfig() 146 147 { 148 149 Configuration config = new Configuration(); 150 151 config.SetProperty("dialect", "NHibernate.JetDriver.JetDialect, NHibernate.JetDriver"); 152 153 config.SetProperty("hibernate.show_sql", "true"); 154 155 config.SetProperty("connection.driver_class", "NHibernate.JetDriver.JetDriver, NHibernate.JetDriver"); 156 157 config.SetProperty("connection.provider","NHibernate.Connection.DriverConnectionProvider"); 158 159 config.SetProperty("proxyfactory.factory_class", "NHibernate.Bytecode.DefaultProxyFactoryFactory, NHibernate"); 160 161 config.SetProperty("connection.isolation", "ReadCommitted"); 162 163 config.SetProperty("connection.release_mode", "auto"); 164 165 config.SetProperty("adonet.batch_size", "500"); 166 167 config.SetProperty("current_session_context_class", "call"); 168 169 config.SetProperty("cache.use_query_cache", "false"); 170 171 config.AddAssembly("BDC.Standard"); 172 173 config.SetProperty("connection.connection_string", AccessConnectionString.ConnectionString); 174 175 176 177 return config.BuildSessionFactory(); 178 179 } 180 181 182 183 184 185 } 186 187 } 188 189
StatelessSession使用时间
ADO执行原生SQL
1 using System; 2 3 using System.Collections.Generic; 4 5 using System.Data.OleDb; 6 7 using System.Linq; 8 9 using System.Text; 10 11 using System.Threading.Tasks; 12 13 14 15 namespace BDC.Standard 16 17 { 18 19 public class AccessConnectionTest 20 21 { 22 23 public bool AccessTest() 24 25 { 26 27 OleDbConnection mycon = null; 28 29 OleDbCommand mycom = null; 30 31 try { 32 33 string strcon = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=E:\XXXXXXSXB.mdb;"; 34 35 mycon = new OleDbConnection(strcon); 36 37 mycom = mycon.CreateCommand(); 38 39 mycon.Open(); 40 41 42 43 for( int j= 0; j < 20000; j++) 44 45 { 46 47 48 49 50 51 52 53 string sql = string.Format("insert into sqr(QLRDLJG,QLRDLRDH,QLRDLRMC,QLRFRDH,QLRFRMC,QLRMC,QLRTXDZ,QLRYB,QLRZJH,QLRZJZL,QXDM,YSDM,YWH,YWRDLJG,YWRDLRDH,YWRDLRMC,YWRFRDH,YWRFRMC,YWRMC,YWRTXDZ,YWRYB,YWRZJH,YWRZJZL) values(‘1‘,‘1‘,‘1‘,‘1‘,‘1‘,‘1‘,‘1‘,‘1‘,‘1‘,‘1‘,‘1‘,‘1‘,{0},‘1‘,‘1‘,‘1‘,‘1‘,‘1‘,‘1‘,‘1‘,‘1‘,‘1‘,‘1‘) ", j); 54 55 mycom.CommandText = sql; 56 57 int i = mycom.ExecuteNonQuery(); 58 59 } 60 61 62 63 64 65 66 67 return true; 68 69 } 70 71 catch(Exception ex) 72 73 { 74 75 return false; 76 77 } 78 79 finally 80 81 { 82 83 mycom.Dispose(); 84 85 mycon.Close(); 86 87 88 89 90 91 } 92 93 } 94 95 } 96 97 }
ADO执行原生SQL使用时间:
解析:综上就发现,Session效率非常低下,足足运行了1000多秒,就是23多分钟。再看后面两种方法,效率差不多。一个10秒,一个11秒。这么说,我其实还是可以偷懒的。继续使用NHIBERNATE,只需要换一个方法就可以了。那么?为什么这两个方法差别如此大呢。而且前面的方法运行一段时间会失败并抛出内存溢出异常,这是因为 Hibernate 把所有新插入的MotherCat实例在 session 级别的缓存区进行了缓存的缘故。其实不知道你们发现没有,StatelessSession 接口使用insert, update和 delete操作是操作数据库的, Session 使用save, saveOrUpdate 和delete 。区别就在Save和Insert这两个方法。
原因:使用StatelessSession(无状态 session)接口是使用流的方式来操作数据,大大提升效率。它没有持久上下文。不存在高层的生命周期。没有多级缓存,它也不管你数据的准确性,是否重复,是否存在脏数据,不级联更新数据。也不会出发Hibernate的事务和触发器等,简单的来说,就相当于一个底层的JDBC。
使用注意:它没有事务,没有缓存,没有脏数据检查。所以我们使用在系统的时候,千万要小心使用,不然会造成脏数据,污染数据库,或者导致数据不正确。而且如果系统抛异常,则是很危险的,数据是马上执行存取操作的。数据写到一半,抛个异常,这个数据就错了。而且还不会回滚。
综上,对已有数据,要求效率的时候,而且保证数据不会出现问题,异或,自己对异常,脏数据等有一套方案,可以使用NHIBERNATE的StateLessSession.不是特别追求速度的话,还是使用Session。