Es与MongoDB地理数据搜索性能比较

基础环境信息

主机信息:

  • 处理器:i5-9400 2.90GHz
  • 内存:16G
  • 硬盘:224G

软件信息:
Es:

  • 版本:7.6.2
  • JDK 1.8
  • 单机模式运行
  • 默认配置文件

Es客户端:

  • RestHighLevelClient 7.6.2

Mongo:

  • 版本:4.2.11
  • 单机模式运行
  • 默认配置文件

Mongo客户端:

  • spring-data-mongodb 3.1.2
  • mongodb-driver-sync 4.1.1

准备和录入测试数据

分别在Es和MongoDB中和存储10、100w等数量的坐标点数据,并随机在其中插入若干数量的测试坐标点,作为范围查询的记录。以下分别是es和mongo对应的数据说明。数据格式采用标准地理数据格式,但在两个数据库中格式略有不同,数据格式标准为RFC7946

Es存储数据

{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [125.6, 10.1]
  },
  "properties": {
    "name": "Dinagat Islands"
  }
}

es中geo格式的数据:

  • geo_point
    地理坐标点,在Elasticsearch中用来存经纬度数据的一种数据格式。在es中指定数据类型:
{
  "mappings": {
     "store":{
      "properties": {
          "location":{
            "type": "geo_point"
          }
      }
     }
  }
}

存储示例:

{
"_index": "my-index-geo-01",
"_type": "_doc",
"_id": "100",
"_version": 1,
"_score": 1,
"_source": {
"location": "-9.85,-28.76"
}
}

Mongo存储数据

坐标数据格式,mongo支持两个格式:
格式一,为mongo支持的较标准的地理格式,但是与geoJson略有不同;支持2d和2dsphere索引:

{
        "_id" : 0,
        "loc" : {
                "coordinates" : [
                        21.38,
                        -40.7
                ],
                "type" : "Point"
        }
}

格式二,较旧的格式模式,支持2d和2dsphere索引:

{ "_id" : 0, "loc" : [ 3.78, 5.94 ] }

创建集合和坐标索引:

db.demo.createIndex({loc: "2dsphere" })

查询耗时比较

查询数据方式

ES:按照geo_distance方式查询,找出指定位置在给定距离内的数据,相当于指定圆心和半径找到圆中点。
Mongo:支持较多的查询方式,分别为圆形查询(球体)、圆形查询和球面直线查询。

不同客户端端查询耗时比较

客户端查询耗时/ms

10w

20w

30w

40w

50w

60w

100w

ES(RestHighLevelClient)

153

150

139

139

142

144

141

MongoDB(MongoTemplate,圆形查询(球体))

91

94

94

96

91

95

91

MongoDB(Mongo cilent,圆形查询(球体))

60

63

66

65

65

63

67

说明:
查询的数据集均创建过对应索引。

不同查询方式比较

MongoDB中提供了对于圆形区域提供了不同方式的查询,以下是查询相同数据集的结果比较:

MongoDB不同查询方式耗时/ms

10w

100w

MongoDB(MongoTemplate,圆形查询(球体))

89

91

MongoDB(MongoTemplate,圆形查询)

91

90

MongoDB(Mongo cilent,圆形查询(球体))

60

67

MongoDB(Mongo cilent,圆形查询)

62

64

说明:
不同的查询方式需创建不同的索引,不然查询时间较长。
球面直线查询误差较大,查询结果中出现了许多不准确的坐标点,故不在对比结果中。
上面查询的耗时的前提是创建了对应格式的索引。

有无索引查询比较

下面给出有无创建索引的查询耗时比较结果:

MongoDB查询耗时/ms

50w

100w

MongoDB(MongoTemplate,圆形查询(球体)),创建索引

91

87

MongoDB(MongoTemplate,圆形查询(球体)),未创建索引

709

1358

MongoDB(Mongo cilent,圆形查询(球体)),创建索引

63

67

MongoDB(Mongo cilent,圆形查询(球体)),未创建索引

668

1310

结论:
在相同地理数据集的情况下,mongo数据库的查询耗时比es数据库短大约30~40%。
spring data mongo提供了基于mongo driver的一层封装,使用起来更加简单便捷,如提供了序列化和反序列化的处理。原生的mongo client也提供了全面的查询方法,查询效率略高一些,不过在程序中需要增加一些处理代码,如序列化方法等,仅对结果简单处理时可以考虑使用。
有无创建索引,对于地理数据的查询耗时影响较大。

客户端查询耗时分析

通过查看MongoTemplate源码,template是在client的基础上做了封装,为了保证了读写数据的安全和于查询结果的处理更加简单,添加了包括对查询结果的映射,包括嵌套数据;上锁保证线程安全等处理代码。添加的处理逻辑增加了一部分时间开销。如下是template查询时的核心代码块:

private <T> List<T> executeFindMultiInternal(CollectionCallback<FindIterable<Document>> collectionCallback,
			CursorPreparer preparer, DocumentCallback<T> objectCallback, String collectionName) {

		try {

			MongoCursor<Document> cursor = null;

			try {

				cursor = preparer
						.initiateFind(getAndPrepareCollection(doGetDatabase(), collectionName), collectionCallback::doInCollection)
						.iterator();//获取查询的原始结果

				List<T> result = new ArrayList<>();

				while (cursor.hasNext()) {
					Document object = cursor.next();
					result.add(objectCallback.doWith(object));//处理原始数据,映射成对应的数据类型
				}

				return result;
			} finally {

				if (cursor != null) {
					cursor.close();
				}
			}
		} catch (RuntimeException e) {
			throw potentiallyConvertRuntimeException(e, exceptionTranslator);
		}
	}

地理空间数据模型说明

数据类型

mongo中支持的地理空间数据类型有:Point、LineString、Polygon、MultiPoint、MultiLineString、MultiPolygon、GeometryCollection。
坐标点的数据结构是:

{ type: "Point", coordinates: [ 40, 5 ] }

空间数据索引

2dsphere索引:支持球面空间查询。
2d索引:支持平面空间查询,支持部分球面查询,但是会出现误差。推荐尽量使用2dsphere索引。

查询方式

主要有四种查询类型、计算方法说明及索引支持:

名称

说明

索引支持

$near

查询指定一个点在平面中从最近到最远的坐标点。

2dsphere and 2d

$nearSphere

查询指定一个点在球面中从最近到最远的坐标点。

2dsphere and 2d

$geoWithin

查询指定多边形形状中全部的坐标点。包括的形状有:矩形、多边形、圆形和球面圆形。

2dsphere and 2d

$geoIntersects

查询与指定GeoJSON对象相交的坐标点。

2dsphere

从10w个数据点中查询某个坐标点相近的坐标点的结果,实际数据中有五个参考点:

名称

查询结果数量

$near(直线距离最远1m内)

14

$nearSphere(直线距离最远1m内)

31356

$geoWithin(半径在1m的平面圆形内)

5

$geoWithin(半径在1m的球面圆形内)

5

$geoIntersects(与参考点相交的点)

5