當您的數據庫遇到高并發挑戰或特定SQL語句資源消耗過高時,云數據庫RDS PostgreSQL為您提供了SQL限流功能。該功能可以有效防止特定SQL語句導致的資源過度消耗,確保您的數據庫系統穩定運行,更好地服務業務需求。
您可以加入RDS PostgreSQL插件交流釘釘群(103525002795),進行咨詢、交流和反饋,獲取更多關于插件的信息。
背景
SQL限流是一種重要的數據庫管理技術,可以通過限制并發SQL數,從而避免過高的數據庫負載,保證數據庫的穩定性和可靠性,提高數據庫的性能和效率,從而更好地支持業務需求。
應用場景
SQL限流的應用場景不僅限于高并發訪問,還包括以下幾個方面:
防止惡意攻擊
通過限制某類SQL的并發數,防止惡意攻擊或者DoS攻擊,保護數據庫的安全性。
控制資源使用
通過限制某類SQL的并發數,控制數據庫資源的使用量,避免資源耗盡導致數據庫崩潰或者性能下降。
例如在數據庫備份、恢復、監控等SQL操作時,為了避免其對數據庫性能的影響,可以使用SQL限流控制相關操作的速度。
前提條件
使用限制
無
影響
如果SQL限流配置不合理,可能影響業務,請在使用時根據業務負載進行配置。
例如,在某商城促銷業務場景下,由于訪問激增造成數據庫壓力較大,可使用此功能限制某類查詢SQL的并發數,從而減輕數據庫壓力。但如果限流設置過小,也可能影響相關業務。
注意事項
如果在實例重啟、主備切換等場景,SQL限流規則不會自動加載,需要手動重新加載。具體操作,請參見加載SQL限流規則。
費用
該插件免費。
創建和刪除插件
請使用高權限賬號執行如下命令。
創建插件
CREATE EXTENSION rds_ccl;
刪除插件
DROP EXTENSION rds_ccl;
使用示例
請使用高權限賬號執行如下命令。
創建SQL限流規則
使用場景
該函數只能在主實例中調用,用于創建一個SQL限流規則。
語法
語法一:根據設置的SQL語句進行限流。
SELECT rds_add_ccl_rule(
query_string varchar,
node_tag int,
max_concurrency int,
max_waiting int,
is_enabled boolean,
comment varchar, -- 可選
search_path varchar -- 可選
);
語法二:根據設置的query_id進行限流。
SELECT rds_add_ccl_rule_with_query_id(
query_id bigint,
node_tag int,
max_concurrency int,
max_waiting int,
is_enabled boolean,
query_string varchar, -- 可選
comment varchar -- 可選
);
參數解釋
參數 | 類型 | 說明 |
query_string | varchar | 需要限流的SQL語句。 配置時,使用 例如:
該函數根據query_string取值計算query_id,再根據query_id匹配同類SQL進行限流。query_id的更多信息,請參見query_id簡介。 |
query_id | bigint | 需要限流的SQL語句的query_id,更多信息,請參見query_id簡介。 |
node_tag | int | 限流的節點。
說明 創建SQL限流規則函數中,此參數僅表示規則可應用的范圍,不會自動在只讀實例中生效,您如果想要此規則在只讀實例中發揮SQL限流作用,需要手動加載,具體操作,請參見加載SQL限流規則。 |
max_concurrency | int | 限制此類SQL的最大并發數。 取值范圍:0~100000 |
max_waiting | int | 限制此類SQL的最大等待數,超過設置上限時,新的SQL會導致PostgreSQL內核觸發ABORT,終止并回滾事務。 取值范圍:0~100000 |
is_enabled | boolean | 該規則是否生效。
|
comment | varchar | 規則描述。 |
search_path | varchar | 目標限流SQL運行時的模式搜索路徑(search_path),如果配置為空字符串( |
注意事項
在語法一中,comment和search_path如果不配置,可以填寫為空字符串(
''
),如果配置為NULL
,則函數不會進行任何操作而直接返回。在語法二中:
query_string和comment如果不配置,可以填寫為空字符串(
''
),如果配置為NULL
,則函數不會進行任何操作而直接返回。如果同時配置了query_id和query_string,AliPG內核將以query_id為準進行SQL限流。
該函數會返回ccl_id,ccl_id是當前數據庫下限流規則的唯一標識。
SQL示例
在主實例的ccl_test數據庫中,有數據表ccl_tbl,創建SQL限流規則:
對所有的
SELECT * FROM ccl_tbl
語句(假設其query_id=1
)進行限流。最多允許3條此類SQL并發執行。
最多允許2個此類SQL等待。
規則創建成功后立即生效。
語法一:
SELECT rds_add_ccl_rule( $$SELECT * FROM ccl_tbl;$$, -- 待限流的SQL語句,使用$$包裹 1, -- 僅限制主實例 3, -- 最多允許3條此類SQL并發執行 2, -- 最多允許2個此類SQL等待 true, -- 規則立即生效 'limit constant select', -- 規則描述 '' -- 使用默認的search_path );
語法二:
SELECT rds_add_ccl_rule_with_query_id( 1, -- 待限流的SQL語句的query_id 1, -- 僅限制主實例 3, -- 最多允許3條此類SQL并發執行 2, -- 最多允許2個此類SQL等待 true, -- 規則立即生效 '' -- SQL語句的文本,本示例不填寫 'limit constant select', -- 規則描述 );
查詢SQL限流規則
使用場景
該命令可以在主實例或只讀實例中使用,用于查詢已創建的SQL限流規則。
語法
查看當前實例中所有庫下的SQL限流規則:
SELECT * FROM rds_enabled_ccl_rule;
說明此命令查詢結果中,由于顯示限制,query_string和comment的信息會被截斷,只顯示前200個字符,如果需要查看完整信息,請使用
SELECT * FROM rds_show_current_db_ccl_rule();
命令。查詢當前數據庫下的所有SQL限流規則:
SELECT * FROM rds_show_current_db_ccl_rule();
使SQL限流規則生效
使用場景
該函數只能在主實例中調用,適用于以下場景:
如果創建SQL限流規則時,is_enabled參數配置為false,則可以使用此函數使規則生效。
使失效的規則再次生效。
語法
SELECT rds_enable_ccl_rule(ccl_id int);
參數解釋
參數 | 類型 | 說明 |
ccl_id | int | SQL限流規則ID,如何查詢SQL限流規則ID,請參見查詢SQL限流規則。 |
注意事項
無
SQL示例
SELECT rds_enable_ccl_rule(1);
加載SQL限流規則
使用場景
該函數可以在主實例或只讀實例中調用,用于加載SQL限流規則,只有被加載的規則才會發揮SQL限流作用。
適用于以下場景:
主實例:
創建SQL限流規則時,如果is_enabled配置為true,則規則會自動加載到主實例,無需手動調用。
創建SQL限流規則時,如果is_enabled配置為false,調用
rds_enable_ccl_rule
函數使規則生效時,規則會自動加載到主實例,無需手動調用。如果主實例重啟,則需要手動調用此函數,加載規則。
只讀實例:
如果需要規則在只讀實例發揮作用,則需要在主實例創建規則(node_tag配置為2或3)后,再在只讀實例中調用此函數,手動加載規則。
語法
SELECT rds_load_ccl_rule(ccl_id int);
參數解釋
參數 | 類型 | 說明 |
ccl_id | int | SQL限流規則ID,如何查詢SQL限流規則ID,請參見查詢SQL限流規則。 |
注意事項
只有在主實例中調用
rds_add_ccl_rule
函數創建了SQL限流規則,且node_tag配置為2或3時,才能在只讀實例中加載規則。失效的規則無法被加載,如果只讀實例需要加載某條失效的規則,請先在主實例中使其生效,具體方法,請參見使SQL限流規則生效。
SQL示例
SELECT rds_enable_ccl_rule(1);
變更SQL限流規則
使用場景
該函數只能在主實例中調用,用于變更SQL限流規則中的最大并發數和最大等待數。
語法
SELECT rds_update_ccl_rule(
ccl_id int,
new_max_concurrency int,
new_max_waiting int
);
參數解釋
參數 | 類型 | 說明 |
ccl_id | int | SQL限流規則ID,如何查詢SQL限流規則ID,請參見查詢SQL限流規則。 |
new_max_concurrency | int | 變更后最大并發數。
|
new_max_waiting | int | 變更后最大等待數。
|
注意事項
更新SQL限流規則后,規則立即生效。
當前僅支持變更SQL限流規則中的最大并發數和最大等待數。
SQL示例
SELECT rds_update_ccl_rule(
2, -- ccl_id
4, -- 新的最大并發數為4
5 -- 新的最大等待數為5
);
使SQL限流規則失效
使用場景
該函數只能在主實例中調用,用于使某條SQL限流規則失效,該規則不再發揮SQL限流作用。
如果要使指定的規則生效,請參見使SQL限流規則生效。
語法
使指定SQL限流規則失效:
SELECT rds_disable_ccl_rule(ccl_id int);
使當前庫下的所有SQL限流規則失效:
SELECT rds_disable_all();
參數解釋
參數 | 類型 | 說明 |
ccl_id | int | SQL限流規則ID,如何查詢SQL限流規則ID,請參見查詢SQL限流規則。 |
注意事項
失效的規則無法被加載,如果只讀實例需要加載某條失效的規則,請先在主實例中使其生效,具體方法,請參見使SQL限流規則生效。
使SQL限流規則失效時,會自動將規則從主實例卸載,該規則不再發揮SQL限流作用。
SQL示例
SELECT rds_disable_ccl_rule(1);
卸載SQL限流規則
使用場景
該函數可以在主實例或只讀實例中調用,用于卸載SQL限流規則,被卸載的規則不會再發揮SQL限流作用。
適用于以下場景:
主實例:
如果在主實例中調用了
rds_disable_ccl_rule
或rds_disable_all
函數使規則失效,規則將自動從主實例卸載,無需手動調用。如果在主實例調用了
rds_del_ccl_rule
函數刪除規則,規則將自動從主實例卸載,無需手動調用。您也可以手動調用此函數,將規則從主實例卸載。
只讀實例:
如果不希望某條規則在只讀實例發揮限流作用,可以在只讀實例中手動調用此函數。
語法
SELECT rds_unload_ccl_rule(ccl_id int, db_name varchar default '');
參數解釋
參數 | 類型 | 說明 |
ccl_id | int | SQL限流規則ID,如何查詢SQL限流規則ID,請參見查詢SQL限流規則。 |
db_name | varchar | 默認為空字符串,表示當前數據庫。您也可以指定其他數據庫,將其他數據庫下的SQL限流規則卸載。 |
注意事項
規則卸載后,不再發揮SQL限流作用,如需再次使用,需要重新加載,更多信息,請參見加載SQL限流規則。
SQL示例
SELECT rds_unload_ccl_rule(1,'');
刪除SQL限流規則
使用場景
該函數只能在主實例中調用,用于刪除某條SQL限流規則。規則刪除時,自動從主實例卸載。
語法
SELECT rds_del_ccl_rule(ccl_id int);
參數解釋
參數 | 類型 | 說明 |
ccl_id | int | SQL限流規則ID,如何查詢SQL限流規則ID,請參見查詢SQL限流規則。 |
注意事項
無。
SQL示例
SELECT rds_del_ccl_rule(1);
返回結果:
rds_del_ccl_rule
----------------------
-7851264404688445170
(1 row)
返回結果為query_id。
如果規則不存在,則刪除報錯。
附錄
query_id簡介
query_id是PostgreSQL中SQL的特殊標識,同類SQL的query_id相同。
例如:
-- 以下兩個sql的query_id相同 SELECT * FROM tbl WHERE a = 1; SELECT * FROM tbl WHERE a = 2;
query_id包含有SQL中對象的信息(即oid信息),不同數據庫下的同名表不是同一個對象,不同schema下的同名表也不是同一個對象。所以相同的SQL,如果其包含的查詢對象不同,那么query_id就不相同。
如果是SQL中的訪問對象的是全局表,或是全局函數,query_id相同。
示例1:
pg_database
為全局表,在不同數據庫中查詢query_id相同。ccl_test數據庫中執行如下語句:
SELECT rds_get_query_id($$SELECT * FROM pg_database;$$);
返回結果:
rds_get_query_id ---------------------- -8733244708994363681 (1 row)
ccl_test2數據庫中執行如下語句:
SELECT rds_get_query_id($$SELECT * FROM pg_database;$$);
返回結果:
rds_get_query_id ---------------------- -8733244708994363681 (1 row)
示例2:
pg_sleep
為全局函數,在不同數據庫中查詢query_id相同。ccl_test數據庫中執行如下語句:
SELECT rds_get_query_id($$SELECT pg_sleep(1);$$);
返回結果:
rds_get_query_id -------------------- 440101247839410938 (1 row)
ccl_test2數據庫中執行如下語句:
SELECT rds_get_query_id($$SELECT pg_sleep(1);$$);
返回結果:
rds_get_query_id -------------------- 440101247839410938 (1 row)
調用函數場景下,當參數類型變化或是有無FROM子句時,query_id會發生變化。
例如,同樣調用
pg_sleep
函數,不同情況下返回query_id
結果不同。無FROM子句:
SELECT rds_get_query_id($$SELECT pg_sleep(1);$$);
返回結果:
rds_get_query_id -------------------- 440101247839410938 (1 row)
有FROM子句:
SELECT rds_get_query_id($$SELECT * FROM pg_sleep(1);$$);
返回結果:
rds_get_query_id ---------------------- -3404018605099167039 (1 row)
參數類型變化:
select rds_get_query_id($$SELECT * FROM pg_sleep(1.0);$$);
返回結果:
rds_get_query_id --------------------- 3073869725037049158 (1 row)