Mysql优化/性能,如何有效使用limit |基于位置的选择

Mysql优化/性能,如何有效使用limit |基于位置的选择,mysql,optimization,query-optimization,query-performance,sqlperformance,Mysql,Optimization,Query Optimization,Query Performance,Sqlperformance,以下缩短查询选择从用户e.altloc=0:location或e.altloc=1:altlocation计算的给定距离内的所有行入口。 我在e.uid、al.eid、e.country、e.tmstmp上有索引,id是主键。 问题在于解释所有行都需要处理查询,而不是我喜欢的限制为2的2行。我已经读过这个问题,但是我不能在使用联接之前进行限制,因为我需要在进行限制2之前联接位置表,否则返回将是错误的。 查询: 旁注:entrys的引擎可能应该是innodb,具有大约70%的读取率。位置表都使用

以下缩短查询选择从用户e.altloc=0:location或e.altloc=1:altlocation计算的给定距离内的所有行入口。 我在e.uid、al.eid、e.country、e.tmstmp上有索引,id是主键。 问题在于解释所有行都需要处理查询,而不是我喜欢的限制为2的2行。我已经读过这个问题,但是我不能在使用联接之前进行限制,因为我需要在进行限制2之前联接位置表,否则返回将是错误的。

查询:

旁注:entrys的引擎可能应该是innodb,具有大约70%的读取率。位置表都使用innodb运行

编辑Willem Renzema回答的问题: 这样会更有效率吗

SELECT 
        e.id, e.uid, e.title, e.description, l.place, l.placenonce, al.altplace, al.altplacenonce,
        IF(e.altloc=0,
            6371 * acos( cos( radians(:lat) ) * cos( radians( AES_DECRYPT(lat, UNHEX('###'), latnonce) ) ) * cos( radians( AES_DECRYPT(lng, UNHEX('###'), lngnonce) ) - radians(:lng) ) + sin( radians(:lat) ) * sin(radians(AES_DECRYPT(lat, UNHEX('###'), latnonce))) ) ,
            6371 * acos( cos( radians(:lat) ) * cos( radians( AES_DECRYPT(altlat, UNHEX('###'), altlatnonce) ) ) * cos( radians( AES_DECRYPT(altlng, UNHEX('###'), altlngnonce) ) - radians(:lng) ) + sin( radians(:lat) ) * sin(radians(AES_DECRYPT(altlat, UNHEX('###'), altlatnonce))) )
        ) AS distance
    FROM 
        (
            SELECT id, uid, title, description
            FROM 
                entrys 
            WHERE 
                    approx_lat > :min_lat
                AND approx_lat < :max_lat
                AND approx_lng > :min_lng
                AND approx_lng < :min_lng   
            ORDER BY 
                e.tmstmp 
            DESC
            LIMIT 2

        ) AS e
    INNER JOIN 
        location l 
    ON l.id = uid 
    LEFT JOIN
        altlocation al
    ON al.eid = e.id
    HAVING 
        distance <= 50
如果我想在条目表中添加大约lat和大约lng 线索是将approw_lat和approw_lng移动到entry表中,我可以插入altlocation或location only,这样我就可以在查询中摆脱它。
正在使用距离在查询中使用边界框

WHERE子句中的更改仅为示例:

SELECT 
    e.id, e.uid, e.title, e.description, l.place, l.placenonce, al.altplace, al.altplacenonce,
    IF(e.altloc=0,
        6371 * acos( cos( radians(:lat) ) * cos( radians( AES_DECRYPT(lat, UNHEX('###'), latnonce) ) ) * cos( radians( AES_DECRYPT(lng, UNHEX('###'), lngnonce) ) - radians(:lng) ) + sin( radians(:lat) ) * sin(radians(AES_DECRYPT(lat, UNHEX('###'), latnonce))) ) ,
        6371 * acos( cos( radians(:lat) ) * cos( radians( AES_DECRYPT(altlat, UNHEX('###'), altlatnonce) ) ) * cos( radians( AES_DECRYPT(altlng, UNHEX('###'), altlngnonce) ) - radians(:lng) ) + sin( radians(:lat) ) * sin(radians(AES_DECRYPT(altlat, UNHEX('###'), altlatnonce))) )
    ) AS distance
FROM 
    entrys e 
INNER JOIN 
    location l 
    ON l.id = e.uid 
LEFT JOIN
    altlocation al
    ON al.eid = e.id
WHERE 
    e.country = :countryid
    AND l.approx_lat > :min_lat
    AND l.approx_lat < :max_lat
    AND l.approx_lng > :min_lng
    AND l.approx_lng < :min_long    
HAVING 
    distance <= 50
ORDER BY 
    e.tmstmp 
DESC
LIMIT 2
在执行查询之前,您将计算:min_lat、:max_lat、:min_lng和:max_lng。这些值将根据您的:lat和:lng值(在本例中为50)的所需半径生成

具体怎么做我建议阅读其他答案中的一个,例如,遍布互联网的答案。只需搜索地理位置边界框即可开始

然后,您可以通过在近似lat和近似lng列上添加索引来进一步提高性能。您还可以尝试添加两个复合指数,即About_lat、About_lng和/或About_lng、About_lat,因为优化器可能会利用这些指数。然而,我强烈建议进行基准测试,看看它们是否能带来任何改进。制作这些覆盖索引的附加列可能也会有所帮助,但我现在主要关注最基本的问题

请注意,您试图优化的内容已经是一个困难的优化问题。事实上,你需要加密你的数据,这使它更加困难。然而,只要您能够存储这些近似值,我们就可以绕过大部分额外的困难

我还强烈建议您将IF逻辑排除在WHERE子句之外。通过包含这些,您可以强制优化器查找每个记录,以查看它是否匹配该条件

通常,要获得良好的性能,需要限制需要检查的记录数。IF语句无法优化,它不是。这也是为什么我的答案要求您存储近似值以便有效。如果必须首先解密数据,这意味着必须查找和检查每个记录。这将扼杀你的表现

还要注意,在我的示例查询中,我忽略了WHERE子句中的altlocation表。理想情况下,如果location和altlocation是相同的,则应该只有一个数据表,然后从记录位置id为主要或备用的记录连接到上的该表

我希望这至少能帮你找到正确的方向。

部分答案

有时是子查询的有用提示

请注意,在子查询中有几个uid、title、description庞大的列。 有一个命令和限制,所以拖他们周围需要一些努力。 所以

使用子查询中的最小列数,确保包含行id。 在子查询之后,通过id添加一个连接以获取这些额外的列。 此外,还有一个覆盖索引,包含子查询中保留的所有列:INDEXapprox_lat、approx_lng、tmstmp、id
在给定查询中,您的LIMIT子句在哪里?在这种情况下,您不能将LIMIT用作优化技术。它确实发生了。但是,如果您确实用示例数据和预期结果更好地解释了您的问题,那么我们可能会提供帮助。@MadhurBhaiya抱歉,我忘了添加此示例代码。@Shadow我应该提供哪种示例数据?一排里面是什么?例如,条目可以是由用户创建的带有标题、说明。。。具有固定或不固定的位置。其他用户可以在距其固定位置的给定距离内监视2个入口。@阴影固定位置将始终是主位置,非固定位置将是用户给定的途中位置。入口是按时间戳排序的。这确实是一个很好的详细答案。非常感谢你。我还有一个问题。据我所知,我需要让数据不加密,并且没有小数点?对于altlocation和location,这取决于entry.altloc是1还是0,->1从表altlocation获取值,0从表位置获取值,这是查询的一个重要部分。如果
在where子句中,限制对国家/地区的搜索,或仅在国家/地区内不获取行/或不获取行。保留逗号和care的方式可以绕过小数点问题,所有值的长度都相同。select查询中的IF不会影响性能?我还添加了代码的修改版本,我的示例会更有效吗?@delato468我相信您编辑的版本会有所改进。始终对所做的任何更改进行基准测试是很重要的。至于您是否仍然需要距离大于此值,边界框允许使用INDEXlat或INDEXlng。
  CREATE TABLE `entrys` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `uid` int(5) NOT NULL,
 `tmstmp` bigint(11) NOT NULL,
 `approx_lat` mediumint(9) NOT NULL,
 `approx_lng` mediumint(9) NOT NULL,
 `altloc` tinyint(4) NOT NULL,
 `title` varchar(70) COLLATE latin1_general_ci NOT NULL,
 `description` text COLLATE latin1_general_ci NOT NULL,
 `country` tinyint(4) NOT NULL,
 PRIMARY KEY (`id`),
 KEY `uid` (`uid`),
 KEY `tmstmp` (`tmstmp`),
 KEY `country` (`country`),
) ENGINE=MyISAM CHARSET=latin1 COLLATE=latin1_general_ci

CREATE TABLE `location` (
 `id` int(5) NOT NULL,
 `lat` varbinary(50) NOT NULL,
 `latnonce` varbinary(25) NOT NULL,
 `lng` varbinary(50) NOT NULL,
 `lngnonce` varbinary(25) NOT NULL,
 `place` tinyblob NOT NULL,
 `placenonce` tinyblob NOT NULL,
 UNIQUE KEY `id` (`id`),
 KEY `lat` (`lat`),
 KEY `lng` (`lng`)
) 

CREATE TABLE `altlocation` (
 `id` int(5) NOT NULL,
 `eid` int(5) NOT NULL,
 `altlat` varbinary(50) NOT NULL,
 `altlatnonce` varbinary(25) NOT NULL,
 `altlng` varbinary(50) NOT NULL,
 `altlngnonce` varbinary(25) NOT NULL,
 `altplace` tinyblob NOT NULL,
 `altplacenonce` tinyblob NOT NULL,
 UNIQUE KEY `eid` (`eid`),
 KEY `altlat` (`altlat`),
 KEY `altlng` (`altlng`)
)
SELECT 
        e.id, e.uid, e.title, e.description, l.place, l.placenonce, al.altplace, al.altplacenonce,
        IF(e.altloc=0,
            6371 * acos( cos( radians(:lat) ) * cos( radians( AES_DECRYPT(lat, UNHEX('###'), latnonce) ) ) * cos( radians( AES_DECRYPT(lng, UNHEX('###'), lngnonce) ) - radians(:lng) ) + sin( radians(:lat) ) * sin(radians(AES_DECRYPT(lat, UNHEX('###'), latnonce))) ) ,
            6371 * acos( cos( radians(:lat) ) * cos( radians( AES_DECRYPT(altlat, UNHEX('###'), altlatnonce) ) ) * cos( radians( AES_DECRYPT(altlng, UNHEX('###'), altlngnonce) ) - radians(:lng) ) + sin( radians(:lat) ) * sin(radians(AES_DECRYPT(altlat, UNHEX('###'), altlatnonce))) )
        ) AS distance
    FROM 
        (
            SELECT id, uid, title, description
            FROM 
                entrys 
            WHERE 
                    approx_lat > :min_lat
                AND approx_lat < :max_lat
                AND approx_lng > :min_lng
                AND approx_lng < :min_lng   
            ORDER BY 
                e.tmstmp 
            DESC
            LIMIT 2

        ) AS e
    INNER JOIN 
        location l 
    ON l.id = uid 
    LEFT JOIN
        altlocation al
    ON al.eid = e.id
    HAVING 
        distance <= 50
SELECT 
    e.id, e.uid, e.title, e.description, l.place, l.placenonce, al.altplace, al.altplacenonce,
    IF(e.altloc=0,
        6371 * acos( cos( radians(:lat) ) * cos( radians( AES_DECRYPT(lat, UNHEX('###'), latnonce) ) ) * cos( radians( AES_DECRYPT(lng, UNHEX('###'), lngnonce) ) - radians(:lng) ) + sin( radians(:lat) ) * sin(radians(AES_DECRYPT(lat, UNHEX('###'), latnonce))) ) ,
        6371 * acos( cos( radians(:lat) ) * cos( radians( AES_DECRYPT(altlat, UNHEX('###'), altlatnonce) ) ) * cos( radians( AES_DECRYPT(altlng, UNHEX('###'), altlngnonce) ) - radians(:lng) ) + sin( radians(:lat) ) * sin(radians(AES_DECRYPT(altlat, UNHEX('###'), altlatnonce))) )
    ) AS distance
FROM 
    entrys e 
INNER JOIN 
    location l 
    ON l.id = e.uid 
LEFT JOIN
    altlocation al
    ON al.eid = e.id
WHERE 
    e.country = :countryid
    AND l.approx_lat > :min_lat
    AND l.approx_lat < :max_lat
    AND l.approx_lng > :min_lng
    AND l.approx_lng < :min_long    
HAVING 
    distance <= 50
ORDER BY 
    e.tmstmp 
DESC
LIMIT 2