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 |