數據庫全量 SQL 分析與審計系統性能優化之旅

全量 SQL(所有訪問數據庫的 SQL)可以有效地幫助安全進行數據庫審計,幫助業務快速排查性能問題。一般可通過開啓 genlog 日誌或者啓動 MySQL 審計插件方式來進行獲取,而美團選用了一種非侵入式的旁路抓包方案,使用 Go 語言實現。無論採用哪種方案,都需要重點關注它對數據庫的性能損耗。

本文介紹了美團基礎研發平臺抓包方案在數據庫審計實踐中遇到的性能問題以及優化實踐,希望能對大家有所幫助或啓發。

1 背景

數據庫安全一直是美團信息安全團隊和數據庫團隊非常注重的領域,但由於歷史原因,對數據庫的訪問只具備採樣審計能力,導致對於一些攻擊事件無法快速地發現、定損和優化。安全團隊根據歷史經驗,發現攻擊訪問數據庫基本上都存在着某些特徵,經常會使用一些特定 SQL,我們希望通過對 MySQL 訪問流量進行全量分析,識別出慣用 SQL,在數據庫安全性上做到有的放矢。

2 現狀及挑戰

下圖是採樣 MySQL 審計系統的架構圖,數據採集端基於 pcap 抓包方式實現,數據處理端選用美團大數據中心的日誌接入方案。所有 MySQL 實例都部署了用於採集 MySQL 相關數據的 rds-agent、日誌收集的 log-agent。rds-agent 抓取到 MySQL 訪問數據,通過 log-agent 上報到日誌接收端,爲了減少延時,上報端與接收端間做了同機房調度優化。日誌接收端把數據寫入到約定的 Kafka 中,安全團隊通過 Storm 實時消費 Kafka 分析出攻擊事件,並定期拉數據持久化到 Hive 中。

我們發現,通常被攻擊的都是一些核心 MySQL 集羣。經統計發現,這些集羣單機最大 QPS 的 9995 線約 5 萬次左右。rds-agent 作爲 MySQL 機器上的一個寄生進程,爲了宿主穩定性,資源控制也極爲重要。爲了評估 rds-agent 在高 QPS 下的表現,我們用 Sysbench 對 MySQL 進行壓測,觀察在不同 QPS 下 rds-agent 抓取的數據丟失率和 CPU 消耗情況,從下面的壓測數據來看結果比較糟糕:

如何在高 QPS 下保證較低的丟失率與 CPU 消耗?已經成爲當前系統的一個亟待解決的難題與挑戰。

3 分析及優化

下面主要介紹圍繞丟失率與 CPU 消耗這一問題,我們對數據採集端在流程、調度、垃圾回收和協議方面做的分析與改進。

3.1 數據採集端介紹

首先,簡要介紹一下數據採集端 rds-agent,它是一個 MySQL 實例上的進程,採用 Go 語言編寫,基於開源的 MysqlProbe 的 Agent 改造。通過監聽網卡上 MySQL 端口的流量,分析出客戶端的訪問時間、來源 IP、用戶名、SQL、目標數據庫和目標 IP 等審計信息。下面是其架構圖,主要分爲 5 大功能模塊:

1. probe

probe 意爲探針,採用了 gopacket 作爲抓包方案,它是谷歌開源的一個 Go 抓包庫,封裝了 pcap。probe 把抓取到原始的數據鏈路層幀封裝成 TCP 層的數據包。通過變種的 Fowler-Noll-Vo 算法哈希源和目的 IP port 字段,快速實現把數據庫連接打散到不同的 worker 中,該算法保證了同一連接的來包與回包的哈希值一樣。

2. watcher

登錄用戶名對於審計來說極其重要,客戶端往往都是通過長連接訪問 MySQL,而登錄信息僅出現在 MySQL 通信協議的認證握手階段,僅通過抓包容易錯過。

watcher 通過定時執行 show processlist 獲取當前數據庫的所有連接數據,通過對比 Host 字段與當前包的客戶端 ip port,補償錯過的用戶名信息。

3. worker

不同的 worker 負責管理不同數據庫連接的生命週期,一個 worker 管理多個連接。通過定期比對 worker 的當前連接列表與 watcher 中的連接列表,及時發現過期的連接,關閉並釋放相關資源,防止內存泄漏。

4. connStream

整個數據採集端的核心邏輯,負責根據 MySQL 協議解析 TCP 數據包並識別出特定 SQL,一個連接對應一個 connStream Goroutine。因爲 SQL 中可能包含敏感數據,connStream 還負責對 SQL 進行脫敏,具體的特定 SQL 識別策略,由於安全方面原因,這裏不再進行展開。

5. sender

負責數據上報邏輯,通過 thrift 協議將 connStream 解析出的審計數據上報給 log-agent。

3.2 基礎性能測試

抓包庫 gopacket 的性能直接決定了系統性能上限,爲了探究問題是否出在 gopacket 上,我們編寫了簡易的 tcp-client 和 tcp-server,單獨對 gopacket 在數據流向圖中涉及到的前三個步驟(如下圖所示)進行了性能測試,從下面的測試結果數據上看,性能瓶頸點不在 gopacket。

3.3 CPU 畫像分析

丟失率與 CPU 消耗二者密不可分,爲了探究如此高 CPU 消耗的原因,我們用 Go 自帶的 pprof 工具對進程的 CPU 消耗進行了畫像分析,從下面火焰圖的調用函數可以歸納出幾個大頭:SQL 脫敏、解包、GC 和 Goroutine 調度。下面主要介紹一下圍繞它們做的優化工作。

3.4 脫敏分析及改進

因爲 SQL 中可能包含敏感信息,出於安全考慮,rds-agent 會對每一條 SQL 進行脫敏處理。

脫敏操作使用了 pingcap 的 SQL 解析器對 SQL 進行模板化:即把 SQL 中的值全部替換成 “?” 來達到目的,該操作需要解析出 SQL 的抽象語法樹,代價較高。當前只有採樣和抓取特定 SQL 的需求,沒有必要在解析階段對每條 SQL 進行脫敏。這裏在流程上進行了優化,把脫敏下沉到上報模塊,只對最終發送出去的樣本脫敏。

這個優化取得的效果如下:

3.5 調度分析及改進

從下面的數據流向圖可以看出整個鏈路比較長,容易出現性能瓶頸點。同時存在衆多高頻運行的 Goroutine(紅色部分),由於數量多,Go 需要經常在這些 Goroutine 間進行調度切換,切換對於我們這種 CPU 密集型的程序來說無疑是一種負擔。

針對該問題,我們做了如下優化:

  1. 縮短鏈路:分流、worker、解析 SQL 等模塊合併成一個 Goroutine 解析器。

  2. 降低切換頻率:解析器每 5ms 從網絡協議包的隊列中取一次,相當於手動觸發切換。(5ms 也是一個多次測試後的折中數據,太小會消耗更多的 CPU,太大會引起數據丟失)

這個優化取得的效果如下:

3.6 垃圾回收壓力分析及改進

下圖爲 rds-agent 抓包 30 秒,已分配指針對象的火焰圖。可以看出已經分配了 4 千多萬個對象,GC 壓力可想而知。關於 GC,我們瞭解到如下兩種優化方案:

  1. 池化:Go 的標準庫中提供了一個 sync.Pool 對象池,可通過複用對象來減少對象分配,從而降低 GC 壓力。

  2. 手動管理內存:通過系統調用 mmap 直接向 OS 申請內存,繞過 GC,實現內存的手動管理。

但是,方案 2 容易出現內存泄漏。從穩定性的角度考慮,我們最終選擇了方案 1 來管理高頻調用函數里創建的指針對象,這個優化取得的效果如下:

3.7 解包分析及改進

MySQL 是基於 TCP 協議之上的,在功能調試過程中,我們發現了很多空包。從下面的 MySQL 客戶端 - 服務端數據的交互圖可以看出:當客戶端發送一條 SQL 命令,服務端響應結果,由於 TCP 的消息確認機制,客戶端會發送一個空的 ack 包來確認消息,而且空包在整個流程中的比例較大,它們會穿透到解析環節,在高 QPS 下對於 Goroutine 調度和 GC 來說無疑是一個負擔。

下圖是 MySQL 數據包的唯一格式,通過分析,我們觀察到以下特點:

  1. 一個完整的 MySQL 數據包長度 >=4Byte

  2. 客戶端新發送命令的 sequence id 都是爲 0 或者 1

而 pcap 支持設置過濾規則,讓我們可以在內核層將空包排除掉,下面是上述特點對應的兩條過濾規則:

特點1:ip[2:2] - ((ip[0] & 0x0f) << 2) - ((tcp[12:1] & 0xf0) >> 2) >= 4
特點2: (dst host {localIP} and dst port 3306 and (tcp[(((tcp[12:1] & 0xf0) >> 2) + 3)] <= 0x01))

這個優化取得的效果如下:

基於上述經驗,我們對數據採集端進行功能代碼重構,同時還進行一些其它優化。

4 最終成果

下面是優化前後的數據對比,丟失率從最高 60% 下降到了 0%, CPU 消耗從最高佔用 6 個核下降到了 1 個核。

爲了探究抓包功能對 MySQL 性能損耗,我們用 Sysbench 做了一個性能對比測試。從下面的結果數據可以看出功能對 MySQL 的 TPS、QPS 和響應時間 99 線指標最高大約有 6% 的損耗。

5 未來規劃

雖然我們對抓包方案進行了各種優化,但對於一些延遲敏感的業務來說性能損耗還是偏大,而且該方案對一些特殊場景支持較差:如 TCP 協議層發生丟包、重傳、亂序時,MySQL 協議層使用壓縮、傳輸大 SQL 時。而業界普遍採用了直接改造 MySQL 內核的方式來輸出全量 SQL,同時也支持輸出更多的指標數據。

目前,數據庫內核團隊也完成了該方案開發,正在線上灰度替換抓包方案中。另外,對於線上全量 SQL 端到端丟失率指標的缺失,我們也將陸續進行補齊。

本文作者

粟含,來自於美團基礎研發平臺 / 基礎技術部 / 數據庫技術中心。

美團技術團隊 10000 + 工程師,如何支撐中國領先的生活服務電子商務平臺?數億消費者、數百萬商戶、2000 多個行業、幾千億交易額背後是哪些技術在支撐?這裏是美團、大衆點評、美團外賣、美團配送、美團優選等技術團隊的對外窗口。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/g2VD9SK0xq8R8biG2HyUfw