叶水全
摘要:结合测绘成果管理中需要处理大量EDB文件,针对文件存储空间的大小问题以及分发的效率进行了思考与总结。通过对原有大比例尺地形图的存储进行了优化,针对EDB文件的格式特点,提出一种新的压缩和管理方法,即用Oracle 11g数据库和7z压缩技术,提高测绘档案管理中大比例尺地形图的分发速度,降低了数据管理的存储需求。为今后相关工作的开展提供了较大的便利与可靠的支持。
关键词:EDB文件;7Z压缩;Oracle数据库存储;C++开发
一、前言
从2005年开始,重庆市勘测院全面使用北京山维科技股份有限公司的EPS地理信息工作站进行大比例尺1∶500地形图采集与存储。作为其数据保存的主要形式,档案管理工作中积累了数量庞大的EDB文件(*.edb),包括工程项目文件和国家标准基本比例尺文件,其年度归档数已超过10万,每年的存储空间增长量接近1TB。这种小文件形式的文件存储,不仅会造成存储空间的迅速扩大,更会影响后期数据的分发效率。在档案搜索和分发时,相同的大比例尺文件名存在多个时期的多个版本,数据众多,且每年度存储位置分散,加重了数据追溯困难。因此,为了方便存储EDB文件,笔者做了以下优化:增加上载入口时的自动冗余清理,按EPS平台的规则清理EDB文件中的冗余内容,缩小数据大小;使用7z压缩技术,在传输和存储过程中进一步缩小数据大小;使用Oracle数据库存储EDB文件。
二、功能实现
(一)EDB冗余清理
EDB格式作为EPS地理信息工作站数据承载格式,其底层是一个Access数据库,使用GeoPointTB、GeoLineTB、GeoAreaTB表存储图形数据,使用MarkNoteTB表存储注记数据,使用UserLayerTB、AttrLinkInfoTB表管理图形关联属性表,使用FeatureCode、SymbolScriptTB、NoteTemplateTB表实现图形符号化[1]。EPS地理信息工作站为防止采集和编绘过程中数据因故障丢失,每条数据记录都真实地保存至上述表格。同时,为保证整个加工过程的可追述,整个数据记录过程都是增量的,即数据存入表格后将永远存在而不会删除。在用户视角的软件操作窗口中,需要表达一个对象删除时,“删除”动作在数据存储的底层映射为对象的Mark属性值做累加运算;如果用户有回退操作则可以通过减去累加值完成还原。
于是EDB文件就会越来越大,例如,一个普通的大比例尺文件一般为6Mb,在清理前可能有20Mb,甚至极个别达到30Mb,数据存储量最大增至5~6倍于原文件。归档后,采集和编绘的过程操作已经完成。为了节约存储空间,应该将Mark字段标记为“删除”的那些数据条目从EDB文件中删除。此处以DAO(DATA ACESS OBJECTS)方式为例,连接EDB数据文件,将表中冗余过程记录直接删除,C++核心函数如下:
Int EDBCompress(CString edbPath){
CDaoDatabase* m_pDB = new CDaoDatabase;
CString strDeleteTab = _T( "DELETE * From [tab] where Mark Mod 2 = 0");
……
strSQL = strDeleteTab;
strSQL.Replace( "[tab]", strTabNames[i]);
m_pDB->Execute( strSQL, dbConsistent );
……
}
(二)7z数据压缩
7-Zip是基于GNU LGPL授权协议的开源压缩软件[2],可以自由地整合到现有的软件系统中。它的压缩格式.7z具有较高的压缩比,较Zip压缩格式有30%-70%能力提升,较商业软件WinRAR在压缩比方面也略有优势(图1),在办公自动化数据集成中一般优先使用。
笔者将数据解压缩功能集成到系统中,在7z的SDK开发文档中,推荐使用其开源接口SzAr_Extract,其接口如下:
SZ_RESULT SzAr_Extract(
CArchiveDatabaseEx *db,
ILookInStream *inStream,
UInt32 fileIndex, /* index of file */
UInt32 *blockIndex, /* index of solid block */
Byte **outBuffer, /* pointer to pointer to output buffer */
size_t *outBufferSize, /* buffer size for output buffer */
size_t *offset, /* offset of stream for required file in *outBuffer */
size_t *outSizeProcessed, /* size of file in *outBuffer */
ISzAlloc *allocMain,
ISzAlloc *allocTemp
);
为了将内存中的7z数据生成EDB文件,需要一些定制。在7z的源代码项目Client7z“7z1900-src\CPP\7zip\UI\Client7z”(7-Zip 19.00为例)里,在Client7Z.cpp补充一个由内存至文件的解压函数,并将此Client7Z项目以lib的形式编译进入现有系统中即可。补充的内存解压函数如下:
int decode_memory2f(const Byte * memory_data, size_t memory_size, const char * save_folder) {
……
CBufInStream *bufInStreamSpec = new CBufInStream;
CMyComPtr
bufInStreamSpec->Init(memory_data, memory_size);
CArchiveOpenCallback *openCallbackSpec = new CArchiveOpenCallback;
CMyComPtr
……
CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback;
CMyComPtr
extractCallbackSpec->Init(archive, folderName);
extractCallbackSpec->PasswordIsDefined = false;
HRESULT result = archive->Extract(NULL, (UInt32)(Int32)(-1), false, extractCallback);
if (result != S_OK) return 2;
return 0;
}
(三)Oracle数据库存储
原有的数据成果以文件的形式保存在服务器,虽然管理成本低,但再利用的效率不高。笔者依托Oracle数据的大数据文件的管理能力,在Oracle11g数据库中,按照海量数据库的架构创建存储表[3],按实际的工作节奏每一年度建一个内容存储表。例如,2020年版的创建表SQL如下:
-- 创建存储表
CREATE TABLE eps_repos_2020(
id NUMBER(12,0) PRIMARY KEY,
name VARCHAR2(100 char),
data_format VARCHAR2(60 char),
save_format VARCHAR2(60 char),
save_code VARCHAR2(60 char),
blob_chip BLOB,
update_date DATE DEFAULT sysdate NOT NULL
)
并且,在下载时方便复原EDB文件,需要在Oracle的下载接口中一并使用7z解压。笔者在Oracle11g数据库接口中使用了Oracle Instant Client连接远程数据库[4],Oracle OCI(Oracle Call interface)模块和其轻封装ocilib(Vincent Roginer)作为开发模块[5]。使用Oracle连接池技术,其Oracle连接池子函数如下:
void fileDw_byPool_subWorker(OCI_Thread *thread, void *data) {
fileDw_byPool_argument * pf = (fileDw_byPool_argument*)data;
OCI_Connection *cn = OCI_PoolGetConnection(pf->pool, NULL);
……
wchar_t name_out = { 0 };
if (false == _download_7zblob(cn, pf->db_object_target, id, pf->savedir, name_out)) {
……
}
OCI_ConnectionFree(cn);
}
其中,数据库BLOB下载函数如下:
bool _download_7zblob(OCI_Connection *cn, const otext * db_object_target, big_uint id, const otext * savedir, wchar_t * name_out) {
bool rs = false;
const otext sql_format1[] = OTEXT("SELECT blob_chip FROM %s WHERE id = %%lu");
……
if (FALSE != OCI_ImmediateFmt(cn, sql_format2, id, OCI_ARG_LOB, blob)) {
rs = fwrite2_blob_7z_onlyone(blob, savedir, name_out);
//wprintf( L"%s OK\n", name);
}
OCI_LobFree(blob);
return rs;
}
其中,数据库7z解压为EDB文件的函数如下:
bool fwrite2_blob_7z_onlyone(OCI_Lob * blob, const otext * savedir, wchar_t * name_out) { // Y.S.Q 2018-05-17
……
unsigned char * buff = new unsigned char[len];
if (FALSE != OCI_LobRead2(blob, buff, &rd, &rd)) {
if (len == rd) {
err = decode_memory2f(buff, len, savedir, name_out);
}
}
……
}
三、测试与性型分析
笔者在Visual Studio 2017创建从数据库下载EDB文件的C++项目fileDw_byPool.vcxproj,并包含Client7z项目的lib和ocilib项目的lib,编译为exe执行程序。其中fdwp32.exe是主程序,7z.dll提供解压缩功能,fdwp32.xml是数据库的信息及参数,oracle11203目录是数据库Oracle Instant Client功能目录。
以下是笔者优化后的分发机制与旧模式的比较:
1.下载速度。随机选择了100个EDB文件作为下载清单,在两台同配置的普通电脑上,一台使用旧模式下载数据,一台使用优化后的下载机制。前者的平均下载时间为14秒(图2中的单点)。后者基于ORACLE连接池开启了数据库多线程模式。从图2(竖轴时间秒,横轴线程池数)的函数曲线可以发现,4线程模式时间为6秒,提高了2.5倍的下载速度。
2.存储空间。以某年度全部基本比例尺EDB文件为例,旧模式下为服务器文件共享,文件个数为10.5万,存储大小584GB;优化后的Oracle数据库表空间大小52GB,约为11倍的存储空间优化。
四、结语
基本比例尺数据的管理一直是重庆市勘测院测绘档案保管的重要工作。面对数十年长期积累的大比尺EDB格式数据成果,本文分别从存储空间和分发效率两方面入手,按内容特性对文件精简其存储大小;使用7z压缩方法进一步降低存储空间的大小;利用Oracle数据库的线程池技术优化分发速度。实际访问效率相比于传统的文件共享方式快2~3倍,服务器存储空间节约85%以上;与此同时,借用了Oracle数据库的海量存储能力以及安全访问机制,提升了测绘成果管理中针对EDB格式的大比例尺数据的存储与分发能力。
参考文献
[1]Igor Pavlov.7-Zip SDK[EB/OL].[2021-12-22].https://www.7-zip.org/.
[2]杨亮亮.Oracle数据库处理海量数据的技术分析[J].信息技术与信息化.2020(11):25-27.
[3]吴政,李成名,等.Oracle数据库矢栅数据一体化存储与管理[J].测绘学报.2017,46(5):639-648.
[4]ORACLE. Oracle Call Interface Programmer's Guide[EB/OL].[2021-12-22].https://docs.oracle.com/en/database/oracle/oracle-database/21/lnoci/index.html.
[5]Vincent Rogier.C and C++ Driver for Oracle[EB/OL].[2021-12-22].http://vrogier.github.io/ocilib/.