
系列
- 1 分鍾快速使用 Docker 上手最新版 Sentry-CLI - 創建版本
- 快速使用 Docker 上手 Sentry-CLI - 30 秒上手 Source Maps
- Sentry For React 完整接入詳解
- Sentry For Vue 完整接入詳解
- Sentry-CLI 使用詳解
- Sentry Web 性能監控 - Web Vitals
- Sentry Web 性能監控 - Metrics
- Sentry Web 性能監控 - Trends
- Sentry Web 前端監控 - 最佳實踐(官方教程)
- Sentry 后端監控 - 最佳實踐(官方教程)
- Sentry 監控 - Discover 大數據查詢分析引擎
- Sentry 監控 - Dashboards 數據可視化大屏
- Sentry 監控 - Environments 區分不同部署環境的事件數據
- Sentry 監控 - Security Policy 安全策略報告
- Sentry 監控 - Search 搜索查詢實戰
- Sentry 監控 - Alerts 告警
- Sentry 監控 - Distributed Tracing 分布式跟蹤
- Sentry 監控 - 面向全棧開發人員的分布式跟蹤 101 系列教程(一)
- Sentry 監控 - Snuba 數據中台架構簡介(Kafka+Clickhouse)
- Sentry 監控 - Snuba 數據中台架構(Data Model 簡介)
Snuba 有一個查詢處理管道,首先將 Snuba 查詢語言( legacy 和 SnQL)解析為 AST,然后在 Clickhouse 上執行 SQL 查詢。在這兩個階段之間,在 AST 上執行幾次傳遞以應用查詢處理轉換。
處理管道有兩個主要目標:優化查詢並防止對我們的基礎設施構成危險的查詢。
在數據模型上,查詢處理流水線分為邏輯部分,進行產品相關處理,物理部分專注於優化查詢。
邏輯部分包含查詢驗證等步驟,以確保它與數據模型匹配或應用自定義函數。 物理部分包括諸如提升標簽(promoting tags)和選擇預聚合視圖(pre-aggregated view)來為查詢提供服務等步驟。
查詢處理階段
本節介紹了上述各階段的代碼和示例,並提供了一些提示。

Legacy 和 SnQL 解析器
Snuba 支持兩種語言,傳統的基於 JSON 的語言和新的名為 SnQL 的語言。除了傳統語言不支持的連接和復合查詢之外,查詢處理管道不會更改是否使用一種或另一種語言。
Snuba 支持兩種語言,一種是基於 JSON 的舊語言,另一種是名為 SnQL 的新語言。 除了遺留語言不支持的連接和復合查詢之外,無論使用哪種語言,查詢處理管道都不會改變。
它們都生成一個邏輯查詢AST,該查詢由下面數據結構表示。
基於 JSON 的語言舊解析器源碼:
SnQL 解析器:
查詢驗證(Query Validation)
此階段確保可以運行查詢(大多數情況下,我們還沒有捕獲所有可能的無效查詢)。 這個階段的職責是在無效查詢的情況下返回一個 HTTP400,並向用戶提供適當的有用消息。
這分為兩個子階段:一般驗證(general validation)和實體特定驗證(entity specific validation)。
一般驗證由一組檢查組成,這些檢查在解析器生成查詢之后立即應用於每個查詢。這在 QueryEntity 函數中發生。這包括防止別名陰影(alias shadowing)和函數簽名驗證(function signature validation)等驗證。
- QueryEntity:https://github.com/getsentry/snuba/blob/master/snuba/query/parser/init.py#L91
每個實體也可以以必需列的形式提供一些驗證邏輯。這發生在 class Entity(Describable, ABC):。 這允許查詢處理拒絕在 project_id 上沒有條件或沒有時間范圍的查詢。
邏輯查詢處理器(Logical Query Processors)
查詢處理器是無狀態轉換,接收查詢對象(及其 AST)並就地轉換。這是為邏輯處理器實現的接口。在邏輯階段,每個實體提供按順序應用的查詢處理器。常見的用例是像 apdex 這樣的自定義函數,或者像時間序列處理器(time series processor)那樣的計時。
- apdex: https://github.com/getsentry/snuba/blob/10b747da57d7d833374984d5eb31151393577911/snuba/query/processors/performance_expressions.py#L12-L20
- time series processor:https://github.com/getsentry/snuba/blob/master/snuba/query/processors/timeseries_processor.py
查詢處理器不應該依賴於在之前或之后執行的其他處理器,並且應該彼此獨立。
存儲選擇器(Storage Selector)
如 Snuba 數據模型中所述,每個實體可以定義多個存儲。 多個存儲代表多個表,並且出於性能原因可以定義物化視圖(materialized views),因為某些視圖可以更快地響應某些查詢。
在邏輯處理階段(完全基於實體)結束時,存儲選擇器可以檢查查詢並為查詢選擇合適的存儲。 存儲選擇器在實體數據模型中定義並實現此接口。 一個例子是 Errors 實體,它有兩個存儲,一個用於一致查詢(它們被路由到寫入事件的相同節點),另一個只包括我們沒有寫入的副本來服務大多數查詢。 這減少了我們寫入的節點上的負載。
查詢轉換器(Query Translator)
不同的 storage 有不同的 schema(這些反映了 clickhouse 表或視圖的 schema)。 它們通常都與實體模型不同,最顯着的例子是用於標簽 tags[abc] 的可下標表達式,它在 clickhouse 中不存在,其中訪問標簽看起來像 tags.values[indexOf(tags.key, 'abc')]。
選擇 storage 后,需要將查詢轉換為物理查詢。Translator 是一個基於規則的系統,規則由實體(針對每個 storage)定義並按順序應用。
與查詢處理器相反,翻譯規則在查詢上沒有完整的上下文,只能翻譯單個表達式。 這使我們能夠輕松地編寫翻譯規則並跨實體重用它們。
這些是 transactions 實體的轉換規則。
物理查詢處理器(Physical Query Processors)
與邏輯查詢處理器相比,物理查詢處理器的工作方式非常相似。它們的接口非常相似,語義相同。 不同之處在於它們對物理查詢進行操作,因此,它們主要是為優化而設計的。 例如,該處理器在標簽上找到相等條件,並將它們替換為標簽哈希圖(有布隆過濾器索引)上的等效條件,從而使過濾操作更快。
查詢拆分器(Query Splitter)
通過將某些查詢拆分為多個單獨的 Clickhouse 查詢並組合每個查詢的結果,可以以優化的方式執行某些查詢。
兩個例子是時間拆分和列拆分。兩者都在下面這個文件中。
時間拆分(Time splitting)將一個查詢(不包含聚合且已正確排序)在一個可變的時間范圍內拆分為多個查詢,該時間范圍的大小逐漸增大,並在得到足夠的結果后按順序停止執行。
列拆分(Column splitting)拆分篩選和列獲取。它對最少數量的列執行查詢的篩選部分,以便 Clickhouse 加載較少的列,然后通過第二個查詢,僅為第一個查詢篩選的行獲取缺少的列。
查詢格式化器(Query Formatter)
該組件只是將查詢格式化為 Clickhouse 查詢字符串。
復合查詢處理
上面的討論僅適用於簡單查詢、復合查詢(連接和包含子查詢的查詢遵循稍微不同的路徑)。
上面討論的簡單查詢管道不適用於連接查詢或包含子查詢的查詢。 為了使這項工作發揮作用,每個步驟都必須考慮連接的查詢和子查詢,這會增加過程的復雜性。
為了解決這個問題,我們將每個連接查詢轉換為多個簡單子查詢的連接。每個子查詢都是一個簡單的查詢,可以通過上述管道進行處理。這也是運行 Clickhouse 連接(join)的首選方式,因為它允許我們在連接之前應用過濾器。

此類查詢的查詢處理管道由與上述內容相關的幾個附加步驟組成。
子查詢生成器(Subquery Generator)
該組件采用一個簡單的 SnQL 連接查詢,並為連接中的每個表創建一個子查詢。
表達式下推(Expressions Push Down)
上一步生成的查詢將是一個有效的連接,但效率極低。 這一步基本上是一個連接優化器(join optimizer),它將所有可以成為子查詢一部分的表達式下推到子查詢中。 這是一個獨立於子查詢處理的必要步驟,因為 Clickhouse join 引擎不執行任何表達式下推,所以它由 Snuba 來優化查詢。
簡單查詢處理管道(Simple Query Processing Pipeline)
這與上面討論的從邏輯查詢驗證到物理查詢處理器的管道相同。
連接優化(Join Optimizations)
在處理結束時,我們可以對整個復合查詢應用一些優化,例如將 join 轉換為 Semi Join。
