OceanBase DBA实战营2期--SQL 关键字限流学习笔记

1.SQL 关键字限流简介

SQL 限流是一种控制数据库并发访问 的能力,当数据库面临大量并发请求时,可以通过限制某些 SQL 语句的执行数量来保护数据库资源。OceanBase 数据库支持关键字的 SQL 限流,能够在 SQL 语句进入数据库引擎之前就将其拦截 ,防止查询在并发时发生资源争抢。

简单来说,实现逻辑就是尽量在 SQL 引擎的更靠前的位置进行限流规则的匹配检查 ,最大程度阻止 SQL 进入后续 的执行逻辑。限流规则检查时将遍历所有的限流规则 ,对所有满足的规则记录并发度+1当满足任意一个限流规则上限时,报错退出。

2.OceanBase 关键字限流特性介绍

语法支持

创建 SQL 限流规则

当 SQL 请求数达到限流规则上限后,会抛出相关错误码到客户端。限流规则创建之后将立即生效

删除 SQL 限流规则

被删除的限流规则会立即失效。

权限管理

社区版的 MySQL 模式的租户,需要 user level 的 create 权限。

细节说明

限流规则名称不能超过 128 字节

系统中限流规则个数目前无限制

database name 和 tablename在MySQL模式下会受 lower_case table_names 变量的影响。

限流关键字:

  • 关键字之间将用通配符%连接,例如给定3个关键词’asdasd’,‘c2= 123’xxyyzz’,其会被拼成’asdasdc2=123xxyyzz%’,后续将按照这个字符串进行SQL 文本的通配匹配。
  • 当关键字中有’时会被转义,如’table t1’会被记录成’&table\ t1%'限流关键字无个数限制,但限制拼凑后的字符串长度不能超过OB_MAX_VARCHAR_LENGTH=1048576 。

关键字字符串的字符集和字符序为 binary。

其中 MAX_CONCURRENCY=10指单机并发上限,即其为机器级而非集群级的。比如假设集群有3 台机器,那么指的是这三台机器各自的限流并发上限为 10。

per sql为可选的选项。表示限流规则的统计粒度,假设有限流规则ccl_1:

有如下请求:

  • 如果没有 per sql,表示统计粒度为规则级别,那么sql-1和sq1-2将共同被 cc_1 限制执行的并发上限,同一时间全部 SQL 数量总计不能超过 10。
  • 如果有 per sql,表示统计粒度为 Format SQLID 语句级别,那么 sq-1和sql-2 将各自被cC1 1 限制执行并发上限,同一时间每个 SQL 数量总计不能超过 10。

说明:

format_sal_id是创建 outline 时用到的,可以通过两种方式创建模糊 outline,一种是通过 format SQL TEXT(用户执行的带参数的原始语句),另一种是通过fommat_sql_id 创建。

需要用 outline 绑定的 SQL 时,对应的经过一些规则的改写得到的 format_stmt,然后系统会根据format_stmt 计算 md5 值得到的 format sal_id。format_sqli_d 可通过 GVSOB SQL_AUDIT 中获取。

作用范围

对于 multi statement 不生效。

说明:

multi statement 指将多条 SQL 语句合并为一个请求发送 ,例如:SELECT * FROM users; SELECT COUNT(*) FROM orders;数据库会依次执行这两条语句,并返回每个语句的结果

命中多条限流规则时的行为

一条 SQL 请求可能命中多条限流规则,此时每条限流规则都生效,属于 and 逻辑。

例如:

先加表级 select 单条并发限制 3,后加库级 select 全局并发限制 10000。

user1 对 db1.tbA 的每条查询语句,并发数都不能超过3;并且不能超过整体1万的并发限制,触达任意一个规则时都会报错

与 outline 限流的正交

当前 OceanBase 已有的 outline 限流与本期课程描述的关键字限流规则,属于并列关系。会按照 3.4 的规则与关键字限流规则一起生效。

用法举例

新增字典视图

可以通过字典视图DB_OB_CCL_RULES 来查询全部的限流规则。

。可以通过性能视图 GV$OB_SQL_CCL_STATUS或V$OB_SQL_CCL_STATUS 来访问当前被限流的 SQL 与命中的限流规则,以及其这些规则剩余可用并发度,用于诊断。

3.实验过程

按照以下步骤,在 OceanBase 数据库 MySQL 模式下创建 SQL 限流规则:

-- 创建测试表
CREATE TABLE test_table(id INT, name VARCHAR(50));

-- 创建限流规则(示例:限制对 test_table 的查询并发数)
CREATE CONCURRENT_LIMITING_RULE IF NOT EXISTS test_limit_rule
ON test.test_table
TO 'root'@'%'
FOR SELECT
FILTER BY KEYWORD('test_table')
WITH MAX_CONCURRENCY = 0;

-- 查看限流规则
SELECT * FROM oceanbase.DBA_OB_CCL_RULES;

-- 测试限流效果
SELECT COUNT(*) FROM test_table WHERE id = 123;

-- 当触发限流规则时,会返回以下错误信息:
ERROR 11087 (HY000): SQL reach max ccl rule test_limit_rule, concurrent num 0

-- 删除限流规则
DROP CONCURRENT_LIMITING_RULE IF EXISTS test_limit_rule;

实验过程如下:

连接数据库

在线环境中 root 用户未设置密码,仅供体验使用,在实际环境中,请根据需要配置相关用户密码。

在开始之前,请确保已连接到 OceanBase 数据库。如果尚未连接,可以使用以下命令:

[admin@xxx ~]obclient -h127.0.0.1 -P2881 -uroot@mysql_tenant -A -Dtest

创建数据表结构

电商平台在双十一等大促期间,数据库会面临巨大的访问压力。某些不重要的查询(如商品详情页的辅助信息查询)可能会消耗大量资源,影响核心交易流程。通过 SQL 关键字限流,可以在事前对这些查询进行限制,确保核心业务不受影响。

假设我们有一个电商数据库,包含以下核心表:

  • orders :订单表(核心业务)
  • products :商品表(核心业务)
  • product_reviews :商品评论表(非核心业务)
  • user_activities :用户行为表(非核心业务)

在大促期间,我们需要:

  1. 保护核心订单和商品查询
  2. 限制非核心业务的查询并发
  3. 设置应急限流机制

创建表结构来存储业务数据:

-- 创建订单表
CREATE TABLE orders (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT,
    product_id BIGINT,
    quantity INT,
    total_amount DECIMAL(10,2),
    order_status ENUM('pending', 'paid', 'shipped', 'delivered'),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 创建商品表
CREATE TABLE products (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    product_name VARCHAR(255),
    price DECIMAL(10,2),
    stock INT,
    category VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 创建商品评论表(非核心业务)
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
);

-- 创建用户行为表(非核心业务)
CREATE TABLE user_activities (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT,
    activity_type VARCHAR(50),
    activity_data JSON,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 插入测试数据
INSERT INTO products (product_name, price, stock, category) VALUES
('iPhone 15', 5999.00, 100, 'Smartphone'),
('MacBook Pro', 12999.00, 50, 'Computer'),
('AirPods Pro', 1999.00, 200, 'Accessory'),
('iPad Air', 3999.00, 80, 'Tablet');

INSERT INTO orders (user_id, product_id, quantity, total_amount, order_status) VALUES
(1001, 1, 1, 5999.00, 'paid'),
(1002, 2, 1, 12999.00, 'pending'),
(1003, 3, 2, 3998.00, 'shipped');

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');

INSERT INTO user_activities (user_id, activity_type, activity_data) VALUES
(1001, 'view_product', '{"product_id": 1, "duration": 30}'),
(1002, 'search', '{"keyword": "iPhone", "results": 5}'),
(1003, 'add_to_cart', '{"product_id": 3, "quantity": 2}');

配置限流策略

-- 创建非核心业务限流规则
-- 限制商品评论查询的并发数
CREATE CONCURRENT_LIMITING_RULE IF NOT EXISTS review_query_limit
ON test.product_reviews
TO 'root'@'%'
FOR SELECT
FILTER BY KEYWORD('product_reviews')
WITH MAX_CONCURRENCY = 5;

-- 限制用户行为查询的并发数
CREATE CONCURRENT_LIMITING_RULE IF NOT EXISTS activity_query_limit
ON test.user_activities
TO 'root'@'%'
FOR SELECT
FILTER BY KEYWORD('user_activities')
WITH MAX_CONCURRENCY = 3;

-- 创建全局应急限流规则(限制所有包含特定关键字的查询)
CREATE CONCURRENT_LIMITING_RULE IF NOT EXISTS emergency_limit
ON *.*
TO 'root'@'%'
FOR ALL
FILTER BY KEYWORD('product_reviews', 'user_activities')
WITH MAX_CONCURRENCY = 1;

测试限流效果

核心业务查询测试限流效果

-- 核心业务查询:商品信息查询(应该成功)
SELECT * FROM products WHERE id = 1;

-- 核心业务查询:订单信息查询(应该成功)
SELECT * FROM orders WHERE user_id = 1001;

分别输出如下:

obclient [test]> SELECT * FROM products WHERE id = 1;
+----+--------------+---------+-------+------------+---------------------+
| id | product_name | price   | stock | category   | created_at          |
+----+--------------+---------+-------+------------+---------------------+
|  1 | iPhone 15    | 5999.00 |   100 | Smartphone | 2025-08-20 15:26:47 |
+----+--------------+---------+-------+------------+---------------------+
1 row in set (0.059 sec)

obclient [test]> SELECT * FROM orders WHERE user_id = 1001;
+----+---------+------------+----------+--------------+--------------+---------------------+
| id | user_id | product_id | quantity | total_amount | order_status | created_at          |
+----+---------+------------+----------+--------------+--------------+---------------------+
|  1 |    1001 |          1 |        1 |      5999.00 | paid         | 2025-08-20 15:26:47 |
+----+---------+------------+----------+--------------+--------------+---------------------+
1 row in set (0.008 sec)

非核心业务查询测试限流效果

在线实验环境中倒是没遇到被限流

-- 非核心业务查询:商品评论统计(可能被限流)
SELECT COUNT(*) FROM product_reviews WHERE product_id = 1;

-- 当触发限流规则时,会返回以下错误信息:
ERROR 11087 (HY000): SQL reach max ccl rule review_query_limit, concurrent num 5

-- 非核心业务查询:用户行为统计(可能被限流)
SELECT COUNT(*) FROM user_activities WHERE user_id = 1001;

-- 当触发限流规则时,会返回以下错误信息:
ERROR 11087 (HY000): SQL reach max ccl rule activity_query_limit, concurrent num 3

分析限流信息

-- 查看所有限流规则
SELECT * FROM oceanbase.DBA_OB_CCL_RULES;

-- 查看限流统计信息
-- 记录当前被限流的 SQL 与命中的限流规则,以及这些规则剩余可用并发度
SELECT * FROM oceanbase.V$OB_SQL_CCL_STATUS;

-- 查看被限流的 SQL 语句
SELECT * FROM oceanbase.V$OB_SQL_AUDIT WHERE CCL_RULE_ID != 0;

-- 可能的返回结果如下:

+------------+------------------+------------------+------------------+------------------+------------------+
| SQL_ID     | CCL_RULE_ID     | CCL_RULE_NAME    | SQL_TEXT        | EXEC_TIME        | STATUS           |
+------------+------------------+------------------+------------------+------------------+------------------+
| 1234567890 | 1               | review_query_limit| SELECT COUNT(*) FROM product_reviews WHERE product_id = 1 | 2024-01-01 10:05:00 | THROTTLED        |
| 1234567891 | 2               | activity_query_limit| SELECT COUNT(*) FROM user_activities WHERE user_id = 1001 | 2024-01-01 10:05:30 | THROTTLED        |
+------------+------------------+------------------+------------------+------------------+------------------+
2 rows in set (0.001 sec)

清理环境

-- 删除所有限流规则
DROP CONCURRENT_LIMITING_RULE IF EXISTS `review_query_limit`;
DROP CONCURRENT_LIMITING_RULE IF EXISTS `activity_query_limit`;
DROP CONCURRENT_LIMITING_RULE IF EXISTS `emergency_limit`;

-- 删除测试表
DROP TABLE IF EXISTS user_activities;
DROP TABLE IF EXISTS product_reviews;
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS products;

图片好像都挂了