1. 程式人生 > >Nhibernate的Session和StatelessSession性能比較

Nhibernate的Session和StatelessSession性能比較

nec 代碼 blog aso add 實例 oracle10g bat lock

NhibernateSessionStatelessSession性能比較

作者:鄧家海

一個月入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。

Nhibernate的Session和StatelessSession性能比較