PolarDB PostgreSQL版提供了SQL限流功能。SQL限流功能以連接地址為維度配置限流規則,避免異常流量的SQL語句造成業務影響。本文主要為您介紹SQL限流功能的使用方式。
簡介
SQL限流功能允許以連接地址為維度配置限流規則,通過SQL模板的方式匹配當前連接地址上執行的SQL并限制其最大并發數或QPS。可被用于以下場景:
PolarDB集群上存在慢SQL導致數據庫負載較高,影響正常業務執行。
希望對某一類風險SQL限制其可使用的資源,或完全拒絕其執行。
操作步驟
如您需要開啟SQL限流功能,請聯系我們處理。
登錄PolarDB控制臺。
在左側導航欄單擊集群列表。
在左上角,選擇集群所在地域。
找到目標集群,單擊集群ID。
在左側導航欄單擊 。
在SQL限流頁簽,點擊新增,新建SQL限流規則。
在新建SQL限流規則彈窗內設置如下配置項后,單擊確定。
類別
參數
說明
基本信息
規則名稱
限流規則的名稱,需滿足如下要求:
不能超過30個字符。
必須由大小寫字母和數字組成。
規則描述
可選,用于備注該限流規則的相關信息,便于后續管理。不能超過64個字符。
EndpointID
選擇限流規則作用的連接地址。
說明當前僅支持在集群地址和按活躍請求數負載均衡的自定義地址(讀寫或只讀均可)上配置限流規則,主地址以及按連接數負載均衡的只讀地址暫時不支持SQL限流。
不同地址上配置的限流規則互不影響,同一地址上配置的限流規則僅對使用該地址的業務連接生效。
規則配置
規則類型
選擇限流規則模式,支持活躍并發數限流和連接級QPS限流。
說明按連接級QPS限流為限制單個連接的每秒請求次數,可用于業務側配置連接池或使用長連接的場景,短連接場景下建議使用活躍并發數限流。
當前模式
選擇SQL模板的匹配方式,支持模板匹配和全文匹配,兩種匹配方式的區別請參考模板匹配與全文匹配。
數據庫賬號名
配置限流規則作用的賬號,支持配置多個賬號,但不超過10個,賬號名稱之間使用英文逗號分隔。為空時,默認對所有賬號生效。
數據庫名
配置限流規則作用的數據庫。支持配置多個數據庫,但不超過10個,數據庫名稱之間使用英文逗號分隔。為空時,默認對所有數據庫生效。
SQL模板
配置SQL模板,詳細配置請參考SQL模板與匹配模式。
最長等待隊列長度
配置最長等待隊列長度,取值范圍為0-1024。當命中的SQL并發度或QPS達到規則限制時將會進入延遲隊列進行重試,當延遲隊列中等待的SQL數量超過最長等待隊列長度時新的請求將會直接返回錯誤。合理配置最長等待隊列長度可避免大量SQL被限流時延遲隊列無限制增長導致數據庫代理內存耗盡(OOM)。
最大活躍并發數
設置最大活躍并發數。
說明僅選擇活躍并發數限流規則類型時,需要配置該參數。
每連接最大QPS
設置每連接最大QPS。
說明僅選擇連接級QPS限流規則類型時,需要配置該參數。
實現方式
SQL限流為數據庫代理側實現的功能,通過在數據庫代理上配置各種限流規則以控制轉發的特定SQL的并發數或QPS,對數據庫集群的讀寫或只讀節點不會帶來額外影響,因此僅支持以集群或自定義地址為維度進行配置。
SQL模板與匹配模式
模板匹配與全文匹配
SQL模板可以是任何符合PolarDB PostgreSQL版集群標準語法的SQL,當配置SQL模板后根據配置的匹配模式不同,數據庫代理會對SQL模板進行不同的預處理。
假設配置了一條限流規則并配置了以下SQL模板:
SELECT * FROM tbl WHERE id < 1;
當選擇模板匹配時,會對SQL模板進行簡單正則化,去除多余空格、注釋,并掃描SQL中的常量部分(單引號字符串、數字等)替換為通配符,得到如下結果:
-- 模板化結果 SELECT * FROM tbl WHERE id < ?
當選擇全文匹配時,也會對SQL模板進行簡單正則化,但不會替換其中的常量部分,結果如下所示:
-- 僅格式化結果 SELECT * FROM tbl WHERE id < 1
之后數據庫代理會對生成的格式化SQL生成唯一標識用于后續匹配。
當限流規則啟用后,對于每條經過的業務SQL,數據庫代理也會進行和SQL模板類似的預處理。以以下業務SQL為例:
SELECT * FROM tbl WHERE id < 100;
同樣會生成兩種格式化的SQL并計算唯一標識,并嘗試與限流規則進行匹配:
-- 模板化結果 SELECT * FROM tbl WHERE id < ? -- 僅格式化結果 SELECT * FROM tbl WHERE id < 100
啟用SQL限流規則后在轉發當前SQL前,數據庫代理會遍歷當前配置的限流規則,當限流規則配置為模板匹配時會使用模板化結果與限流規則配置的SQL模板進行匹配,當限流規則配置為全文匹配時會使用僅格式化結果與限流規則配置的SQL模板匹配,匹配命中后會統計其并發度或QPS并執行對應的限流操作。
因此,對于上述的業務SQL和SQL模板,僅當配置為模板匹配時才會命中規則。
參數化支持
SQL模板中允許使用參數化寫法,與正常PostgreSQL參數綁定語法一致:
SELECT * FROM tbl WHERE id < $1 AND name = $2 LIMIT 1;
其中,參數化部分在模板匹配和全文匹配中均會被格式化為通配符:
-- 模板化結果
SELECT * FROM tbl WHERE id < ? AND name = ? limit ?
-- 僅格式化結果
SELECT * FROM tbl WHERE id < ? AND name = ? limit 1
因此對于以下業務SQL:
SELECT * FROM tbl WHERE id < $1 AND name = 2 LIMIT 100;
當前模式配置為模板匹配時可以命中上述規則,當前模式為全文匹配時無法命中上述規則。
暫不支持在SQL模板中直接使用?
寫法進行參數化:
-- 非法的SQL模板,不符合PostgreSQL標準語法,不會命中任何SQL
SELECT ?, ?, ?;
-- 合法的SQL模版
SELECT $1, $2, $3;
Prepared Statment
當業務使用Prepared statement時,Prepare語句本身不會觸發限流,只有Execute語句會觸發限流。對于Execute語句會對其對應的Prepare語句中的SQL部分進行對應的格式化或模板化并匹配規則。
關于Prepared statement的詳細內容請參考PREPARE。
示例
使用以下SQL模板配置一條限流規則, 匹配模式選擇模板匹配:
SELECT * FROM tbl WHERE id < $1 AND name > $2;
對于以下業務SQL:
-- prepare語句不會觸發限流
PREPARE s1 AS SELECT * FROM tbl WHERE id < $1 AND name > 100;
-- execute語句會用其對應的prepare語句中的sql部分匹配限流規則
EXECUTE s1;
EXECUTE s1;
EXECUTE s1;
三條Execute將會命中限流規則并被限流。
同樣地,當在限流規則的SQL模板中使用Prepare語句時,也僅會對Prepare語句中的SQL部分進行必要的格式化或模板化用于后續限流,因此創建規則時使用以下兩條SQL模板完全等價:
-- 模板1
PREPARE s1 AS SELECT * FROM tbl WHERE id < $1 AND name > $2;
-- 模板2
SELECT * FROM tbl WHERE id < $1 AND name > $2;
擴展協議支持
與Prepare語句類似,當業務驅動使用擴展協議時,僅有Execute報文會觸發限流。對每個Execute報文會找到其對應的Parse報文并計算SQL模板匹配限流規則。因此SQL限流可以支持擴展協議,配置時通常無需額外關注業務使用的協議。
關于擴展協議的詳細內容請參考社區文檔。
使用限制
目前SQL限流功能存在以下受限的使用場景:
多語句(Multi-Statement)暫不支持限流,使用多語句時將不會觸發任何配置的限流規則。
多語句指在一條SQL文本中包含多個使用分號間隔的SQL語句,以下是一個使用JDBC驅動執行的多語句示例:
Statement statement = connection.createStatement(); statement.execute("select 1; select 2; select 3");
多語句可能同時命中多條限流規則,為避免非預期的結果暫不支持限流。
部分特殊語句,如事務控制語句、存儲過程等不支持限流。對如Commit類的事務控制語句限流會導致事務無法正常結束,因此不會命中限流規則。
當客戶端或驅動使用批量執行(Statement Batching)模式時,批量執行的SQL僅會觸發第一條命中的限流規則。以下是一個使用JDBC驅動的批量執行的示例:
Statement statement = connection.createStatement(); statement.addBatch("select 1"); statement.addBatch("select 2"); statement.addBatch("select 3"); int[] result = statement.executeBatch(); statement.close(); connection.close();
與多語句類似,使用Statement Batching時驅動通常會將多條SQL的擴展協議報文組合并一次性發送,同樣會形成類似多語句的同時命中多條限流規則的情況。此時僅會啟用命中的第一條限流規則。對于上述的批量執行示例,當連接地址上同時配置以下三條SQL模板的限流規則時:
-- 模板1 SELECT 1; -- 模板2 SELECT 2; --模板3 SELECT 3;
僅有模板1會命中。
SQL模板暫時不支持對關鍵字忽略大小寫,配置SQL模板時需要與期望限流的SQL文本大小寫一致。
SQL模板暫不支持對in/any等不定長表達式進行忽略元素個數的模板化。例如:
-- SQL模版 SELECT * FROM tbl WHERE id IN ($1, $2, $3); -- SQL1,可以命中模版 SELECT * FROM tbl WHERE id IN (1, 6, 8); -- SQL2,無法命中模版 SELECT * FROM tbl WHERE id IN (1, 6, 8, 8);
當前不存在任何已配置的限流規則時,初次添加規則時無法對存量連接生效。在控制臺存在任何已配置的限流規則時(無論是否啟用),后續的新增、修改、刪除操作均可實時對所有連接生效。
說明當您的業務啟用長連接并期望任何時刻新增規則都可以立即生效時,建議在對應地址上配置一條任意的規則禁用后保留,后續增加或修改規則均可對新老連接生效。
限流方式
目前SQL限流使用SQL模板加延遲隊列的技術實現QPS或活躍并發數限流。業務SQL必須命中限流規則后才會統計對應規則上的QPS或并發數。當并發數或QPS超過規則配置的限制時,數據庫代理會將該SQL放入延遲隊列中延遲一段時間后重試,從而維持數據庫側的并發度/QPS不超過規則限制。
延遲隊列的延遲時間與規則配置的QPS/并發數成反比。同時命中某條規則的SQL在延遲隊列中等待的最大數量受規則中配置的最長等待隊列長度限制,超出限制時代理將不會轉發該SQL,同時向客戶端返回以下錯誤:
SELECT 123;
Current query is being throttled and waiting queue is full.
以上報錯不會中斷或改變當前連接的事務狀態,客戶端在收到該錯誤后依然可以選擇提交或回滾該事務。
同時,當配置的SQL限流規則中最大活躍并發數或每連接最大QPS為0時,任何命中該規則的SQL都會被拒絕轉發并直接向客戶端返回上述錯誤,可以使用該方式完全拒絕一類SQL的執行。
延遲隊列內部存在最小的重試時間間隔,當配置的最大QPS較大時,實際QPS會略小于設置的最大值。
配置任意SQL限流規則后,無論是否命中,代理都需要對每條業務SQL進行模板化、生成唯一標識并嘗試匹配規則等一系列操作,因此啟用SQL限流會帶來5%-10%的轉發性能下降,建議僅在數據庫側存在明顯慢SQL影響正常業務運行時進行限流。慢SQL消除后可以直接在控制臺禁用限流規則,已配置的規則禁用后不會生效并可以持久化保留,后續可隨時啟用。
最佳實踐
測試配置的限流規則是否生效
由于限流規則可以配置對哪些賬號和數據庫生效,通常可以新建一個賬號并對該賬號配置一條并發數或QPS為0的限流規則,以測試是否可以命中期望限流的SQL。
假設希望對以下SQL限流:
SELECT * FROM generate_series(1, 100000);
利用SQL限流處理生產環境慢SQL
準備測試環境。
ECS準備
創建一個Linux操作系統的ECS實例,本案例使用的ECS為CentOS 7.6 64位操作系統。詳情請參考自定義購買實例。
說明建議ECS實例和PolarDB集群在同一可用區和VPC。
在ECS實例中安裝pgbench工具。
sudo yum install postgresql-contrib
PolarDB集群準備
在PolarDB集群購買頁面,購買PolarDB集群,詳細請參考創建PolarDB PostgreSQL版數據庫集群。
創建數據庫賬戶,詳細操作請參考創建數據庫賬號。
獲取集群連接地址,詳細操作請參考查看或申請連接地址。如果PolarDB集群和ECS在同一可用區,可直接使用私網地址,否則需要申請公網地址。將ECS實例的地址添加到PolarDB集群白名單中,請參考設置集群白名單。
在控制臺創建測試數據庫,詳細步驟請參考創建數據庫。
為使后續配置的限流規則可以對存量連接生效,在控制臺配置一條任意規則并禁用,詳細步驟請參考操作步驟。
在ECS實例上使用pgbench工具連接PolarDB集群地址并初始化壓測數據。
pgbench -h <PolarDB集群地址> -p <PolarDB集群地址的端口> -i -s 10 -U <PolarDB數據庫用戶名> <測試數據庫名稱>
然后啟動壓測,使用pgbench內置的tpcb-like模式,模擬正常業務負載。
pgbench -h <PolarDB集群地址> -p <PolarDB集群地址的端口> -P 1 -b tpcb-like -j 5 -c 10 -M prepared -T 6000 -U <PolarDB數據庫用戶名> <測試數據庫名稱>
模擬慢SQL場景,新建一個連接會話,在測試數據庫中執行如下語句:
WITH t AS (SELECT md5(i::text) AS id FROM generate_series(1, 10000000) i) SELECT * FROM t ORDER BY id LIMIT 1;
該SQL會消耗大量計算資源,通常大約需要5秒返回如下結果:
id ---------------------------------- 0000023f507999464aa2b78875b7e5d6 (1 row)
再次啟動pgbench,使用自定義腳本壓測上述SQL,啟動10個連接,模擬慢SQL導致集群負載高的場景:
echo "WITH t AS (SELECT md5(i::text) AS id FROM generate_series(1, 10000000) i) SELECT * FROM t ORDER BY id LIMIT 1;" > slow.sql pgbench -h <PolarDB集群地址> -p <PolarDB集群地址的端口> -P 1 -f slow.sql -j 5 -c 10 -M prepared -T 6000 -U <PolarDB數據庫用戶名> <測試數據庫名稱>
開始壓測后,原本的正常業務負載立刻大幅下跌:
在控制臺使用以下SQL模板配置一條限流規則,規則類型選擇活躍并發數限流:
WITH t AS (SELECT md5(i::text) AS id FROM generate_series($1, $2) i) SELECT * FROM t ORDER BY id LIMIT $3;
將慢SQL限制并發數為1,啟用規則后即可看到業務負載回升,限流規則已生效。
在控制臺修改限流規則,單擊目標限流規則操作列的修改將活躍最大并發數進一步下調為0,完全拒絕慢SQL執行:
配置后即可看到慢SQL壓測端返回錯誤并中斷,至此業務負載完全恢復。