繁体   English   中英

如何优化以下 SELECT 查询

[英]How to optimize the following SELECT query

我们有下表

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
");

解释一下,我无法连接到主客户端的服务器,但这是另一个数据较少的客户端

在此处输入图像描述

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

如果您从 auth 中删除索引,则 select 查询将写入慢速查询日志,但不会写入更新,如果您将索引添加到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"
    );
");

解释

在此处输入图像描述

大多数情况下,这是慢速查询日志中的唯一查询。 有没有更优化的方式来编写查询? 有没有更优化的方法来添加索引? 客户端正在使用旧的 MariaDB 版本,相当于 MYSQL 5.5,在运行 LAMP 的 centos 6 服务器上

附加信息

在此处输入图像描述

在此处输入图像描述

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

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

你的几个索引不应该减慢你的更新。

您需要两个索引才能使您的更新和 select 都表现良好。 我最好的猜测是你从来没有同时拥有过两者。

 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 将合并索引的结果并且合并应该很快......除非很多行匹配。

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

而且,正如其他人指出的那样, select查询可能会返回具有匹配 auth 或 old_auth 的多个匹配设备之一。 这可能很糟糕。 如果authold_auth旨在标识设备,请添加唯一约束


或者,您需要重组数据。 包含相同值的多个列是一个危险信号。 正如您所经历的那样,它可能会导致索引激增,并且还会限制您可以存储的版本数量。 相反,每行只有一个身份验证,并允许每个设备有多行。

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进行搜索和排序的查询。

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(至少); 该版本优化了临时表。

没有ORDER BYLIMIT给出随机行; 那样行吗?

请为这个查询提供慢日志条目的完整文本——它包含可能有用的信息。 如果“Rows_examined”大于 2(或者可能是 3),那么就会发生一些奇怪的事情。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM