1. 背景
SQL 限流顾名思义,是一种对 SQL 的查询速度进行限制的能力。
一般情况下,我们希望 SQL 查询语句在数据库上的执行速度越快越好。然而当数据库的资源有限,例如在 CPU、IO、内存等某一项资源达到上限时,查询在并发执行时会有激烈的资源争抢,此时有的查询会因为资源不足而出现超时,从而影响用户业务。
为应对突发的数据库请求流量、资源消耗过高的语句访问以及 SQL 访问模型的变化等问题,数据库需要对外提供一个限流方法。
OceanBase 之前支持通过创建 outline 绑定 /+max_concurrent(N)/ 的方法来对指定 SQL 进行限流(限制这个 SQL 文本的并发数),但不支持关键字限流。
CREATE outline ol_1 ON
SELECT /*+max_concurrent(1)*/ *
FROM t2
WHERE c1 = 1 AND c2 = ?;
有一些用户在使用过程中,会表示 “因为业务方有时候简单粗暴,会出现大量自动重试操作,基于用户的某些标准,希望 OceanBase 在未来具备全局自适应限流能力,自动对每种类型的 SQL 限制并发度,防止重试 SQL 瞬间激增,导致数据库 Load 激增。这样一来,当我们设置全局 SQL 执行的并发度在 10 或 20 的时候,无论历史 SQL 还是新 SQL 都会自动统一,无需运维手动操作,当出现故障时即可快速止损。目前 OCP 具备单独对 SQL 限流的能力,但不能全局限流。”
另一方面,OceanBase 当前创建完 outline 限流规则后,若想限流规则生效,仍需进入 SQL 引擎。使用 outline 的限流逻辑必须要先通过 Parse、Resolve 和 Optimize 阶段生成出执行计划,且在将计划添加进 PlanCache 后才会检查是否触发限流上限。对于一些在 Parse、Resolve 阶段就卡死的 SQL 来说,此类限流方法将失效。OceanBase 需要一种新的限流方式预期能够在 SQL 请求进入 SQL 引擎之前就将其拦住,避免上述场景发生问题。
当前通过 outline 限流逻辑如下:先尝试获取 PlanCache,如果获取到,则直接检查该 SQL 是否超过并发数上限。否则,就会进入完整的 SQL 解析流程,并在向 PlanCache 加入新计划后再进行并发数上限的检查和判断(这个流程看不懂没关系,直接忽略影响也不大)。
因为有用户的业务对 OceanBase 实现全局自适应限流提出了新需求,以及 outline 限流方式存在的一些问题,加之易用性方面的一些考虑,OceanBase 支持了通过关键字对 SQL 进行限流的功能。
2. 边学边练,效果拔群
正所谓 “纸上得来终觉浅,实践才能出真知”,强烈推荐大家点击下面的链接,根据在线体验页面左边的实验文档,亲手体验一把 SQL 关键字限流的易用性。
-
在线实验地址:《SQL 关键字限流》。
- 在实验环境中的 “测试限流效果” 部分,设计实验的同学写了一句 “当触发限流规则时,会返回以下错误信息 xxx”,但实验手册中并没有直接给大家构造出这个报错场景。大家可以想想如何快速构造一个被 MAX_CONCURRENCY 限制住的场景?并尝试下在实验环境中,把这个触发限流规则之后的报错给构造出来~
小提示 1:
除了直接把 MAX_CONCURRENCY 设置成 0 以外,在实验环境中,还可以想办法同时跑多条都命中关键字的 SQL,例如:
- 第一条给它用 sleep 关键字 hang 上一段儿时间,然后让它在后台跑。
- 在第一条 SQL 睡觉的这段儿时间内,执行第二条 SQL,预期就可以看到超出并发数限制后的报错了。
obclient -h127.0.0.1 -P2881 -uroot@mysql_tenant -A -Dtest
set global ob_query_timeout = 100000000000000;
CREATE TABLE product_reviews (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
product_id BIGINT,
user_id BIGINT,
rating INT,
comment TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO product_reviews (product_id, user_id, rating, comment) VALUES
(1, 1001, 5, 'Excellent smartphone'),
(1, 1002, 4, 'Good value for money'),
(2, 1003, 5, 'Powerful performance');
drop CONCURRENT_LIMITING_RULE product_reviews_table_limit;
CREATE CONCURRENT_LIMITING_RULE IF NOT EXISTS `product_reviews_table_limit`
ON test.product_reviews
TO 'root'@'%'
FOR SELECT
WITH MAX_CONCURRENCY = 1;
quit
obclient -h127.0.0.1 -P2881 -uroot@mysql_tenant -A -Dtest -e "SELECT COUNT(*) FROM product_reviews WHERE product_id > sleep(100);" &
obclient -h127.0.0.1 -P2881 -uroot@mysql_tenant -A -Dtest
SELECT COUNT(*) FROM product_reviews WHERE product_id = 123;
小提示 2:
- 需要先登录 OceanBase 账号,才能初始化屏幕右边的实验环境进行实验。
- 在实验环境里,干什么都可以。大家不要受限于屏幕左边的实验手册,可以天马行空地做一些你感兴趣的事情,或者验证一些你对 OceanBase 官网文档的疑问、以及自己的猜想等等(甚至可以尝试怎么搞能把这个实验环境里的 OBServer 给弄崩)。
- 欢迎大家平时在学习 OceanBase 的过程中,也都能充分利用在线体验页面为您提供的一些实验环境,来体验 OceanBase 中您感兴趣的新特性。
-
课后小测地址:【DBA 实战营】OceanBase SQL 关键字限流。
- 大家完成课后小测,并在小测中上传实验截图,判卷通过后就会自动获取 10 积分,并自动获得抽奖资格。有机会获得实体礼物或高额积分奖励。
- 大家完成课后小测,并在小测中上传实验截图,判卷通过后就会自动获取 10 积分,并自动获得抽奖资格。有机会获得实体礼物或高额积分奖励。
大家如果都不来做实验的话,老板以后就不会再让兹拉坦更新这些教程文档了。大家的对在线实验的支持,是兹拉坦的老板让兹拉坦更新这些教程内容的动力!
闲言少叙,正文开始。
3. OceanBase 关键字限流特性介绍
3.1. 语法支持
创建 SQL 限流规则:
-- 限流规则名称
CREATE CONCURRENT_LIMITING_RULE [ IF NOT EXISTS ] `ccl_rule_name`
ON `database`.`table` -- 限流生效的数据库和表,全选为*.*
TO '<usename>'@'ip' -- 限流生效的用户名和机器 ip,
-- 例如: test@%,指test用户下的所有机器
FOR { ALL | UPDATE | SELECT | INSERT | DELETE } -- 限流作用到的 DML 类型
filter_options:
[ FILTER BY KEYWORD('KEYWORD1', 'KEYWORD2',…) ] -- 限流的关键字
with_options:
WITH MAX_CONCURRENCY = value1 [per sql] -- 限流的并发度
-- 并且可以指定限流统计粒度是规则级别的,还是 Format SQLID 语句级别的
当 SQL 请求数达到限流规则上限后,会抛出相关错误码到客户端。
删除 SQL 限流规则:
DROP CONCURRENT_LIMITING_RULE [ IF EXISTS ]
`ccl_rule_name1` [, `ccl_rule_name2`, ...]
限流规则创建之后将立即生效,而被删除的限流规则会立即失效。
3.2. 权限管理
社区版的 MySQL 模式的租户,需要 user_level 的 create 权限。
grant create on *.* to user1;
grant drop on *.* to user1;
3.3. 细节说明
-
限流规则名称不能超过 128 字节。
-
系统中限流规则个数目前无限制。
-
database name 和 table name 在 MySQL 模式下会受
lower_case_table_names
变量的影响。 -
限流关键字:
-
关键字之间将用通配符
%
连接,例如给定3个关键词'asdasd', 'c2 = 123', 'xxyyzz'
,其会被拼成'%asdasd%c2 = 123%xxyyzz%'
,后续将按照这个字符串进行 SQL 文本的通配匹配。- 当关键字中有’_'时会被转义,如
'table_t1'
会被记录成'%table\_t1%'
- 当关键字中有’_'时会被转义,如
-
限流关键字无个数限制,但限制拼凑后的字符串长度不能超过
OB_MAX_VARCHAR_LENGTH=1048576
。
-
-
关键字字符串的字符集和字符序为
binary
。 -
其中
MAX_CONCURRENCY = 10
指单机并发上限,即其为机器级而非集群级的。比如假设集群有 3 台机器,那么指的是这三台机器各自的限流并发上限为 10。 -
per sql
为可选的选项。表示限流规则的统计粒度,假设有限流规则ccl_1
:
CREATE CONCURRENT_LIMITING_RULE IF NOT EXISTS `ccl_1`
ON *.*
TO user1@`ip`
FOR SELECT
FILTER BY KEYWORD('c1 = 1');
WITH MAX_CONCURRENCY 10 [per sql]
有如下 SQL 请求:
sql-1: select * from t1 where c1 = 1;
sql-2: select * from t1 where c1 = 1 and c2 = 2;
-
如果没有
per sql
,表示统计粒度为规则级别,那么 sql-1 和 sql-2 将共同被ccl_1
限制执行的并发上限,同一时间全部 SQL 数量总计不能超过 10。 -
如果有
per sql
,表示统计粒度为 Format SQLID 语句级别,那么 sql-1 和 sql-2 将各自被ccl_1
限制执行并发上限,同一时间每个 SQL 数量总计不能超过 10。说明:
这里多说两句什么是 format_sql_id?这个是创建 outline 时用到的一种东西。可以通过两种方式创建模糊 outline,一种是通过 format SQL_TEXT(用户执行的带参数的原始语句),另一种是通过 format_sql_id 创建。
/* 使用 SQL_TEXT 创建 Outline */
CREATE [OR REPLACE] FORMAT OUTLINE outline_name
ON sql_text [ TO target_stmt ]
/* 使用 SQL_ID 创建 Outline */
CREATE [OR REPLACE] FORMAT OUTLINE outline_name
ON format_sql_id USING HINT hint;
需要用 outline 绑定的 SQL 时,对应的经过一些规则的改写得到的 format_stmt,然后系统会根据 format_stmt 计算 md5 值得到的 format_sql_id。format_sql_id 可通过 GV$OB_SQL_AUDIT 中获取。
3.4. 作用范围
对于 multi statement 不生效。
说明:
multi statement 指将多条 SQL 语句合并为一个请求发送,例如:
SELECT * FROM users; SELECT COUNT(*) FROM orders;
数据库会依次执行这两条语句,并返回每个语句的结果。
3.5. 命中多条限流规则时的行为
一条 SQL 请求可能命中多条限流规则,此时每条限流规则都生效,属于 and 逻辑。
例如:
先加表级 select 单条并发限制 3,后加库级 select 全局并发限制 10000。
create ccl_rule r1 on db1.tbA to user1 for select with max_concurrency = 3 per sql;
create ccl_rule r2 on db1.* to user1 for select with max_concurrency = 10000;
user1 对 db1.tbA 的每条查询语句,并发数都不能超过3;并且不能超过整体 1 万的并发限制,触达任意一个规则时都会报错
3.6. 与 outline 限流的正交
当前 OceanBase 已有的 outline 限流与本期课程描述的关键字限流规则,属于并列关系。会按照 3.4 的规则与关键字限流规则一起生效。
3.7. 用法举例
CREATE CONCURRENT_LIMITING_RULE IF NOT EXISTS `global_emergency_ccl_rule`
ON *.*
TO test@%
FOR all
FILTER BY KEYWORD('Zlatan', 'Ibrahimović')
WITH MAX_CONCURRENCY = 0;
CREATE CONCURRENT_LIMITING_RULE IF NOT EXISTS `sql1_keywords_ccl_rule`
ON database1.table1
TO user1@`ip`
FOR SELECT
FILTER BY KEYWORD(‘table1’, ’table2’, ’c1’);
WITH MAX_CONCURRENCY 100;
3.8. 新增字典视图
- 可以通过字典视图
DBA_OB_CCL_RULES
来查询全部的限流规则。 - 可以通过性能视图
GV$OB_SQL_CCL_STATUS
或V$OB_SQL_CCL_STATUS
来访问当前被限流的 SQL 与命中的限流规则,以及其这些规则剩余可用并发度,用于诊断。
4. 实现逻辑
这部分内容可能会稍微深入一点儿,怕大家不爱看,就偷懒直接 “一图胜前言” 了。
了解一些背景的同学自然能看懂,不了解背景的同学直接跳过即可,对于使用这个功能来说,并没有啥影响。
最后的最后,再对实现逻辑多啰嗦两句:
第一句:简单来说,实现逻辑就是尽量在 SQL 引擎的更靠前的位置进行限流规则的匹配检查,最大程度阻止 SQL 进入后续的执行逻辑。
第二句:限流规则检查时将遍历所有的限流规则,对所有满足的规则记录并发度 + 1,当满足任意一个 限流规则上限时,报错退出。
5. 参考
- CREATE CONCURRENT_LIMITING_RULE
- DROP CONCURRENT_LIMITING_RULE
- DBA_OB_CCL_RULES
- GV$OB_SQL_CCL_STATUS / V$OB_SQL_CCL_STATUS