如何优化以下SELECT查询

时间:2020-02-13 22:12:27

标签: mysql sql mariadb

我们有下表

id  # primary key

device_id_fk 

auth # there's an index on it

old_auth  # there's an index on it

以及以下查询。

$select_user = $this->db->prepare("
        SELECT device_id_fk 
        FROM wtb_device_auths AS dv 
        WHERE (dv.auth= :auth OR dv.old_auth= :auth) 
        LIMIT 1
");

解释,我无法访问主要客户端的服务器,但这是另一个客户端数据较少的

enter image description here

由于auth上还有很多其他更新查询,因此更新查询开始写入慢速查询日志和CPU峰值

如果您从auth中删除索引,则选择查询将写入慢速查询日志,但不会写入更新,如果您向device_id_fk添加索引,则没有任何区别。

我尝试使用union而不是or重写查询,但被告知仍然存在CPU峰值,并且select查询仍然写入慢速查询日志中

$select_user = $this->db->prepare("
    (SELECT device_id_fk 
     FROM wtb_device_auths 
     AS dv WHERE dv.auth= :auth) 
    UNION ALL
    (SELECT device_id_fk 
     FROM wtb_device_auths AS dv 
     WHERE dv.old_auth= :auth)
     LIMIT 1"
    );
");

说明

enter image description here

通常,这是慢查询日志中的唯一查询。有没有编写查询的最佳方法?有没有添加索引的最佳方法?客户端在运行LAMP的centos 6服务器上使用的是旧的MariaDB版本,相当于MYSQL 5.5。

其他信息

enter image description here

enter image description here

每当将索引添加到auth时都会记录到慢查询日志中的更新查询是

$update_device_auth = $this->db->prepare("UPDATE wtb_device_auths SET auth= :auth WHERE device_id_fk= :device_id_fk");

2 个答案:

答案 0 :(得分:1)

您的少数索引应该不会减慢您的更新速度。

您需要两个索引来使您的更新和选择性能都很好。我最好的猜测是你们永远不会同时拥有两者。

UPDATE wtb_device_auths SET auth= :auth WHERE device_id_fk= :device_id_fk

您需要在device_id_fk上建立索引,此更新才能正常运行。并且无论其索引如何,都应将其声明为外键。

    SELECT device_id_fk 
    FROM wtb_device_auths AS dv 
    WHERE (dv.auth= :auth OR dv.old_auth= :auth) 
    LIMIT 1

您需要在auth, old_auth上使用单个组合索引,此查询才能正常运行。

假设没有太多重复项,单独的authold_auth索引也应该能很好地工作。 MySQL will merge the results from the indexes,并且合并应该很快……除非有很多行匹配。

如果您还单独搜索old_auth,请在old_auth上添加索引。

并且,正如其他人指出的那样,select查询可以返回具有匹配auth或old_auth的多个匹配设备之一。这可能是不好的。如果authold_auth是用来标识设备的,则add a unique constraint


或者,您需要重组数据。具有相同值的多列是一个红色标记。随着您的使用,它可能导致索引数量激增,并且还限制了可以存储的版本数。相反,每行只有一个身份验证,并允许每个设备具有多行。

create table wtb_device_auths (
  id serial primary key,
  device_id bigint not null references wtb_devices(id),
  auth text not null,
  created_at datetime not null default current_timestamp,

  index(auth)
);

现在您只需要搜索一列。

select device_id from wtb_device_auths where auth = ?

现在,一个设备可以具有许多wtb_device_auths行。如果您想要设备的当前身份验证,请搜索最新的身份验证。

select device_id
from wtb_device_auths
where device_id = ?
order by created_at desc
limit 1

由于每个设备仅具有少量身份验证,仅使用device_id索引就可能很快。对设备的少数几行进行排序将很快。

如果不是,则可能需要附加的组合索引,例如created_at, device_id。这涵盖了仅通过created_at进行搜索和排序,以及通过created_atdevice_id进行查询和排序的查询。

答案 1 :(得分:0)

OR通常会导致缓慢的全表扫描。这个UNION技巧和合适的INDEXes一起更快:

( SELECT  device_id_fk
    FROM  wtb_device_auths AS dv
    WHERE  dv.auth= :auth
    LIMIT  1 )
UNION ALL
( SELECT  device_id_fk
    FROM  wtb_device_auths AS dv
    WHERE  dv.old_auth= :auth
    LIMIT  1 )
LIMIT 1

并具有以下“复合”索引:

INDEX(auth, device_id)
INDEX(old_auth, device_id)

这些索引可以用相同的第一列替换现有索引。

请注意,我有3个LIMITs;你只有1。

UNION ALL涉及一个临时表。您应该至少升级到5.7;该版本可以优化临时表。

没有LIMIT的{​​{1}}给出随机行;可以吗?

请为此查询提供慢速日志条目的整个文本-它具有可能有用的信息。如果“ Rows_examined”大于2(或3),则说明发生了奇怪的情况。

相关问题