php - 有什么方法可以优化 MySQL 中的复杂数学查询吗?

标签 php mysql math optimization

我一直在研究这个,我开始觉得我不能再提高效率了,但我想问问是否有人有任何提示。

我正在对数百万条记录运行查询,以查找沿线性列从系统 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/

相关文章:

algorithm - 封装 3D 三角形的最小球体?

php - 如何在 Codeigniter 中使用 guest 用户愿望 list 到数据库?

php - 尝试使用 PHP 和 mySQL 创建可编辑的 HTML 表,但该表不会更新

php - 向远程sqlite插入数据

php - 带有 anchor 标记的 CodeIgniter 文件上传问题

php - 包括来自单独文件的数据库连接

javascript - 搜索时间戳大于X天的对象

math - 如何使用带有数学公式的对数似然相似度度量来计算相似度?

php - 使用 phpseclib 在 MySQL 中进行 SQL 查询

php - 无法重命名文件夹(PHP + Linux)