当前位置: 首页 > news >正文

ShardingSphere 5.2.0:分片审计功能拦截多分片场景下的不合理请求

一、背景

Apache ShardingSphere 基于用户的实际使用场景,为用户打造了多种实用功能,包括数据分片、读写分离等。在数据分片功能中,我们发现有些用户涉及到的分片较多,一个分片逻辑表可能对应后端 1000 个物理表,这给用户带来了一定的困扰。例如用户执行 SELECT * FROM t_order 语句则会导致全路由,显然这并不是 OLTP 的使用场景,这种 SQL 可以放到其他的 Proxy 里,避免阻塞其他的请求。但用户可能对 Proxy 并不了解,或者写了 where 条件,却不清楚条件里面不包含分片条件,这样的话一样是需要全路由。全路由会导致 Proxy 性能受到影响,严重情况下将导致正常的请求无法执行。试想一下如果这 1000 个分片都在一个物理库里面,如果并行的话需要取 1000 个连接,如果串行可能请求直接超时。对此用户提出了一个需求,能不能直接拦截这种不合理的请求。

对于这个问题,我们进行了更深层次的思考。如果只是简单拦截这种全路由的操作,只要在代码里面判断一下,并且在配置文件添加一个开关即可满足。那如果用户后面需要设置某个表只读或者要求更新操作必须携带 limit,那是不是又要改代码加配置?这显然与 Proxy 可插拔的逻辑相违背。

基于以上痛点,在刚刚发布的 5.2.0 版本中,Apache ShardingSphere 为用户提供了分片审计的功能。审计既可以是拦截操作也可以是统计操作,与分片和唯一键生成算法类似,审计算法同样支持热插拔,用户可以自定义,并且通过配置即可实现审计。下面我们将结合具体的 SQL 实例,为大家详细解读分片审计的实现逻辑。

二、分片审计接口

Apache ShardingSphere 审计的入口在 org.apache.shardingsphere.infra.executor.check.SQLCheckEngine 类,该类会调用 SQLChecker 接口的 check 方法,目前 ShardingSphere 的审计包括权限审计(验证用户名密码)以及分片审计,这里我们要关心的是分片审计 ShardingAuditChecker 里面实现的父类接口。

d05d774c02c3101adb287c898e389a82.png

通过查看 org.apache.shardingsphere.sharding.checker.audit.ShardingAuditCheckercheck 代码,我们可以快速了解它的原理。

public interface ShardingAuditAlgorithm extends ShardingSphereAlgorithm {
    
    /**
     * Sharding audit algorithm SQL check.
     *
     * @param sqlStatementContext SQL statement context
     * @param parameters SQL parameters
     * @param grantee grantee
     * @param database database
     * @return SQL check result
     */
    SQLCheckResult check(SQLStatementContext<?> sqlStatementContext, List<Object> parameters, Grantee grantee, ShardingSphereDatabase database);
}

该方法会去获取涉及到的所有分片表的审计策略,并且遍历调用每个分片表审计策略里面配置的审计算法,如果有一个审计算法没有通过就直接抛出异常给用户。可能有些用户会好奇这里面的 disableAuditNames 有什么用。分片审计还支持了跳过分片审计的功能。对于有一些场景用户可能就是需要执行一些原本应该被审计拦截的 SQL,并且他们很清楚这条 SQL 带来的影响。对此我们提供了 Hint: disableAuditNames 跳过审计拦截,后面会结合实际的例子来介绍该用法。当然并不是用户想跳过就能跳过的,Proxy 管理员可以配置 allowHintDisable 来控制是否允许用户通过 Hint 的形式跳过分片审计,该参数默认是 true,即允许。

三、分片审计算法

分片审计算法接口 org.apache.shardingsphere.sharding.spi.ShardingAuditAlgorithm 继承自 SPI 类 ShardingSphereAlgorithm,继承了父类的 typeprops 两个参数,并且定义了自己的方法 check,如果想自定义自己的审计算法,只要实现该接口并添加到 META-INF.services 即可。

0dceecab5bfa201c2f537a7873c8be51.png

public interface ShardingAuditAlgorithm extends ShardingSphereAlgorithm {
    
    /**
     * Sharding audit algorithm SQL check.
     *
     * @param sqlStatementContext SQL statement context
     * @param parameters SQL parameters
     * @param grantee grantee
     * @param database database
     * @return SQL check result
     */
    SQLCheckResult check(SQLStatementContext<?> sqlStatementContext, List<Object> parameters, Grantee grantee, ShardingSphereDatabase database);
}

Apache ShardingSphere 内部实现了一个比较通用的分片审计算法 org.apache.shardingsphere.sharding.algorithm.audit.DMLShardingConditionsShardingAuditAlgorithm,也就是前文提到的拦截全路由的 SQL 语句。该算法通过判断分片条件是否为空来拦截,当然如果广播表或者非分片表则不应该拦截。

public final class DMLShardingConditionsShardingAuditAlgorithm implements ShardingAuditAlgorithm {
    
    @Getter
    private Properties props;
    
    @Override
    public void init(final Properties props) {
        this.props = props;
    }
    
    @SuppressWarnings({"rawtypes", "unchecked"})
    @Override
    public SQLCheckResult check(final SQLStatementContext<?> sqlStatementContext, final List<Object> parameters, final Grantee grantee, final ShardingSphereDatabase database) {
        if (sqlStatementContext.getSqlStatement() instanceof DMLStatement) {
            ShardingRule rule = database.getRuleMetaData().getSingleRule(ShardingRule.class);
            if (rule.isAllBroadcastTables(sqlStatementContext.getTablesContext().getTableNames())
                    || sqlStatementContext.getTablesContext().getTableNames().stream().noneMatch(rule::isShardingTable)) {
                return new SQLCheckResult(true, "");
            }
            ShardingConditionEngine shardingConditionEngine = ShardingConditionEngineFactory.createShardingConditionEngine(sqlStatementContext, database, rule);
            if (shardingConditionEngine.createShardingConditions(sqlStatementContext, parameters).isEmpty()) {
                return new SQLCheckResult(false, "Not allow DML operation without sharding conditions");
            }
        }
        return new SQLCheckResult(true, "");
    }
    
    @Override
    public String getType() {
        return "DML_SHARDING_CONDITIONS";
    }
}

这里再介绍一个分片审计算法 LimitRequiredShardingAuditAlgorithm,该算法会拦截 updatedelete 操作未携带 limit 的 SQL,因为该算法通用性较差,目前并没有集成在 Apache ShardingSphere 内。可以看见要实现一个自定义的算法还是很简单的,这也是为什么要设计分片审计框架的原因,通过热插拔的形式,ShardingSphere 具备了强大的拓展性。

public final class LimitRequiredShardingAuditAlgorithm implements ShardingAuditAlgorithm {
    
    @Getter
    private Properties props;
    
    @Override
    public void init(final Properties props) {
        this.props = props;
    }
    
    @SuppressWarnings({"rawtypes", "unchecked"})
    @Override
    public SQLCheckResult check(final SQLStatementContext<?> sqlStatementContext, final List<Object> parameters, final Grantee grantee, final ShardingSphereDatabase database) {
        if (sqlStatementContext instanceof UpdateStatementContext && !((MySQLUpdateStatement) sqlStatementContext.getSqlStatement()).getLimit().isPresent()) {
            return new SQLCheckResult(false, "Not allow update without limit");
        }
        if (sqlStatementContext instanceof DeleteStatementContext && !((MySQLDeleteStatement) sqlStatementContext.getSqlStatement()).getLimit().isPresent()) {
            return new SQLCheckResult(false, "Not allow delete without limit");
        }
        return new SQLCheckResult(true, "");
    }
    
    @Override
    public String getType() {
        return "LIMIT_REQUIRED";
    }
}

四、分片审计的使用

分片审计需要给逻辑表配置审计策略,为了便于大家快速上手,分片审计的配置和分片算法以及分片键值生成器一样,有一个算法定义以及策略定义,同时还支持默认的审计策略。如果逻辑表里面配置了审计策略则只对逻辑表生效,如果配置了 defaultAuditStrategy 则对该分片规则下的所有逻辑表生效。auditors 类似 shardingAlgorithmsauditStrategy 类似 databaseStrategydatabaseStrategydefaultAuditStrategy 类似 defaultDatabaseStrategydefaultTableStrategy

参考配置如下,这里只添加了分片审计相关的配置,分片算法、数据源等配置请自行添加。

rules:
  - !SHARDING
    tables:
      t_order:
        actualDataNodes: ds_${0..1}.t_order_${0..1}
        auditStrategy:
          auditorNames:
            - sharding_key_required_auditor
          allowHintDisable: true

    defaultAuditStrategy:
      auditorNames:
        - sharding_key_required_auditor
      allowHintDisable: true

    auditors:
      sharding_key_required_auditor:
        type: DML_SHARDING_CONDITIONS

第一步我们执行一个查询操作,由于配置了拦截全库路由的审计策略,直接抛出了错误。

mysql> select * from t_order;
ERROR 13000 (44000): SQL check failed, error message: Not allow DML operation without sharding conditions

第二步我们添加 HINT,HINT 名称是 /* ShardingSphere hint: disableAuditNames */disableAuditNames 后跟随的是上面配置的 auditorsNames,如果是多个的话用空格分隔,例如 /* ShardingSphere hint: disableAuditNames=auditName1 auditName2*/,我们使用该 HINT 后可以看见 SQL 操作执行成功。

mysql> /* ShardingSphere hint: disableAuditNames=sharding_key_required_auditor */ select * from t_order;
+----------+---------+------------+--------+
| order_id | user_id | address_id | status |
+----------+---------+------------+--------+
|       30 |      20 |         10 | 20     |
|       32 |      22 |         10 | 20     |
+----------+---------+------------+--------+
2 rows in set (0.01 sec)

这里再给大家说一些注意事项,如果要使用 HINT 需要修改 Proxy 的 server.yaml 配置。另外如果你是直接使用 MySQL 的终端连接 Proxy,需要添加 -c 参数,否则 HINT 注释将在 MySQL 终端被过滤掉,自然也就无法到后端被 Proxy 解析到了。

rules:
  - !SQL_PARSER
    sqlCommentParseEnabled: true
    sqlStatementCache:
      initialCapacity: 2000
      maximumSize: 65535
    parseTreeCache:
      initialCapacity: 128
      maximumSize: 1024
props:
  proxy-hint-enabled: true
mysql -uroot -proot -h127.0.0.1 -P3307  -c

五、分片审计 DistSQL

目前 Apache ShardingSphere 也已经支持了分片审计的部分 DistSQL,5.2.0 版本支持如下 DistSQL,具体以官方 Release Note 为准:https://github.com/apache/shardingsphere/discussions/20639。

CREATE SHARDING AUDITOR
ALTER SHARDING AUDITOR
SHOW SHARDING AUDIT ALGORITHMS

未来版本将支持以下 DistSQL:

DROP SHARINDG AUDITOR
SHOW UNUSED SHARIDNG AUDIT ALGORITHMS
CREATE SHARDING TABLE RULE # 包含AUDIT_STRATEGY

本文详细介绍了分片审计的基本实现原理以及具体的使用示例,相信通过本文读者朋友们对分片审计都有了一些基本的了解,大家可以根据自己的需求使用分片审计或者自定义算法,如果是通用算法也欢迎大家向社区提交。在使用过程中遇到任何问题或者有任何想法,都欢迎来社区反馈。

GitHub 地址:https://github.com/apache/shardingsphere
中文社区:https://community.sphere-ex.com/

作者

黄挺,腾讯金融科技工程师,ShardingSphere Committer,主要负责与 Proxy 相关的数据分片审计及事务特性等研发工作。

相关文章:

  • 毕业设计 单片机stm32智能大棚监控护理系统 - lora 远程通信
  • 关于QCefView的一些事
  • b站pink老师JavaScript的jQuery 案例代码——电梯导航案例
  • Python快速实现简易飞机大战小游戏
  • PowerWorld仿真与电力系统潮流计算(牛顿拉夫逊法和高斯赛德尔法)(Matlab实现)
  • VSCode自动更新后关闭,重新打开后版本自动降级
  • Google Earth Engine(GEE)——如何处理阈值筛选后的结果没发生变化,以青藏高原NDBI为例
  • 数字验证学习笔记——SystemVerilog芯片验证4 ——数据类型
  • 行为树BT设计与实现
  • 基于神经网络的指纹识别,指纹比对技术何时出现
  • 【图像分割】基于差分进化算法优化模糊熵实现多级图像阈值分割附matlab代码
  • LeetCode 0817. 链表组件
  • 27、Java——超市会员管理系统(对象+IO流)
  • 嵌入式分享合集74
  • 布尔模型,向量空间模型
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • Android 架构优化~MVP 架构改造
  • CSS居中完全指南——构建CSS居中决策树
  • Docker容器管理
  • ES6 ...操作符
  • FineReport中如何实现自动滚屏效果
  • Java方法详解
  • Nginx 通过 Lua + Redis 实现动态封禁 IP
  • Quartz初级教程
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • 对JS继承的一点思考
  • 复杂数据处理
  • 更好理解的面向对象的Javascript 1 —— 动态类型和多态
  • 聚类分析——Kmeans
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 详解NodeJs流之一
  • 鱼骨图 - 如何绘制?
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • 中文输入法与React文本输入框的问题与解决方案
  • Nginx实现动静分离
  • Unity3D - 异步加载游戏场景与异步加载游戏资源进度条 ...
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • ​​​​​​​​​​​​​​Γ函数
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (LeetCode) T14. Longest Common Prefix
  • (八)Flask之app.route装饰器函数的参数
  • (附源码)ssm经济信息门户网站 毕业设计 141634
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (四)Android布局类型(线性布局LinearLayout)
  • (转)C#开发微信门户及应用(1)--开始使用微信接口
  • (转)甲方乙方——赵民谈找工作
  • .NET BackgroundWorker
  • .NET Core 网络数据采集 -- 使用AngleSharp做html解析
  • .Net 中Partitioner static与dynamic的性能对比
  • :如何用SQL脚本保存存储过程返回的结果集
  • @hook扩展分析
  • @staticmethod和@classmethod的作用与区别
  • [ 渗透工具篇 ] 一篇文章让你掌握神奇的shuize -- 信息收集自动化工具