GH GambleHub

索引和優化查詢

1)索引化和優化目標

潛伏期:減少P50/P95/P99。
吞吐量:QPS的增長沒有水平縮放。
可預見性:計劃穩定,響應時間沒有「飛躍」。
節省:IO/CPU更少,雲費更少。
可靠性:通過正確的可用性減少鎖定和阻塞。

不變量:
  • 任何優化都必須保持正確性和一致性。
  • 跟蹤計劃指標和邏輯中的效果。

2)索引的基本結構以及何時應用

2.1 B樹(默認值)

等於/範圍,排序,「ORDER BY」。
適用於大多數時間/ID/狀態過濾器。

2.2 Hash

純等式('=')在內存上更便宜,但沒有順序(PG:取消限制,但仍然是一個利基選擇)。

2.3 GIN / GiST (PostgreSQL)

GIN: 數組/JSONB密鑰、全文、containment('@>')。
GiST:地理,範圍,kNN。

2.4 BRIN (PostgreSQL)

超便宜的指數通過「自然排序」的桌子(時間僅適合)。適用於帶有大表的時間系列。

2.5位圖(MySQL/InnoDB:非本機;DW-DBMS/OLAP)

對低基數和小面有效,通常在柱狀存儲器中。

2.6列索引(ClickHouse)

Primary key + data skipping (minmax), secondary через `skip indexes` (bloom, set).

帶有聚合和範圍的OLAP查詢。

2.7反向索引(Elasticsearch/OpenSearch)

全文,面板,混合搜索。對於精確的過濾器,請使用keyword字段和doc values。

2.8 MongoDB

Single,compound,multikey(數組),partial,TTL,text,hashed(用於通過均勻鍵進行散列)。

3)密鑰和復合索引設計

3.1左前綴規則"

索引中的字段順序決定了可用性。

查詢'WHERE tenant_id=?AND created_at >=?ORDER BY created_at DESC` → индекс `(tenant_id, created_at DESC, id DESC)`.

3.2 Tie-breaker

添加獨特的尾巴(通常為「id」)以進行穩定的排序和seek分頁。

3.3部分/過濾索引

僅索引熱子集:
sql
CREATE INDEX idx_orders_paid_recent
ON orders (created_at DESC, id DESC)
WHERE status = 'paid' AND created_at > now() - interval '90 days';

3.4覆蓋索引

在索引中包含「可讀」字段(MySQL: 「INCLUDE」;PG 11+: `INCLUDE`):

sql
CREATE INDEX idx_user_lastseen_inc ON users (tenant_id, last_seen DESC) INCLUDE (email, plan);

3.5功能/可計算

使索引中的鍵正常化:
sql
CREATE INDEX idx_norm_email ON users (lower(email));

4)參與和混亂

4.1參與(PG本機/表繼承;MySQL RANGE/LIST)

按時間輪換(「daily/weekly」)簡化了「VACUUM/DELETE」。
指數是本地分期→小於B樹,比計劃更快。

sql
CREATE TABLE events (
tenant_id bigint,
ts timestamptz,
...
) PARTITION BY RANGE (ts);

4.2黨派鑰匙

在OLTP中-通過「tenant_id」(負載本地化)。
在時間系列/OLAP中-通過「ts」(範圍查詢)。
混合體:「(tenant_id,ts)」+子條紋。

4.3 Sharding

通過「tenant_id」或時間進行一致性收縮/範圍檢查。
跨碼查詢→ scatter-gather和k-way merge;保持每張收件箱。

5)統計,基數和計劃

5.1最新統計

啟用自動分析(「autovacuum/autoanalyze」),將「default_statistics_target」放大「臟」分布。

5.2高級統計(PG)

相關列:
sql
CREATE STATISTICS stat_user_country_city (dependencies) ON country, city FROM users;
ANALYZE users;

5.3執行計劃

參見「EXPLAIN(ANALYZE,BUFFERS,VERBOSE)」;關鍵字段:
  • `Rows`, `Loops`, `Actual time`, `Shared Read/Hit`, `Recheck Cond`.
  • Типы join: Nested Loop, Hash Join, Merge Join.
  • Seq Scan vs Index Scan/Only Scan/Bitmap Heap Scan.

5.4計劃穩定性

參數化(預置狀態)可能在不良計劃上「紮根」。使用plan cache guerrails (PG: 'plan_cache_mode=force_custom_plan'對於有問題的查詢)或「鉆孔」常數。

6)優化加入和排序

6.1個戰略

Nested Loop:內部較小的外部快速指數。
Hash Join:大套件,在hash表下有足夠的內存。
Merge Join:排序的輸入,如果已經有順序,則有利可圖。

6.2合並下的索引

對於'A JOIN B ON B.a_id=A.id' →索引為'B (a_id)'。
對於join後過濾器,是內部表過濾器列上的索引。

6.3個分類

避免在沒有相應索引的情況下使用「ORDER BY」;通過內存/磁盤對大型集進行排序。

7)重寫查詢(query rewrite)

擺脫分帶的「雪花」;在JOIN中展開。
使用CTE-inline (PG ≥12 inlines CTE默認,但如果需要,「MATERIALIZED」可以提交中間結果)。
清除「SELECT」 →列出字段(節省IO/網絡)。
將計算從「WHERE」遷移到索引形式(預測列)。
聚合:具有增量更新的預匯總表/實例化視圖。

8) Batching,限制和分區

Batch-insert/Update: 500-5000封裝而不是後綴。
Seek分割為'(sort_key,id)'而不是深'OFFSET'。
排序/join之前的設置限制(推下「LIMIT」)。

9)緩存和非正規化

應用程序級別的Query-cache(鍵=SQL+bind-vars+權限版本)。
重型單元的材料化視圖;輪換/反射計劃。
非歸一化:存儲經常可讀的可計算字段(按折扣計算的價格),但具有用於一致性的觸發/背景任務。
Redis是「熱鍵」的L2(具有TTL和事件障礙)。

10)流行引擎的細節

10.1 PostgreSQL

Индексы: B-Tree, Hash, GIN/GiST, BRIN, partial, functional, INCLUDE.

示例:
sql
CREATE INDEX idx_orders_tenant_created_desc
ON orders (tenant_id, created_at DESC, id DESC)
INCLUDE (amount, status);
全文:
sql
CREATE INDEX idx_docs_fts ON docs USING GIN (to_tsvector('russian', title          ' '          body));

10.2 MySQL/InnoDB

復合的覆蓋索引(通過在鍵中包含字段),測試的無形索引:
sql
ALTER TABLE orders ALTER INDEX idx_old INVISIBLE; -- check risk-free plans

直方圖統計('ANALYZE TABLE……UPDATE HISTOGRAM` в 8.0).

10.3 ClickHouse

主鍵=排序;'ORDER BY (tenant_id, ts, id)'。

跳過索引:
sql
CREATE TABLE events (
tenant_id UInt64,
ts DateTime64,
id UInt64,
payload String,
INDEX idx_bloom_payload payload TYPE bloom_filter GRANULARITY 4
) ENGINE = MergeTree()
ORDER BY (tenant_id, ts, id);

10.4 MongoDB

復合/復合:順序很重要,過濾器和排序必須與索引匹配:
js db. orders. createIndex({ tenant_id: 1, created_at: -1, _id: -1 });
db. orders. createIndex({ status: 1 }, { partialFilterExpression: { archived: { $ne: true } } });

使用「hint()」進行診斷,註意「covered query」。

10.5 Elasticsearch/OpenSearch

Keyword vs文本字段;排序/聚合doc_values。
重分段:聚合-重分段;限制「大小」並使用「合成」聚合(逐頁采樣)。
不要在需要精確比較的地方打開分析儀。

11)競爭力、鎖定和MVCC

短期交易;避免在「REPEATABLE READ」下進行「長期」閱讀而無需。
索引操作也采用鎖定(通過引導減少寫入)。
計劃在線索引:「CREATE INDEX CONCURRENTLY」(PG),「ALGORITHM=INPLACE」/「ONLINE」(MySQL)。
按小時/ID插入尾巴→索引的「熱頁」;分配密鑰(UUIDv7/鹽)。

12)可觀察性和SLO

度量標準:
  • 「db_query_latency_ms」 (P50/P95/P99)按查詢名稱排列。
  • `rows_examined`, `rows_returned`, `buffer_hit_ratio`.
  • `deadlocks`, `lock_wait_ms`, `temp_sort_disk_usage`.
  • 「Seq Scan」計劃中預期「Index Scan」的比例。
  • 更改DBMS版本/參數時出現倒退。
Logi/Tracing:
  • 打開帶閾值的慢查詢日誌(例如200毫秒)。
  • 查詢與spans的相關性(trace_id)。
  • 刪除有問題的查詢計劃,並存儲在對象存儲中以進行回顧。
SLO示例:
  • 在「LIMIT<=50」和「熱」tenant下讀取P95「<=150 ms」。
  • P95記錄「<=200 ms」在戰鬥中最多可達1000行。

13)安全性和多重性

訪問控制字段(「tenant_id」,「owner_id」)上的索引是強制性的。
保單(RLS/ABAC)必須是預過濾器;否則,優化程序計劃不正確。
不要以開放形式索引敏感字段;使用哈希/令牌。

14)反模式

深度為「OFFSET」,沒有seek光標替代品。
「每個所有一個索引」是內存過載和寫路徑。
關鍵路徑中的「SELECT」。
「WHERE」中列上方的函數沒有函數索引。
由於舊的統計數據,計劃不穩定。
在等待穩定順序時不存在「ORDER BY」。
索引為索引:ROI <0由於昂貴的記錄/支持。

15)實施支票

1.QPS和時間的前N查詢→選擇3-5名候選人。
2.刪除「EXPLAIN ANALYZE」計劃,檢查基數vs實際。
3.設計索引:字段順序,INCLUDE/partial/functional。
4.為大型表(臨時/影子鍵)實施分批。
5.重寫查詢:刪除「SELECT」,將簡單的CTE插頁,限制設置。
6.包括batching和seek分割。
7.配置緩存:L1/L2、事件障礙。

8.引入計劃監控和慢速日誌,Alertes回歸.

9.使用實際數據分布進行負載測試。
10.更新開發天線(ORM-hint,索引,限制)。

16)「之前/之後」示例"

之前:
sql
SELECT FROM orders
WHERE status = 'paid'
ORDER BY created_at DESC
LIMIT 50 OFFSET 5000;
之後:
sql
-- Индекс: (status, created_at DESC, id DESC) INCLUDE (amount, currency)
SELECT id, amount, currency, created_at
FROM orders
WHERE status = 'paid'
AND (created_at, id) < (:last_ts,:last_id)   -- seek
ORDER BY created_at DESC, id DESC
LIMIT 50;

17) ORM和API協議

避免N+1:貪婪的樣本(「includes」,「JOIN FETCH」,「preload」)。
顯式字段投影,通過光標標定。
gRPC/REST:限制「page_size」,固定「sort」,使用不透明的令牌。
計劃緩存:使用參數化;不要為每個調用生成「唯一」SQL。

18)遷移和運營

在線添加索引和標記為INVISIBLE/CONCURRENTLY,測試計劃,然後切換。
索引修訂版是定期的衛生清潔:舊幻影的副本,未使用的「死亡」。
分期輪換計劃(舊分期付款)和「VACUUM/OPTIMIZE」時間表。

19)總結

查詢優化是系統工程:正確的密鑰和索引,整潔的計劃,深思熟慮的分期和散列,查詢和ORM的紀律,緩存和可觀察性。通過遵循所描述的模式,您將獲得一個快速,可預測且經濟高效的系統,可以抵抗數據和負載的增長。

Contact

與我們聯繫

如有任何問題或支援需求,歡迎隨時聯絡我們。我們隨時樂意提供協助!

開始整合

Email 為 必填。Telegram 或 WhatsApp 為 選填

您的姓名 選填
Email 選填
主旨 選填
訊息內容 選填
Telegram 選填
@
若您填寫 Telegram,我們將在 Email 之外,同步於 Telegram 回覆您。
WhatsApp 選填
格式:國碼 + 電話號碼(例如:+886XXXXXXXXX)。

按下此按鈕即表示您同意我們處理您的資料。