[英]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
使此查詢正常執行。
假設沒有太多重復項,單獨的auth
和old_auth
索引也應該能很好地工作。 MySQL 將合並索引的結果並且合並應該很快......除非很多行匹配。
如果您還單獨搜索old_auth
,請在old_auth
上添加索引。
而且,正如其他人指出的那樣, select
查詢可能會返回具有匹配 auth 或 old_auth 的多個匹配設備之一。 這可能很糟糕。 如果auth
和old_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_at
和device_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 BY
的LIMIT
給出隨機行; 那樣行嗎?
請為這個查詢提供慢日志條目的完整文本——它包含可能有用的信息。 如果“Rows_examined”大於 2(或者可能是 3),那么就會發生一些奇怪的事情。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.