PostgreSQL RDS索引陷阱 - nanit

banq發表於2022-02-05

提高 RDBMS 查詢效能的最常見方法之一是根據查詢的選定欄位和條件新增索引。但有時索引可能會降低資料庫效能而不是提高它。在這篇博文中,我將描述刪除索引如何幫助我們提高效能並減輕 RDS 例項的負載。
幾周前,我們收到了一條 CloudWatch 警報,指出我們的一個 RDS 例項的突發餘額不足。在我們深入細節之前,讓我先解釋一下這個RDS例項的用法以及什麼是突發平衡。
RDS 例項正由名為 Inbox 的服務使用。Inbox 是一項 HTTP 服務,為 Nanit 的客戶提供運動檢測和高溫等事件通知。Inbox 使用 RDS 進行儲存,例項使用通用 (gp2) SSD 卷。
為了解釋突發平衡,讓我們首先深入瞭解 RDS 卷效能。
IOPS、IO 積分和突發餘額
  1. RDS 卷效能透過每秒 IO 運算元 (IOPS) 來衡量:基準效能確定為以 GiB x 3 為單位的卷大小,例如,500 GiB 卷的基準效能為 1500 IOPS。當基準效能不足時,卷可能會消耗 IO 積分。只要卷效能低於基線,積分就會以每 GiB 3 IOPS 的速率累積。這意味著 500 GiB 的卷以 1500 IOPS 的速率累積積分。
  2. 需要注意的是,低於 1 TiB 的卷在 30 分鐘內使用積分的限制為 3000 IOPS。
  3. 突發餘額是可用積分的百分比。

 。。。
 

解釋分析
為了找出查詢執行緩慢的原因,我們使用了EXPLAIN ANALYZE命令。EXPLAIN命令顯示查詢的執行計劃而不實際執行它。
使用 ANALYZE 選項執行查詢,結果包括實際執行時間。
在慢查詢上執行EXPLAIN ANALYZE的結果。我們發現索引掃描需要 10 秒(準確地說是 10444.278),它返回 9019 行,然後由資料庫在記憶體中排序。如果我們只需要一行,從索引掃描返回 9019 行意味著大量冗餘讀取 IOPS,我們發現這可能是我們在看到的 IOPS 激增的原因。
 

索引使用
接下來是瞭解查詢計劃器為何使用此索引以及我們如何減少查詢時間。
下一件事是瞭解為什麼查詢計劃器使用這個索引,以及我們如何能減少查詢時間。我們對索引的結構很感興趣,看看它是否符合查詢的結構(選定的列、where子句、限制),所以我們在psql客戶端執行了\d命令,該命令返回一個表的所有索引及其結構。現在我們有了索引的結構,但令人驚訝的是,列表中包括另一個索引,其結構包括查詢中使用的確切欄位和標準。我們的問題是為什麼查詢計劃器沒有使用正確的索引?我們接下來做的事情是透過使用下面的查詢來看看是否使用了適當的索引。

SELECT
    pg_t.tablename,
    pg_size_pretty(pg_relation_size(pg_c.oid)) AS table_size,
    pg_sai.indexrelname AS index_name,
    pg_sai.idx_scan AS number_of_scans,
    pg_sai.idx_tup_read AS tuples_read,
    pg_sai.idx_tup_fetch AS tuples_fetched
FROM
    pg_tables pg_t
    LEFT JOIN pg_class pg_c ON pg_t.tablename = pg_c.relname
    LEFT JOIN pg_index pg_i ON pg_c.oid = pg_i.indrelid
    LEFT JOIN pg_stat_all_indexes pg_sai ON pg_i.indexrelid = pg_sai.indexrelid
WHERE
    pg_t.schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY 
    pg_t.tablename, 
    table_size;


該查詢輸出在一個索引上啟動的掃描次數。透過多次執行這個查詢,我們可以透過比較兩次執行之間的number_of_scans來確定哪個索引正在被使用,並看到它在增加。
我們注意到,合適的索引根本沒有被使用,也就是說,掃描次數保持不變,而不太合適的索引的掃描次數在我們每次執行查詢時都會增加。令我們驚訝的是,不太最佳化的索引的掃描次數比適當索引的掃描次數要低得多。這個資料讓我們意識到,索引的使用從合適的索引轉移到了不太最佳化的索引,從而增加了讀取IOPS。
 

索引的刪除
接下來我們做的事情是使用DROP INDEX CONCURRENTLY查詢來刪除這個不太最佳化的索引。我們使用CONCURRENTLY選項來避免鎖定表。一旦索引被刪除,查詢計劃器就開始使用適當的索引。
,現在的總執行時間是1ms。此外,索引的使用也從索引掃描變成了只掃描索引。索引掃描從索引和表的堆中獲取行,而僅索引掃描則直接從索引的資料中返回行,而不訪問表的堆。
但最重要的是,只用索引掃描只返回1條記錄,而使用以前的索引則返回9019條記錄。
現在,查詢計劃員開始使用適當的索引,讀取IOPS下降,收件箱完全恢復。
 

總結
一段索引誤區讓我們開始了一段旅程,從前端應用程式開始,訪問了後端程式碼,最後在資料庫本身結束。我們從中學到了很多東西,特別是在除錯計劃器查詢執行輸出和索引使用方面。

相關文章