本文详细描述了SQL限流特性的需求设计方案以及使用方式,开发、测试人员可根据本文实现功能的开发、测试,DBA可根据本文合理使用SQL限流功能。
生产环境中可能出现由于业务量增长过快或者慢SQL等原因导致CPU使用率打满,当CPU打满时,会影响所有依赖该数据库实例的业务,因此需要有手段限制影响CPU的业务执行,保证其他核心业务不受影响。
SQL限流的目标是在出现CPU使用率打满影响所有业务时,通过限制非核心业务或者慢SQL的业务,实现核心业务的快速恢复。
MySQL软件结构如下图所示:
MySQL官网:https://dev.mysql.com/doc/refman/5.7/en/
总体架构如图所示,SQL限流的主要功能在执行器工作阶段通过限流规则与查询串的匹配来实现。
对外接口
针对SQL限流功能设计对外提供管理命令,如下:
对于预留用户的处理,核心功能如下:
// 更新
static bool update_delimiter(sys_var *self, THD *thd, enum_var_type type)
{
reload_rules(thd);
}
// 校验
static bool check_delimiter(sys_var *self, THD *thd, set_var *var)
{
judge(var->value);
judge(str);
judge(length);
}
SQL限流使用的场景为CPU负载过高,为了防止加重系统的负担,限流规则应该事先加载到内存中。基于此,需要对内存中的限流规则进行管理,涉及的操作有:规则的读取、移除、解析。
规则读取的场景有:
规则移除的场景有:
用户输入的规则在物理表中的表现是一个字符串,在将其加载到内存时,需要根据限流规则的分隔符将其解析为对应的规则模式串,在模式串中包含的关键字全部被满足时,需要进行限流。解析过程如下:
SQL限流的的流程控制添加在SQL执行阶段,主要的内容有:
int mysqld_main(int argc, char **argv)
{
...
load_rules();
...
}
void dec_conc(THD *thd, int command)
{
// 根据查询类型在对应链表找节点
node = find_by_id(list, thd->id);
// 并发数量减1
if (node) {
__sync_sub_and_fetch(&(node->concur), 1);
}
// 重置状态
thd->id = 0;
}
关闭服务时,释放限流规则占用的内存资源(遍历规则链表,释放其中每一个节点占用的内存)。
void clean_up()
{
cleanup();
}
SQL限流的基本功能的实现逻辑为查询串与规则串的匹配,匹配主要的策略如下:
/* The flow control does not take effect on system tables */
if (check_system_table(first_table->db)) {
return ret;
}
bool check_rule_matched(THD* thd, LIST* list)
{
while (满足条件,无异常) {
// 根据大小写开关是否打开,分别进行模式串匹配
it = find(query_str, item->key_array[nums]);
// 如果it为空,没有匹配到,查看下一个list,否则继续匹配当前限流规则节点
judge();
}
// 匹配成功,或者对下一个节点进行匹配
}
用户输入的限流规则保存在限流规则系统表中,在需要读取物理表中的数据时,需要打开表、读取数据、关闭表。
通过创建限流规则表读取类来控制对表的操作,类的定义如下:
class Du_table_access {
public:
Du_table_access() : m_drop_thd_object(NULL) {}
virtual ~Du_table_access() {}
// 初始化打开表的环境、锁表并且打开表
bool init(THD **thd, TABLE **table, bool is_write);
// 关闭表,清理环境
bool deinit(THD *thd, TABLE *table, bool error, bool need_commit);
// 设置打开表的策略
void before_open(THD *thd);
// 如果需要的话创建线程,大部分时候并不需要,因为手动执行读取数据的时候已经在线程中了
THD *create_thd();
// 如果手动创建了 thd,则需要手动清理
void drop_thd(THD *thd);
};
读取到的数据以节点的形式保存,并且挂在LIST中进行管理。
SET @cmd= "CREATE TABLE IF NOT EXISTS du_flow_control_rules (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'Id of the flow control rules.',
type ENUM('SELECT', 'UPDATE', 'INSERT', 'DELETE') CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT 'Type of flow control rules.',
max_concur INT NOT NULL COMMENT 'Max concurrent of sql.',
orig_str VARCHAR(1024) CHARSET SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT 'Original string of flow control rules.',
PRIMARY KEY(id)) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'Flow control rules info.'";
SET @str=IF(@have_innodb <> 0, CONCAT(@cmd, ' ENGINE= INNODB;'), CONCAT(@cmd, ' ENGINE= MYISAM;'));
PREPARE stmt FROM @str;
EXECUTE stmt;
DROP PREPARE stmt;
通过存储过程实现权限与格式控制:
DELIMITER $$
CREATE DEFINER='root'@'localhost' PROCEDURE mysql.add_flow_control ( IN sql_type INT, IN str VARCHAR(1024), IN max_num INT )
COMMENT '
Description
-----------
Basic functions for inserting rules.
It is not recommended to call it directly, but to call it through add_select_flow_control、
add_update_flow_control、add_update_flow_control and add_delete_flow_control.
'
SQL SECURITY INVOKER
BEGIN
IF (sql_type = 0) THEN
INSERT INTO mysql.du_flow_control_rules(type, max_concur, orig_str) VALUES('SELECT', max_num, str);
ELSEIF (sql_type = 1) THEN
INSERT INTO mysql.du_flow_control_rules(type, max_concur, orig_str) VALUES('UPDATE', max_num, str);
ELSEIF (sql_type = 2) THEN
INSERT INTO mysql.du_flow_control_rules(type, max_concur, orig_str) VALUES('INSERT', max_num, str);
ELSEIF (sql_type = 3) THEN
INSERT INTO mysql.du_flow_control_rules(type, max_concur, orig_str) VALUES('DELETE', max_num, str);
ELSE
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Sql type is error, please input correctly.';
END IF;
END$$
CREATE DEFINER='root'@'localhost' PROCEDURE mysql.add_select_flow_control (IN str VARCHAR(1024), IN max_num INT )
COMMENT '
Description
-----------
Used to add select type rules to the current rule table.
Parameters
-----------
str (VARCHAR(1024)):
The string of select rules entered by user.
max_num (INT):
The number of queries that can be executed concurrently.
Example
--------
mysql> SELECT * FROM du_flow_control_rules;
Empty set (0.00 sec)
mysql> CALL add_select_flow_control(''select~from~t1'', 100);
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM du_flow_control_rules;
+----+--------+------------+----------------+
| id | type | max_concur | orig_str |
+----+--------+------------+----------------+
| 1 | SELECT | 100 | select~from~t1 |
+----+--------+------------+----------------+
1 row in set (0.00 sec)
'
SQL SECURITY INVOKER
BEGIN
CALL add_flow_control(0, str, max_num);
END$$
DELIMITER ;
当开关关闭时,对于每一个查询在内核中执行,增加了对开关是否打开的判断的消耗,该操作对于整个语句的执行来说影响较小,可以忽略。
当开关打开时,需要将查询字符串与限流规则进行匹配,整个过程是内存操作,且根据类型将限流规则分为不同的链表,加快匹配速度,整个过程对性能的影响与限流规则的数量以及复杂度有关(注:加入SQL限流特性对系统有影响,影响程度与限流规则的数量和复杂度有关,但实际使用场景是针对少部分业务进行限流,且不需要限流的时候不启用该功能,因此该场景对性能的影响不决定最终实现)。
本文详细描述了基于MySQL内核的SQL限流功能的整体架构、主要功能模块等关键要素。SQL限流的设计与实现基于原生MySQL的执行原理与字符串匹配规则实现。整体功能对原生MySQL嵌入修改较少,除了对是否进行限流的判断以外,其他功能以独立模块呈现,安全可控。
*文 / Peter
本文属得物技术原创,更多精彩文章请看:得物技术
未经得物技术许可严禁转载,否则依法追究法律责任!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。