距经度 x 距离的最大纬度 - 距纬度 x 距离的最大经度 - SQL
Max latitude for x distance from longitude - Max longitude for x distance from latitude - SQL
现在我有一个 table 一亿个插入:
CREATE TABLE o (
id int UNIQUE,
latitude FLOAT(10, 8),
longitude FLOAT(11, 8)
);
在我的后端,我正在接收一个用户 lat/long 并尝试 return 在其 x 距离内的所有内容。
我想我可以计算 X 距离的最大值 lat/long,而不是对每个结果都计算距离公式。
所以我们通过找到最大 lat/min 纬度、最大 long/min 长来创建一个正方形。
一旦我们有了这些最大值,我们就会对这个值范围进行查询,从而使我们的子集明显变小,然后进行实际距离公式(即,找到 X 距离内的值)。
所以我的问题是:
是什么让我 运行 更快?
选项 1)
- 1亿词条上的距离公式得到集合
选项 2)
- 我们计算 min/max lat/long.
而不是对 1 亿个条目的集合执行距离公式
- Select 来自 table 的 1 亿条目
范围内的值
- 在我们新的较小的集合上计算距离公式。
选项 3)
- SQL
中已经存在相关内容
如果选项 2 更快,下一期实际上是解决该数学问题。
如果你想看那个继续阅读:
Lat/Long距离公式
dlon = lon2 - lon1
dlat = lat2 - lat1
a = (sin(dlat/2))^2 + cos(lat1) * cos(lat2) * (sin(dlon/2))^2
c = 2 * atan2(sqrt(a), sqrt(1-a))
d = R * c
显然我们可以重新排列这个,因为 D(假设 1 英里)和 R(是地球的半径)是一个设定值,所以我们得到 D/R = C.
那么问题就来了,我们如何计算C/2 = atan2(sqrt(a), sqrt(1-a))?
1 -- 100M 行需要大量扫描和测试。偶尔做一次可以,但做多了太慢了。
2 -- 使用伪正方形边界框并做
WHERE latitude BETWEEN ...
AND longitude BETWEEN ...
是很好的第一步。纬度范围是一个简单的常量乘以 X;经度范围也除以 cos(latitude)
.
但是当您试图在正方形中找到那些行时,问题就来了。 latitude
and/or longitude
上的任何索引组合,无论是单独还是一起,都只会部分过滤。也就是说,它会忽略经度并为您提供纬度范围内的所有内容,反之亦然。这可能会让您减少到 100,000 行来检查距离。这比 100,000,000 好很多,但不如您希望的那么好。
3 -- http://mysql.rjweb.org/doc.php/latlng 确实下到广场了,还是很近的。它旨在扩展。我只测试了 3M 行,而不是 100M,但它应该可以正常工作。
主要技巧是根据纬度进行分区,然后将经度作为 PRIMARY KEY
中的第一列,以便 InnoDB 将分区附近的附近行聚集在一起。如果您查找 X 英里(或公里)内的所有行,它可能会查看(并计算大圆距离)所需行数的两倍左右,而不是 100K。如果你想找到最近的 100 个项目,它可能会触及大约 400 (4x)。
至于 SPATIAL
索引,您可能想升级到 5.7.6,这是在添加 ST_Distance_Sphere()
和 ST_MakeEnvelope()
时。 (MakeEnvelope 只比自己构建多边形方便一点点——它有扁平地球综合症。)
现在我有一个 table 一亿个插入:
CREATE TABLE o (
id int UNIQUE,
latitude FLOAT(10, 8),
longitude FLOAT(11, 8)
);
在我的后端,我正在接收一个用户 lat/long 并尝试 return 在其 x 距离内的所有内容。
我想我可以计算 X 距离的最大值 lat/long,而不是对每个结果都计算距离公式。
所以我们通过找到最大 lat/min 纬度、最大 long/min 长来创建一个正方形。
一旦我们有了这些最大值,我们就会对这个值范围进行查询,从而使我们的子集明显变小,然后进行实际距离公式(即,找到 X 距离内的值)。
所以我的问题是: 是什么让我 运行 更快?
选项 1)
- 1亿词条上的距离公式得到集合
选项 2)
- 我们计算 min/max lat/long. 而不是对 1 亿个条目的集合执行距离公式
- Select 来自 table 的 1 亿条目 范围内的值
- 在我们新的较小的集合上计算距离公式。
选项 3)
- SQL 中已经存在相关内容
如果选项 2 更快,下一期实际上是解决该数学问题。
如果你想看那个继续阅读:
Lat/Long距离公式
dlon = lon2 - lon1
dlat = lat2 - lat1
a = (sin(dlat/2))^2 + cos(lat1) * cos(lat2) * (sin(dlon/2))^2
c = 2 * atan2(sqrt(a), sqrt(1-a))
d = R * c
显然我们可以重新排列这个,因为 D(假设 1 英里)和 R(是地球的半径)是一个设定值,所以我们得到 D/R = C.
那么问题就来了,我们如何计算C/2 = atan2(sqrt(a), sqrt(1-a))?
1 -- 100M 行需要大量扫描和测试。偶尔做一次可以,但做多了太慢了。
2 -- 使用伪正方形边界框并做
WHERE latitude BETWEEN ...
AND longitude BETWEEN ...
是很好的第一步。纬度范围是一个简单的常量乘以 X;经度范围也除以 cos(latitude)
.
但是当您试图在正方形中找到那些行时,问题就来了。 latitude
and/or longitude
上的任何索引组合,无论是单独还是一起,都只会部分过滤。也就是说,它会忽略经度并为您提供纬度范围内的所有内容,反之亦然。这可能会让您减少到 100,000 行来检查距离。这比 100,000,000 好很多,但不如您希望的那么好。
3 -- http://mysql.rjweb.org/doc.php/latlng 确实下到广场了,还是很近的。它旨在扩展。我只测试了 3M 行,而不是 100M,但它应该可以正常工作。
主要技巧是根据纬度进行分区,然后将经度作为 PRIMARY KEY
中的第一列,以便 InnoDB 将分区附近的附近行聚集在一起。如果您查找 X 英里(或公里)内的所有行,它可能会查看(并计算大圆距离)所需行数的两倍左右,而不是 100K。如果你想找到最近的 100 个项目,它可能会触及大约 400 (4x)。
至于 SPATIAL
索引,您可能想升级到 5.7.6,这是在添加 ST_Distance_Sphere()
和 ST_MakeEnvelope()
时。 (MakeEnvelope 只比自己构建多边形方便一点点——它有扁平地球综合症。)