怎么提高ArcSDE 写入地理数据库的效率
link: http://blog.csdn.net/linghe301/article/details/20900615
2014-03-14 09:20 2686人阅读 评论(6) 收藏 举报
ArcSDE(193) ArcGIS Engine(30) ST_Geometry(30) Oracle(106) 性能优化(53)
很多用户经常会有将数据写入ArcSDE地理数据库的操作,也时常会写入效率而烦恼,它们渴望不管多大多复杂的数据,尽可能的使用较少的时间写入到ArcSDE中,而往往用户写入ArcSDE也通过依赖于本身的业务需求,使用自定义的代码开发就非常常见了,那影响ArcSDE写入效率的因素就比较多了。
----------------------------------------------------------------------------------
版权所有,文章允许转载,但必须以链接方式注明源地址,否则追究法律责任!
Blog: http://blog.csdn.net/linghe301
----------------------------------------------------------------------------------
代码因素
大多数用户可能还在使用ArcGIS Engine、空间SQL、ArcSDE API和WebAPI进行开发,针对后两者我不是很熟悉,也没有什么发言权,所以更多的以前面两种开发模式为例,看看在代码开发过程中是否有可以优化的余地。
ArcGIS Engine
以下部分可以参考2011年Esri中国开发者大会《Geodatabase高效编程_李圣虎》,特此感谢!
该文档建议ArcGIS Engine的开发者仔细学习,里面介绍了很多提高效率的技巧和细节!
另外大家也可以参考:Geodatabase API best practices
http://resources.arcgis.com/en/help/arcobjects-net/conceptualhelp/index.html#//000100000047000000
1:使用多线程
说实话,ArcGIS Engine是否支持多线程,大家的理解是不一样的。用资深人士解释就是ArcGIS Engine支持多线程,但是不支持多个线程之间的彼此调用。
下面就以更新多个要素类的字段来演示一下多线程的使用方法,以下都是伪代码。
编写一个更新要素类的函数,输入参数为要素类名称
- public class VersionEditing
- {
- /// <summary>
- /// 更新要素类指定字段值
- /// </summary>
- /// <param name="parameter">要素类名称</param>
- public void UpdateFiledValue(object parameter)
- {
- ........
- }
- }
编写多线程函数,调用这个更新要素类的函数
- private void MultiThreadEditing()
- {
- VersionEditing VersionEditing = new GIS.VersionEditing();
- //声明线程对象
- Thread thA = new Thread(new ParameterizedThreadStart(VersionEditing.UpdateFiledValue));
- Thread thB = new Thread(new ParameterizedThreadStart(VersionEditing.UpdateFiledValue));
- //设置线程工作模型
- thA.SetApartmentState(ApartmentState.STA);
- thB.SetApartmentState(ApartmentState.STA);
- //启动线程
- thA.Start("featureclass1");
- thA.Start("featureclass2");
- }
2:使用游标进行地理数据库编辑
如果是批量进行数据编辑,不建议用户使用传统的Create Feature+Store的方法,而是使用insertCursor或者Update Cursor的方法。
- //建议的方法
- public static void InsertFeaturesUsingCursor(IFeatureClass featureClass, List <
- IGeometry > geometryList)
- {
- using(ComReleaser comReleaser = new ComReleaser())
- {
- // Create a feature buffer.
- IFeatureBuffer featureBuffer = featureClass.CreateFeatureBuffer();
- comReleaser.ManageLifetime(featureBuffer);
- // Create an insert cursor.
- IFeatureCursor insertCursor = featureClass.Insert(true);
- comReleaser.ManageLifetime(insertCursor);
- // All of the features to be created are classified as Primary Highways.
- int typeFieldIndex = featureClass.FindField("TYPE");
- featureBuffer.set_Value(typeFieldIndex, "Primary Highway");
- foreach (IGeometry geometry in geometryList)
- {
- // Set the feature buffer's shape and insert it.
- featureBuffer.Shape = geometry;
- insertCursor.InsertFeature(featureBuffer);
- }
- // Flush the buffer to the geodatabase.
- insertCursor.Flush();
- }
- }
测试场景:从本地File Geodatabase中读取数据,向ArcSDE Geodatabase要素类中插入读取的数据;
数据情况:简单的线要素类,2721089条记录;
测试环境:硬件(T9600 CPU 4G内存)软件(Windows 7 64Bit Oracle 11g ArcGIS 10)
备注:测试结果中时间单位为秒
3:游标
游标分为类绑定游标: SearchCursor、UpdateCursor、InsertCursor和非类绑定游标:QueryDef Cursor。在使用游标使用过程中经常会遇到如下情况
- Public Void test()
- {
- workspaceEdit.StartEditOperation();
- IQueryFilter queryFilter = new QueryFilter();
- queryFilter.WhereClause = clause;
- //是否重复绑定游标
- ICursor updateCursor = table.Update(queryFilter, true);
- comReleaser.ManageLifetime(updateCursor);
- int fieldIndex = table.Fields.FindField("FWMC");
- IRow temp = null;
- while ((temp = updateCursor.NextRow()) != null)
- {
- temp.set_Value(fieldIndex, "aaaa");
- updateCursor.UpdateRow(temp);
- }
- workspaceEdit.StopEditOperation();
- }
如果选择TRUE,说明使用资源回收游标,但是下一个游标还指向同一个内存地址,如果是FALSE,说明资源回收游标和内存地址,下一个游标还需要重新分配一个新的内存地址,这样无疑增加了服务器资源的开销,所以没有特殊需求,建议使用TRUE。
关于游标的相关提示:
查询游标(Search Cursor)
- 在编辑会话中初始化SearchCursor可能导致缓存记录提交到数据库(触发DBMS写操作)
- 通过SearchCursor获得的行支持Store和Delete操作
更新游标(Update Cursor)
- 必须使用UpdateCursor提供的的UpdateRow和DeleteRow方法对通过UpdateCursor获得的行进行更新和删除操作
- UpdateCursor提供的UpdateRow方法只能针对通过UpdateCursor获得的当前行进行操作
- UpdateCursor不支持跨编辑操作使用,打开一个新的编辑操作时必须初始化一个新的更新游标
- 使用UpdateCursor对复杂要类类进行更新时必须处于一个编辑会话中
插入游标(Insert Cursor)
- 主要用于批量要素插入
- 使用缓存,定时调用Flush将缓存写入数据库,调用InsertRow和Flush方法时要捕捉异常
- 使用InsertCursor时关闭空间缓存
QueryDef 游标
- 不能针对通过QueryDef Cursor获得的行进行Store和Delete操作
- 在编辑会话中使用QueryDef Cursor会导致缓存提交到数据库
4:资源回收
尽可能使用Esri提供的MangeLifetime来管理ArcGIS Engine对象的生命周期,系统会自动进行维护,而不需要在使用类似以下方式进行释放资源了。(由于版本原因,可能有所不同)
ESRI.ArcGIS.ADF.ComReleaser.ReleaseCOMObject(o);
System.Runtime.InteropServices.Marshal.ReleaseComObject(o);
GC.Collect();
5:如果可能化,使用缓存来提高效率
- /// <summary>
- /// 执行空间缓存
- /// </summary>
- /// <param name="pFWS">工作空间</param>
- /// <param name="pEnv">缓存区域</param>
- private void SpatialCache(IFeatureWorkspace pFWS, IEnvelope pEnv)
- {
- if (m_pWS != null)
- {
- ISpatialCacheManager3 pSCM = m_pWS as ISpatialCacheManager3;
- if (!pSCM.CacheIsFull)
- {
- pSCM.FillCache(pEnv);
- }
- //执行空间操作代码
- //清空缓存
- pSCM.EmptyCache();
- }
- }
更多了解:http://resources.arcgis.com/en/help/arcobjects-net/componenthelp/index.html#//002500000831000000
更多参考:http://resources.arcgis.com/en/help/arcobjects-net/componenthelp/index.html#/IDatasetEdit_Interface/00250000015m000000/
用户也可以查看《Geodatabase高效编程_李圣虎》总结的常见开发错误,都是一些比较典型的可能导致效率的案例。
这个是比较难于发现的问题。ORACLE不会对不匹配的数据类型报错,ORACLE会隐式地把VARCHAR2列的数据类型转换成要被比较的数值型数据类型。
如果是使用ST_geometry函数的话,还需要注意对ST_Geometry函数的使用
http://blog.csdn.net/linghe301/article/category/797871
2:尽可能的使用存储过程
如果可能的话,尽可能的使用存储过程来代替原始的单个SQL语句,这里面就不再解释存储过程的好处了。
3:使用绑定变量而不是传入固定的值
----------------------------------------------------------------------------------
版权所有,文章允许转载,但必须以链接方式注明源地址,否则追究法律责任!
Blog: http://blog.csdn.net/linghe301
----------------------------------------------------------------------------------
网络因素
网络的原因也是用户需要注意的,如果你的网络带宽、延迟都有问题,肯定会影响数据传输的效率的。这里我只是指出这是一个影响因素,因为不专业,所以不去更多的分析这个问题,但是用户需要留意该问题。
----------------------------------------------------------------------------------
版权所有,文章允许转载,但必须以链接方式注明源地址,否则追究法律责任!
Blog: http://blog.csdn.net/linghe301
----------------------------------------------------------------------------------
ArcSDE地理数据库因素(以Oracle为例)
1:空间索引问题
在ArcSDE地理数据库编辑过程中,系统会自动维护该编辑要素对象的空间索引,那么就会耗费一定的服务器资源,特别是在大数据量进行数据插入的过程中,无需进行空间索引的创建,可以等批量插入完成之后,统一创建空间索引,这样无疑就提高了操作效率。
ArcGIS Desktop、ArcToolbox、ArcSDE命令行、Python都提供了管理空间索引的方法,当然ArcGIS Engine也提供了管理空间索引的接口
IFeatureClassLoad.LoadOnlyMode
用户进行更新,需要先查询出需要更新的要素对象,那么不管是通过属性索引或者空间索引进行查询,都需要这些索引对象有效。
而且在编辑完毕之后,还需要对这些索引对象重新创建,保证新编辑对象的索引更新。
启用日志(Logging):用户进行表创建、索引创建、分区以及后续插入等操作产生REDO日志,作为数据恢复的依据。
禁用日志(No Logging):以上相关操作不会产生REDO日志,进而也不能进行数据恢复,但是数据加载效率会大幅度提高。
注意:这些操作是用户进行大数据量新增条件下,而且具有Oracle数据库知识的用户操作。
select flashback_on from v$database;
select 'ALTER TABLESPACE ' || CHR(34) || NAME || CHR(34) || 'NOLOGGING;'
WHERE NAME NOT IN ('SYSTEM','SYSAUX','USERS','TEMP','UNDOTBS1')ORDER BY NAME;
ALTER TABLESPACE "PM_BDATA" NOLOGGING;
ALTER TABLESPACE "PM_BINDEX" NOLOGGING;
ALTER TABLESPACE "PM_BDATA_TOPO" NOLOGGING;
3:当用户只需完毕之后,在进行表空间日志启用和归档日志的启用即可。
select 'ALTER TABLESPACE ' || CHR(34) || NAME || CHR(34) || 'LOGGING;'
WHERE NAME NOT IN ('SYSTEM','SYSAUX','USERS','TEMP','UNDOTBS1')ORDER BY NAME;
ALTER TABLESPACE "PM_BDATA" LOGGING;
ALTER TABLESPACE "PM_BINDEX" LOGGING;
ALTER TABLESPACE "PM_BDATA_TOPO" LOGGING;
alter system set db_recovery_file_dest_size=10G scope=spfile;
alter system setdb_recovery_file_dest='C:\oradata\flash_recovery_area' scope=spfile;
alter system setlog_archive_dest_1='LOCATION=USE_DB_RECOVERY_FILE_DEST' scope=spfile;
select flashback_on from v$database;
这个问题我就不再赘述了,该博客无数次提及这个问题,感兴趣的用户可以在本博客搜索"版本压缩"或者"Compress"。
比如足够大的SGA和PGA,典型Oracle参数的设置如Open_Cusors、db_writer_processes等
当然,如果用户的硬件环境足够好,比如数据与索引分开存储、分区表、RAC架构等也会为批量写入数据提高效率。
注意;严格禁止用户在OLTP环境下使用Oracle的并行计算,因为这样会严重造成资源占用问题。
----------------------------------------------------------------------------------
版权所有,文章允许转载,但必须以链接方式注明源地址,否则追究法律责任!
Blog: http://blog.csdn.net/linghe301
----------------------------------------------------------------------------------