SQL限流(rds_ccl)

当您的数据库遇到高并发挑战或特定SQL语句资源消耗过高时,云数据库RDS PostgreSQL为您提供了SQL限流功能。该功能可以有效防止特定SQL语句导致的资源过度消耗,确保您的数据库系统稳定运行,更好地服务业务需求。

您可以加入RDS PostgreSQL插件交流钉钉群(103525002795),进行咨询、交流和反馈,获取更多关于插件的信息。

背景

SQL限流是一种重要的数据库管理技术,可以通过限制并发SQL数,从而避免过高的数据库负载,保证数据库的稳定性和可靠性,提高数据库的性能和效率,从而更好地支持业务需求。

应用场景

SQL限流的应用场景不仅限于高并发访问,还包括以下几个方面:

  • 防止恶意攻击

    通过限制某类SQL的并发数,防止恶意攻击或者DoS攻击,保护数据库的安全性。

  • 控制资源使用

    通过限制某类SQL的并发数,控制数据库资源的使用量,避免资源耗尽导致数据库崩溃或者性能下降。

    例如在数据库备份、恢复、监控等SQL操作时,为了避免其对数据库性能的影响,可以使用SQL限流控制相关操作的速度。

前提条件

  • RDS PostgreSQL实例需满足以下要求:

    • 实例大版本为PostgreSQL 14或以上。

    • 实例内核小版本为20230330或以上。

    说明

    如需升级内核小版本,请参见升级内核小版本

  • 使用此插件前,需配置如下参数:

    • rds_enable_ccl取值为on

    • compute_query_id取值为autoon

    说明

    您可以前往控制台修改实例参数,更多信息,请参见设置实例参数

使用限制

影响

如果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语句。

配置时,使用$$$<任意字符>$包裹限流SQL语句,更多信息,请参见Dollar-Quoted String Constants

例如:

-- 使用$$包裹SQL语句
$$SELECT * FROM my_table;$$

-- SQL语句中已存在$$,使用$ccl$包裹SQL语句
$ccl$SELECT * FROM my_table WHERE my_column = $$hello$$;$ccl$

该函数根据query_string取值计算query_id,再根据query_id匹配同类SQL进行限流。query_id的更多信息,请参见query_id简介

query_id

bigint

需要限流的SQL语句的query_id,更多信息,请参见query_id简介

node_tag

int

限流的节点。

  • 1:仅限制主实例。

  • 2:仅限制只读实例。

  • 3:限制主实例和只读实例。

说明

创建SQL限流规则函数中,此参数仅表示规则可应用的范围,不会自动在只读实例中生效,您如果想要此规则在只读实例中发挥SQL限流作用,需要手动加载,具体操作,请参见加载SQL限流规则

max_concurrency

int

限制此类SQL的最大并发数。

取值范围:0~100000

max_waiting

int

限制此类SQL的最大等待数,超过设置上限时,新的SQL会导致PostgreSQL内核触发ABORT,终止并回滚事务。

取值范围:0~100000

is_enabled

boolean

该规则是否生效。

  • true:立即生效

  • false:不生效

comment

varchar

规则描述。

search_path

varchar

目标限流SQL运行时的模式搜索路径(search_path),如果配置为空字符串(''),将使用默认search_path,您可以使用SHOW search_path;命令查询。

注意事项

  • 在语法一中,commentsearch_path如果不配置,可以填写为空字符串(''),如果配置为NULL,则函数不会进行任何操作而直接返回。

  • 在语法二中:

    • query_stringcomment如果不配置,可以填写为空字符串(''),如果配置为NULL,则函数不会进行任何操作而直接返回。

    • 如果同时配置了query_idquery_string,AliPG内核将以query_id为准进行SQL限流。

  • 该函数会返回ccl_idccl_id是当前数据库下限流规则的唯一标识。

SQL示例

在主实例的ccl_test数据库中,有数据表ccl_tbl,创建SQL限流规则:

  1. 对所有的SELECT * FROM ccl_tbl语句(假设其query_id=1)进行限流。

  2. 最多允许3条此类SQL并发执行。

  3. 最多允许2个此类SQL等待。

  4. 规则创建成功后立即生效。

  • 语法一:

    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_stringcomment的信息会被截断,只显示前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配置为23)后,再在只读实例中调用此函数,手动加载规则。

语法

SELECT rds_load_ccl_rule(ccl_id int);

参数解释

参数

类型

说明

ccl_id

int

SQL限流规则ID,如何查询SQL限流规则ID,请参见查询SQL限流规则

注意事项

  • 只有在主实例中调用rds_add_ccl_rule函数创建了SQL限流规则,且node_tag配置为23时,才能在只读实例中加载规则。

  • 失效的规则无法被加载,如果只读实例需要加载某条失效的规则,请先在主实例中使其生效,具体方法,请参见使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

变更后最大并发数。

  • 如果增加最大并发数,则等待中的SQL将立即执行。

  • 如果减少最大并发数,不会影响正在执行的SQL,直到当前并发数小于新的最大并发数后,等待中的SQL才会开始执行。

new_max_waiting

int

变更后最大等待数。

  • 如果增加最大等待数,则允许更多的限流SQL进入等待。

  • 如果减少最大等待数,不会影响正在等待中的SQL,但如果此时有新的SQL需要等待,且会使当前等待SQL数大于新的最大等待数时,将会导致PostgreSQL内核触发ABORT,终止并回滚事务。

注意事项

  • 更新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_rulerds_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)