我们有下表
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中删除索引,则选择查询将写入慢速查询日志,但不会写入更新,如果您向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"
);
");
说明
通常,这是慢查询日志中的唯一查询。有没有编写查询的最佳方法?有没有添加索引的最佳方法?客户端在运行LAMP的centos 6服务器上使用的是旧的MariaDB版本,相当于MYSQL 5.5。
其他信息
每当将索引添加到auth
时都会记录到慢查询日志中的更新查询是
$update_device_auth = $this->db->prepare("UPDATE wtb_device_auths SET auth= :auth WHERE device_id_fk= :device_id_fk");
答案 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
上使用单个组合索引,此查询才能正常运行。
假设没有太多重复项,单独的auth
和old_auth
索引也应该能很好地工作。 MySQL will merge the results from the indexes,并且合并应该很快……除非有很多行匹配。
如果您还单独搜索old_auth
,请在old_auth
上添加索引。
并且,正如其他人指出的那样,select
查询可以返回具有匹配auth或old_auth的多个匹配设备之一。这可能是不好的。如果auth
和old_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_at
和device_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),则说明发生了奇怪的情况。