MS RFC 52:一次通过查询处理

日期

2009年3月8日

作者

史蒂夫·莱姆

联系

com.net上的石灰

最后编辑

2009/06/03

状态

采用

版本

MAPServer 6

概述

此RFC建议在MapServer中更改当前的查询处理(按点、按框、按形状等)。

目前,MapServer支持一种非常灵活的查询机制,它使用两种数据传递。这可以通过缓存一个特征ID列表(传递一个)来工作,然后第二个传递用于表示的特征:模板化输出、绘图或通过mapscript检索。最明显的问题是第二次传球造成的性能冲击。真正的问题是,MSlayerGetShape()函数在实现时提供了对数据的随机访问,这对于某些驱动程序来说可能非常昂贵。

技术解决方案

有许多潜在的解决方案:

1。可以将返回的形状缓存在内存中。虽然这不会导致真正的单次传球,但你不必两次回到原来的车手。但是,它可能会导致即使是中等大小的数据集也会消耗大量内存。同时访问服务的多个客户机只会使问题复杂化。一些测试已经证实,这种方法并不比下面的选项3快,甚至可能慢一点。

2。另一种解决方案是将大部分查询前和后处理代码折叠到mslayerwhichshapes()和mslayernextshapes()函数中,以便可以使用绘图层中使用的访问范式。随后的研究让我们得出这样的结论:在某些情况下,真正的一次通过是不可能的。例如,GML要求在编写单个特性之前先写出一个结果集信封。如果不通过这些特性,就无法得到最初的信封。

三。最后一个解决方案是更改mslayerGetShape()函数的行为方式。我们预先设置更改该函数的行为,以提供对结果集(由mslayerwhichshapes()定义)的随机访问,而不是对整个数据集的访问。这将消除当前引用数据驱动程序在初始查询中返回的结果所产生的大部分开销。为了实现目的,我们将保留当前的mslayergetshape()实现以支持简单的随机访问,并创建一个名为mslayerResultsGetshape()的新函数。

mslayerGetShape()将 only 通过mapscript使用以保留真正的随机访问功能。请注意,绘图使用mslayerNextShape(),不依赖索引值。

根据上一个解决方案,数据驱动程序需要做两件事:

  • 使用允许mslayerResultsGetShape()随机访问结果的值更新shapeobj索引属性(long int)的填充

  • 创建mslayerResultsGetShape()的驱动程序特定版本,从mslayerWhichShapes()中创建的结果中检索形状

  • mslayerResultsGetShape()的默认实现将简单地包装mslayerGetShape(),因为大多数驱动程序不必实现较新的函数(例如,ogr或shapefiles)。

查询函数需要:

  • 完成查询后不关闭层(我们假设用户希望对结果执行某些操作)

  • 允许mslayerWhichitems()检索所有项,以便检索到的形状可以呈现(绘图、模板或…)

演示功能:

  • 不要调用mslayeropen()、mslayerwhichitems()、mslayerwhichshapes(),因为已经在查询函数中完成了这项操作。

MapScript层obj包装:

  • 添加resultsgetshape()方法

该方案已在单通道沙箱中进行了试验,取得了很好的效果。在某些情况下,查询运行的数量级更快。一个积极的副作用是不需要使用主键从结果集中检索特性。驱动程序负责提供数据,以唯一标识结果集中的行。

向后兼容性问题

此解决方案很可能需要更改处理查询结果的mapscript应用程序,这取决于正在处理的数据类型。例如,那些命中shapefiles的函数仍然“原样”,因为该驱动程序仍将使用mslayerGetShape()。任何命中RDBMS数据源的脚本都必须使用resultsgetshape()而不是getfeature()/getshape()。

其中一个牺牲品是查询保存/读取功能。由于一组结果的处理将特定于数据集结果句柄,因此一旦一个层最终关闭,就不可能返回到结果。下一步将提出解决这个问题的建议。

查询文件支持

查询文件提供了一种保存查询操作结果的方法,以便在后续的地图制作中使用。在查询过程中收集的一系列索引将被写入磁盘并在以后读取,以用于一次访问一个特性的数据。对于提议的更改,这根本无法与RDBMS数据源一起工作。有必要重新创建结果集,但重新执行查询。问题是,没有简单的方法来序列化查询参数。

我建议创建一个新的对象queryobj来存储与mapserver查询相关的各种参数。它可能看起来像:

typedef struct {
   int type; /\* By rect, point, shape, attribute, etc... Types match the query functions. \*/
   int qlayer; /\* used by all functions \*/

   rectObj *rect; /\* used by msQueryByRect() \*/

   char *qitem; /* used by msQueryByAttribute() \*/
   char *qstring; /\* used by msQueryByAttribute() \*/

   ...and so on...
} queryObj;

单个queryobj将挂起一个mapobj,而mapobj将是传递给各种查询方法的唯一参数。mapserver c代码,主要是CGI和OGC接口,只需填充适当的queryobj成员并调用正确的查询函数。

mapscript将保持不变。各种查询函数的包装器只需要使用用户提供的参数来填充queryobj,然后调用查询函数。queryobj将是不可变的。

通过将所有信息存储在一个单独的存储中,应该可以很容易地将其序列化到磁盘上。读取时,可使用重新定位的queryobj重新执行适当的查询。mssavequery()和msloadquery()函数签名将保持“原样”,尽管内部结构会发生变化。

文件冲击

  • 驱动程序文件:如有必要,实现msxxxlayerresultsgetshape()函数以获取形状代码

  • maptemplate.c:显示结果时不打开/关闭层

  • mapgml.c:显示结果时不打开/关闭层

  • C:不要打开/关闭图层等…如果绘制查询映射

  • maplayer.c:重构mslayerwhichitems(),将mslayerresultsgetshape()添加到层插件API

  • mapquery.c:重新运行mssavequery()和msloadquery(),更改查询函数以输入一个单独的mapobj,添加msinitquery()和msfreequery()函数

  • mapserv.c:在调用查询函数之前填充map->query

  • mapwxs.c(各种):在调用查询函数之前填充map->query

  • mapfile.c:利用msinitquery()和msfreequery()函数

  • mapserver.h:定义queryobj,添加到mapobj

  • mapscript(各种):更新映射/层查询方法以填充queryobj

  • 其他?(一个是mapcopy.c)

虽然许多文件受到影响,但一般来说,更改相对简单。

未知数

到目前为止,只有shapefiles、postgis和oracle驱动程序通过这个新方案进行了测试,都取得了积极的结果。即使shapefiles也显示出了性能的提高,这仅仅是因为只打开一次文件的开销。目前还不清楚OGR、SDE和栅格查询将受到怎样的影响。我希望那些司机的车主能多加评论。

MapServer支持一个相当模糊的名为queryByIndex()的查询方法,它基本上是MSlayerGetShape()的包装。此更改可能会使该方法过时,但需要进行更多的检查。

投票历史

+来自史蒂文,丹尼尔,霍华德,佩林,塔马斯