我一直在研究这个,我开始觉得我不能再提高效率了,但我想问问是否有人有任何提示。
我正在对数百万条记录运行查询,以查找沿线性列从系统 a 到系统 b 具有给定半径的所有 x、y、z 坐标(这些是星星)。我正在运行 PHP,并在结果集上完成了许多其他工作。我在大约 16 秒内从脚本中得到结果。查询延迟大约是这 16 秒中的 7 秒。
基本的查询逻辑是:
SELECT name, coordinates, and distance from end point
FROM stars
WHERE all stars are in a column of given radius between start and end points
ORDER BY distance from end point DESC
where 子句需要两个单独的计算,它们是这样的:
其中计算 1:
Calculate if the stars are within the space of the column using constants and x,y,z
其中计算 2:
Limit the column radius to a given figure.
(This where clause also performs similar calculations with the same constants and x,y,z.)
where子句中的数学公式不能真正改变,它们是3D空间中柱状计算所需的公式。
查询末尾的 order by 是绝对必要的,因为结果集太大,我的脚本无法保存在内存中。我必须在脚本中以正确的顺序使用它。
在变量替换之前定义的查询最容易阅读:
SELECT
name,
x,
y,
z,
SQRT(
pow(`x`-" . $bx . ",2)+
pow(`y`-" . $by . ",2)+
pow(`z`-" . $bz . ",2)
) d
FROM
stars
WHERE
(((`x`*$cx+`y`*$cy+`z`*$cz)-($constant_1))/($constant_2)) between 0 and 1
AND
SQRT(((($ax + ((((`x`*$cx+`y`*$cy+`z`*$cz)-($constant_1))/($constant_2)) * $cx))-`x`)*(($ax + ((((`x`*$cx+`y`*$cy+`z`*$cz)-($constant_1))/($constant_2)) * $cx))-`x`))+((($ay + ((((`x`*$cx+`y`*$cy+`z`*$cz)-($constant_1))/($constant_2)) * $cy))-`y`)*(($ay + ((((`x`*$cx+`y`*$cy+`z`*$cz)-($constant_1))/($constant_2)) * $cy))-`y`))+((($az + ((((`x`*$cx+`y`*$cy+`z`*$cz)-($constant_1))/($constant_2)) * $cz))-`z`)*(($az + ((((`x`*$cx+`y`*$cy+`z`*$cz)-($constant_1))/($constant_2)) * $cz))-`z`)))
<=$radius
ORDER BY
SQRT(
pow(`x`-" . $bx . ",2)+
pow(`y`-" . $by . ",2)+
pow(`z`-" . $bz . ",2)
) DESC
在数据库上运行的最终查询如下所示:(为简单起见,我使用的示例数据中有很多常量为 0。)
SELECT
name,
x,
y,
z,
SQRT( pow(`x`-25.21875,2)+ pow(`y`--20.90625,2)+ pow(`z`-25899.96875,2) ) d
FROM
stars
WHERE
(((`x`*25.21875+`y`*-20.90625+`z`*25899.96875)-(0))/(670809454.308))
between 0 and 1
AND
SQRT((((0 + ((((`x`*25.21875+`y`*-20.90625+`z`*25899.96875)-(0))/(670809454.308)) * 25.21875))-`x`)*((0 + ((((`x`*25.21875+`y`*-20.90625+`z`*25899.96875)-(0))/(670809454.308)) * 25.21875))-`x`))+(((0 + ((((`x`*25.21875+`y`*-20.90625+`z`*25899.96875)-(0))/(670809454.308)) * -20.90625))-`y`)*((0 + ((((`x`*25.21875+`y`*-20.90625+`z`*25899.96875)-(0))/(670809454.308)) * -20.90625))-`y`))+(((0 + ((((`x`*25.21875+`y`*-20.90625+`z`*25899.96875)-(0))/(670809454.308)) * 25899.96875))-`z`)*((0 + ((((`x`*25.21875+`y`*-20.90625+`z`*25899.96875)-(0))/(670809454.308)) * 25899.96875))-`z`)))
<=600
ORDER BY
SQRT( pow(`x`-25.21875,2)+ pow(`y`--20.90625,2)+ pow(`z`-25899.96875,2) ) DESC
我的表定义如下所示:
CREATE TABLE IF NOT EXISTS `stars` (
`localkey` bigint(20) NOT NULL AUTO_INCREMENT,
`id` bigint(20) NOT NULL,
`x` double NOT NULL,
`y` double NOT NULL,
`z` double NOT NULL,
`name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`localkey`),
UNIQUE KEY `id` (`id`),
KEY `x` (`x`),
KEY `y` (`y`),
KEY `z` (`z`),
KEY `xyz` (`x`,`y`,`z`),
KEY `name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
查询结果的说明表明没有索引使用和额外的:
extra: Using where; Using filesort;
到目前为止我尝试了什么:
- 调整各种数据类型以优化内存使用和索引(尽管我的数学表明不太可能使用索引)
- 使用 PHP 循环和多个较小的查询而不是这个巨大的查询(多个查询花费的时间更长。)
- 在运行查询之前复制到内存表(表太大,内存放不下)
- 仅将表的一部分(localkey、x、y、z)复制到内存中。 (它适合,但为其他进程留下的 max_heap_size 太少了,不值得。)
我还缺少其他选项吗?
谢谢!
最佳答案
假设只有较小的记录子集匹配,您可以通过首先进行基本的“矩形”过滤来减少数学负担。例如对表中的每条记录执行完整的笛卡尔距离是没有意义的,只会丢弃其中的大部分。
一个简单的“盒子”边界检查只是简单的减法和比较:
SELECT ...
FROM (
SELECT ...
WHERE (
(abs($x_coord - x_coordinate) <= $max_distance)
OR (abs($y_coord - y_coordinate) <= $max_distance)
)
) AS square_filter
WHERE ... full calculation here
当然,你正在做 3d 位置,所以它有点复杂,但这应该给你基本的想法。
关于php - 有什么方法可以优化 MySQL 中的复杂数学查询吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38796697/