深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景

阿里巴巴資料庫技術發表於2020-06-23

深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景

作者:田傑,阿里雲資料庫高階運維專家

在資料庫的日常使用中,來自應用的高併發場景並不罕見,其標誌性的表現為高新連線建立速率(CPS,比如 PHP 短連線)、傳送大量請求到 DB 資料庫層

如同海嘯,大量的新建連線和請求猛烈的衝擊考驗著 DB 層的處理能力,非常容易出現資料庫被衝擊 hang 住或響應極其緩慢的情況(想象下無預知無緩衝的短時間內突然工作量翻漲數倍,會不會立刻被忙哭了 ^_^)。

而資料庫通常作為架構最下端的資料存取匯聚單元,其效能表現和穩定性往往決定了應用的最終表現和使用體驗,可謂業務生死之大事,不可不察。
由此,我們一起看一下 “海嘯” 場景下可以用來 “保命” 的各種解決方案。

注:

• 本文目標是總結高併發場景下的應對處理方法,而應對熱點更新(秒殺)場景的“招數”會另文介紹。

• 本文的主旨在於方便資料庫的使用者理解業務高併發請求場景下的保障 DB 可用性和穩定性的機制和方法,非機制的全面深度技術細節介紹。


01

執行緒池


1.1 模型

深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景

我們舉一個生活中的例子方便大家理解執行緒池(Thread Pool)

比如有個銀行,有 10 個視窗(例項規格 CPU 數量),官方說可以容納 10 人(Client Thread)。平時呢,人也不多,一直順暢。稍微忙一點呢,大家擠擠。這個 10 人的地方,擠個 50 人也可以(不是每個人時刻都在視窗辦業務)。效率也挺高。

年底發工資、公司結算、發行紀念幣來了一大幫人,大家一起擠,誰也不讓,就把銀行擠滿了;大家接踵摩肩,動也動不了,再發生些爭搶,那這銀行誰也辦不了業務了。

好了,來個保安(Timer Thread),搞了個隊伍機制(10 個佇列 loose_thread_pool_size = 10),按規定執行,一次放 10 個人。這個效率也不錯。

當然了,如果一下子來了 1000 個人,那麼門口等待的隊伍會很長,雖然不致於把銀行撐爆,但是後面的同學要等很長時間,有的會去抱怨了(應用側等待超過自身定義的超時時間後返回錯誤)。

問題來了,有的同學搞不清楚買哪種紀念幣,一直在看看停停,保安看他們也不像馬上能決定的樣子,而且視窗櫃員也不是非常忙,保安就又搞了個規則,叫 “stall_limit”

看一些同學猶豫超過 stall_limit 定義的時間,那麼就算他們 stall 了,可以再放 1 個人進去(oversubscribing)。但去視窗辦業務的人數是有上限的,最多 50 個人(10 個視窗每個視窗 5 個人, loose_thread_pool_oversubscribe = 4)。

之後,只能出一個,進一個; 如果都不出來,那也 hang 了。這個時候,至少要讓保安能進去,把這些太慢的同學趕出來幾個,讓等待的佇列動起來。
還有,有的同學在裡面發現忘帶證件了,需要等送進來。他們找地方等(
lock wait)。那麼他們是在等待了,這個是不算 oversubscribing 數量的,所以保安也可以放人,一直放到 thread_pool_max_threads 個人。

如果證件還沒送來,那麼銀行就被這些等證件的霸佔了(hang 了)。另外如果一下子證件都送來,那這個銀行一下子忙起來,也爆了(熱點更新)。
當然如果這個銀行沒有大量客戶同時辦業務的場景,是可以不需要搞個保安,不需要搞個隊伍的(
loose_thread_pool_enabled = OFF)。這個銀行本身最多可以 50 個人,但是保安只讓 10 個人進去,那效率就會低了。

還有,門口等待隊伍長了,這個可以有 3 種可能:

• 顧客動作慢(慢 SQL),建議考慮最佳化 SQL 降低執行成本。

• 銀行小, 視窗數量少(例項規格小)建議擴店(升級例項規格)。

• 視窗動作慢(物理機問題、資料庫 bug;不在本文討論範圍內)。

從上面的例子中,我們可以看到 Thread Pool 是透過佇列機制限制資料庫的 Client Thread 的併發度(控制 Running Thread 數量),避免大量的爭搶和建立 Client Thread 的開銷來提升 CPU 使用 效率,保障吞吐的(在應用給與 DB 的訪問壓力不斷增加的情況下,保持 DB 吞吐處理能力)。

1.2 適用場景

如果我們仔細品位下上面的例子,可以發現 Thread Pool 的適用場景:

• 每個要辦的業務簡短OLTP 場景)且效能瓶頸在 CPU 資源上

• 場景中不存在 大量 需要長時間執行且無停頓(可以暫時不使用 CPU)的 SQL

• 能夠接受一定損失(錯誤/開銷)的業務(啟用 Thread Pool 後需要一定開銷,存在簡單的查詢比不啟用 Thread Pool 的情況下執行時間增加的可能,比如被分配到了 stall thread group 而要花時間等待執行)

1.3 小結

讓我們小結一下。點選查大圖)

深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景

官方文件:Thread Pool:

https://help.aliyun.com/document_detail/130306.html

核心文件:  Thread Pool

那麼面對存在長時間執行的查詢,除了最佳化 SQL 降低執行成本外(有時不具有可操作性,當然如果該查詢對資料時效性不敏感可以考慮轉移到只讀例項上執行),是否還有其他招數可用?請看下一招“限流”。


02

限流


如果“海嘯”來的異常猛烈,並且在“海嘯”中能夠定義出一批帶有同樣特徵的查詢,比如 Redis 快取被擊穿,大量相似重複查詢打到 DB 層,或者如上例 Thread Pool 中的長時間執行的查詢,那麼在業務支援/允許降級的情況下我們可以透過對這批請求採取限流的方式來“保命”。

相對 thread pool 這種對“海嘯” 全方位覆蓋的應對機制,限流更像是集力量於一點的定向打擊。

2.1 Statement Concurrency Control

對於 RDS for MySQL 8.0 和 PolarDB for MySQL,我們可以透過“語句併發控制”(Statement Concurrency Control)特性來實現針對指定語句的限流。

比如發現下面的查詢在高併發的場景下拖累了整個例項的效能,和業務核實,業務可以接受該查詢被限流。

# 高成本慢查詢
select count(*)
from jacky.mytab
where cid = 90363
or uid = ???
確定 SQL 語句後,可以根據語句特徵來呼叫 dbms_ccl 工具包建立規則進行限流。

(*請左右滑動閱覽)

# 增加限流規則,限制最多 1 個併發執行
call dbms_ccl.add_ccl_rule('select','jacky','mytab',1,'cid=;uid=');
# 顯示當前的限流規則
call dbms_ccl.show_ccl_rule();
+------+--------+--------+-------+-------+-------+-------------------+---------+---------+----------+-----------+
| ID   | TYPE   | SCHEMA | TABLE | STATE | ORDER | CONCURRENCY_COUNT | MATCHED | RUNNING | WAITTING | KEYWORDS  |
+------+--------+--------+-------+-------+-------+-------------------+---------+---------+----------+-----------+
|    2 | SELECT | jacky  | mytab | Y     | N     |                 1 |     116 |       1 |       26 | cid=;uid= |
+------+--------+--------+-------+-------+-------+-------------------+---------+---------+----------+-----------+
限流規則新增後,超過定義的併發度的 SQL 請求在 "Concurrency control waiting" 狀態

深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景

限流前後對比,可以看到限流後 CPU 使用率從 100% 降低到 50% 左右,有效恢復業務可用性。

深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景

2.2 DAS 限流

對於 RDS for MySQL 5.6 和 5.7 ,控制檯的 CloudDBA 功能直接整合了 SQL 限流功能。

深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景

我們來看一個真實生活中的例子,某客戶在業務高峰期出現大量的集中請求,導致高配例項 CPU 完全打滿,由於例項響應極其緩慢,能採集到的監控資料顯示當時 活動會話達到 14700+

在業務層反覆調整無法恢復的情況下 在 2020.3.24 21:35 透過設定 SQL 限流恢復了業務可用性。

深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景

RDS 例項會話情況

深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景

RDS 例項 CPU 使用率情況

深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景

官方文件:SQL 限流:

https://help.aliyun.com/document_detail/131417.html


03

禦敵於外



上面介紹的都是資料庫層面的應對之策,那麼是否我們一定要被動的在資料庫層面“兵來將擋”呢?有沒有主動“禦敵於外”的辦法呢?

3.1 名詞解釋

深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景

3.2 短連線最佳化

首先我們來看看一個普通的 SQL 請求是如何被從應用透過網路傳送給 DB 層進而得到處理的。

深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景

仔細看一下上述時序圖,就會發現如果應用和資料庫之間在沒有可用的網路連線情況下,需要首先建立起一條基於 TCP/IP 協議棧的 MySQL 網路連線才能夠將 SQL 請求傳送給資料庫例項並獲取到處理的結果集。

在應用採用短連線機制(比如基於 PHP 語言開發的應用)的情況下,每個 SQL/Query 都需要和資料庫例項建立一個 TCP 網路連線,需要消耗資料庫例項(和其所在物理機)的 CPU 資源。

在“海嘯”的場景下,採用短連線機制的應用會保持很高的新連線建立速率(CPS,在出現 ListenOverFlow 和 ListenDrops 場景下大於等於 QPS),這樣在高負載(QPS) 的基礎上進一步消耗資料庫例項的 CPU 資源,拉高 CPU 使用率,降低 CPU 使用效率,進入惡性迴圈容易觸發資料庫雪崩式崩潰。

在 CPU 資源緊張的情況下會出現大量連線請求積壓無法處理而觸發 ListenOverFlow 和 ListenDrops 情況出現。

這裡我們看一個真實世界中的例子。

客戶在 13:30 將應用從長連線模式調整為短連線模式,由於短連線模式的高併發新建連線請求速率(CPS - 每秒新建連線數),修改後例項 CPU 使用率總體上升 25+% 左右,業務側出現大量連線失敗錯誤並感知 RDS 例項響應緩慢。

深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景
部分 CPU 被完全打滿,無法滿足處理高連線請求的需求而出現 ListenOverFlow / ListenDrops。

深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景
深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景
執行緒池 Thread Pool 是資料庫層對該場景較好的解決方案,而啟用了資料庫獨立代理(RDS for MySQL 讀寫分離地址 和 PolarDB for MySQL 的叢集地址)的例項還可以選擇啟用“短連線最佳化”的鏈路層解決方案。

深入淺出!阿里運維專家三種方法教你如何應對高併發“海嘯”場景

當應用斷開連線後,資料庫獨享代理會判斷之前的連線是否為空閒(idle)連線,如果是空閒連線,代理會將代理與資料庫之間的連線保留在連線池內一段時間(僅釋放應用與代理之間的連線)。

在保留連線的這段時間內如果應用發起新連線,代理會直接從連線池裡使用保留的連線,從而減少與資料庫建立連線的開銷。

官方文件:短連線最佳化

(https://help.aliyun.com/document_detail/146352.html)

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69940574/viewspace-2700084/,如需轉載,請註明出處,否則將追究法律責任。

相關文章