簡體   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