码迷,mamicode.com
首页 > 数据库 > 详细

在线程中用 OracleBulkCopy 导至 CPU 百分百

时间:2015-03-18 15:27:47      阅读:324      评论:0      收藏:0      [点我收藏+]

标签:

抓取到的数据, 要批量写数据到 ORACLE , 一开始是用的EF, 处理速度很慢.
主要表现在验证数据上(db.GetValidationErrors), 每分钟才能写 1000条不到.
换成 EnterpriceLibrary.Validation (Validation.Validate) 后, 验证速度大增, 1000条只需几秒钟.
但是整体速度还是慢, 是因为每条数据都被EF转换为一个INSERT语句.
 
我花了一些时间用 OracleBulkCopy (需要引用客户端下面的 Oracle.DataAccess.dll ) 重写了这部份的逻辑, 1000条写到写ORACLE中也不过几秒时间, 直接从牛车跨越到高铁时代.
 
 1         private void BulkCopy(string connStr, DataTable dt, string tblName) { 
 2             if (dt.Rows.Count > 0) { 
 3                 using (var bc = new OracleBulkCopy(connStr)) { 
 4                     bc.DestinationTableName = tblName; 
 5                     foreach (DataColumn col in dt.Columns) { 
 6                         bc.ColumnMappings.Add(col.ColumnName, col.ColumnName); 
 7                     } 
 8                     bc.WriteToServer(dt); 
 9                 } 
10             } 
11         }

connStr 是 ORACLE 的连接字符串, tblName 是目标表的表名.

 1        /// <summary> 
 2         /// 将 List 转换为 DataTable, 只针对 T 中 的 public Property 
 3         /// </summary> 
 4         /// <typeparam name="T"></typeparam> 
 5         /// <param name="list"></param> 
 6         public static DataTable ToDataTable<T>(this List<T> list) where T : class { 
 7             if (list == null) 
 8                 return null; 
 9 
10 
11             Type type = typeof(T); 
12             var ps = type.GetProperties().Where(p => p.CanWrite && (p.PropertyType.IsValueType || p.PropertyType.IsPrimitive || p.PropertyType == typeof(String))); 
13             Type targetType; 
14             NullableConverter nullableConvert; 
15             List<DataColumn> cols = new List<DataColumn>(); 
16             foreach (var p in ps) { 
17                 if (p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { 
18                     nullableConvert = new NullableConverter(p.PropertyType); 
19                     targetType = nullableConvert.UnderlyingType; 
20                     cols.Add(new DataColumn(p.Name, targetType)); 
21                 } else { 
22                     cols.Add(new DataColumn(p.Name, p.PropertyType)); 
23                 } 
24             } 
25 
26 
27             DataTable dt = new DataTable(); 
28             dt.Columns.AddRange(cols.ToArray()); 
29 
30 
31             list.ForEach((l) => { 
32                 List<object> objs = new List<object>(); 
33                 objs.AddRange(ps.Select(p => p.GetValue(l, null))); 
34                 dt.Rows.Add(objs.ToArray()); 
35             }); 
36 
37 
38             return dt; 
39         } 
这段代码是我几年前用存储过程的时代写的, 现在还能用上.
只将T中的可写的 值类型, 基元类型 和字符串映射到 DataTable 中.
因为 EF 生成的实体类中, 导航属性并不是于数据库中的字段.
CLR 中的基元类型有:
Boolean 、 Byte 、 SByte 、 Int16 、 UInt16 、 Int32 、 UInt32 、 Int64 、 UInt64 、 IntPtr 、 UIntPtr 、 Char 、 Double  和  Single 。 
https://msdn.microsoft.com/zh-cn/library/system.type.isprimitive.aspx 
 
String 不是基元类型, 而且是地址引用的, 但它是一个特殊.
除 字符串,基元类型,值类型的属性, 我还真找不出来哪个可以映射到数据库中.
 
本地调试没问题后,放到服务器上, CPU居然高居不下.
生成DUMP文件, 用 WinDbg 查看: 
 
0:000> .load sos.dll 
0:000> !threadpool 
CPU utilization: 96% 
Worker Thread: Total: 0 Running: 0 Idle: 0 MaxLimit: 32767 MinLimit: 3 
Work Request in Queue: 0 
-------------------------------------- 
Number of Timers: 0 
-------------------------------------- 
Completion Port Thread:Total: 1 Free: 1 MaxFree: 6 CurrentLimit: 1 MaxLimit: 1000 MinLimit: 3 

CPU 使用率 96% 

杳看线程执行时间: 

0:000> !runaway 
 User Mode Time 
  Thread       Time 
  21:13dc      0 days 0:39:58.140 
  24:13d4      0 days 0:08:41.750 
  27:11ac      0 days 0:01:29.906 
   5:1250      0 days 0:00:18.796 

查看21号线程的堆栈:

~21s
!clrstack
OS Thread Id: 0x13dc (21) 
Child SP               IP Call Site 
000000002595e7f8 00000000777e85d7 [HelperMethodFrame: 000000002595e7f8]  
000000002595e910 000007fe9482b688 *** ERROR: Module load completed but symbols could not be loaded for Oracle.DataAccess.dll 
Oracle.DataAccess.Client.OracleTuningAgent.DoScan() 
000000002595e950 000007fe9481d28f Oracle.DataAccess.Client.OracleTuningAgent.TuningFunction() 
000000002595e9c0 000007fef0bdd0b5 *** WARNING: Unable to verify checksum for mscorlib.ni.dll 
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) 
000000002595eb20 000007fef0bdce19 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) 
000000002595eb50 000007fef0bdcdd7 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) 
000000002595eba0 000007fef0b50301 System.Threading.ThreadHelper.ThreadStart() 
000000002595eeb8 000007fef1c7ffe3 [GCFrame: 000000002595eeb8]  
000000002595f1e8 000007fef1c7ffe3 [DebuggerU2MCatchHandlerFrame: 000000002595f1e8]  
000000002595f3c8 000007fef1c7ffe3 [ContextTransitionFrame: 000000002595f3c8]  
000000002595f5b8 000007fef1c7ffe3 [DebuggerU2MCatchHandlerFrame: 000000002595f5b8]  

 

接连查了几个线程的堆栈, 都是这个: 
Oracle.DataAccess.Client.OracleTuningAgent.XXX 

 

搜了一下 Oracle.DataAccess.Client.OracleTuningAgent.TuningFunction : 
http://stackoverflow.com/questions/2782169/oracle-data-provider-pegs-iis-worker-process-when-web-site-is-stopped 

This has been fixed in 11.2.0.2 and in Patch 9966926 ORACLE 11G 11.2.0.1 PATCH 5 BUG FOR WINDOWS (64-BIT AMD64 AND INTEL EM64T). 
Or WORKAROUND: is to disable self tuning by adding "Self Tuning=false" to the connection string. 

看不大懂, 但是里面提及了 Oracle.DataAccess.Client 的版本. 
这个DLL 我是从客户端: 11.2.0 中考出来的, 版本是: 2.112.1.0  

Self Tuning 从 VS 的数据库连接管理中, 可以看出它的意思是:
对连接启用或禁用自我优化
技术分享
 
具体有什么功效, 什么影响没有去查证. 先不管.
 
从ORACLE上下载了一个 ODAC121021_x64 (客户端) , 200多M, 安装后, ODP.NET 目录下面有两个文件夹 2.X 和 4
引用 4 下面的 Oracle.DataAccess.dll (版本: 4.121.2.0),  本地运行也是毫无压力,很完美.
拿这个DLL直接替换到服务器上, 结果 CPU 是不高了, 但是连写都不写了! 
不知道是不是需要安装最新的客户端, 没有试验. 也不能去实验, 因为基础环境一改,会影响一大片.
 
 
索性还拿 2.x 的版本, 在连接字符串中加入 Self Tunning = False, 放到服务器上, 数据正常写入了, CPU也不高了!
 

在线程中用 OracleBulkCopy 导至 CPU 百分百

标签:

原文地址:http://www.cnblogs.com/xling/p/4347165.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!