感谢 ShardingSphere 社区 的持续审查和反馈,开发了诸如 数据分片 和读/写分离等功能,我们的团队发现一些用户在使用数据分片功能时创建了许多分片。
在这种情况下,一个分片逻辑表可能对应 1,000 个物理表,这给用户带来了困扰。
例如,SELECT * FROM t_order 语句将导致全路由,这显然不适用于 OLTP。此 SQL 可以放在另一个 Proxy 中,以避免阻塞其他请求。
但是,如果用户不熟悉 Proxy 或如何编写 where 条件,并且不知道分片在此条件中不受支持,则仍然需要全路由。
全路由可能会降低 Proxy 的性能,甚至导致合理的请求失败。想象一下,一个物理数据库中有 1,000 个分片。如果它们并行执行,则需要 1,000 个连接;如果串行执行,则请求可能会导致超时。因此,社区用户询问是否可以直接拦截不合理的请求。
我们的团队考虑了这个问题一段时间。一种选择是简单地阻止全路由操作。这样做需要在代码中进行检查,并在配置文件中添加一个开关。另一方面,如果用户稍后需要将表设置为只读,或要求更新操作携带 limit,这是否意味着代码和配置再次更改?这种方法显然与 Proxy 的可插拔逻辑背道而驰。
为了响应上述问题,最近发布的 Apache ShardingSphere 5.2.0 为用户提供了 SQL 分片功能的审计。审计可以是拦截操作或统计操作。与分片和唯一键生成算法类似,审计算法是面向插件的、用户定义的和可配置的。
[ 相关阅读 Apache ShardingSphere 的 5 项新改进 ]
接下来,我将详细阐述使用特定 SQL 示例审核数据分片功能的实现逻辑。
分片接口的审计
Apache ShardingSphere 审计的入口点位于 org.apache.shardingsphere.infra.executor.check.SQLCheckEngine 类中,该类将调用 SQLChecker 接口的 check 方法。目前,ShardingSphere 审计包含权限审计(验证用户名和密码)和分片审计。
本示例重点介绍在分片审计的 ShardingAuditChecker 中实现的父接口。
 
(Yacine Si Tayeb,CC BY-SA 4.0)
您可以通过查看 org.apache.shardingsphere.sharding.checker.audit.ShardingAuditChecker 的 check 代码快速了解其工作原理。
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 来控制是否允许用户跳过此过程。默认值为 true,表示允许基于 Hint 的跳过。
分片算法的审计
分片算法审计接口 org.apache.shardingsphere.sharding.spi.ShardingAuditAlgorithm 继承自 SPI 类 ShardingSphereAlgorithm。它继承了 type 和 props 属性,并定义了自己的 check 方法。如果您想自定义您的审计算法,只需实现该接口并将其添加到 INF.services 中。
 
(Yacine Si Tayeb,CC BY-SA 4.0)
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 语句。
该算法通过确定分片条件是否为 null 来做出决策。当然,它不会拦截广播表和非分片表。
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。此算法可以拦截在 update 和 delete 操作中未携带 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 类似于 ShardingAlgorithms,auditStrategy 类似于 databaseStrategy,而 defaultAuditStrategy 类似于 defaultDatabaseStrategy 或 defaultTableStrategy。
请参考以下示例。仅显示了分片审计的配置。您必须自己配置分片算法和数据源。
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步骤 1:执行查询操作。由于配置了拦截全数据库路由的审计策略,因此显示错误。
mysql> select * from t_order;
ERROR 13000 (44000): SQL check failed, error message: Not allow DML operation without sharding conditions步骤 2:添加 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 5.2.0 支持以下带有分片功能审计的 DistSQL
CREATE SHARDING AUDITOR
ALTER SHARDING AUDITOR
SHOW SHARDING AUDIT ALGORITHMS
The following DistSQL will be supported in future releases:
DROP SHARDING AUDITOR
SHOW UNUSED SHARDING AUDIT ALGORITHMS
CREATE SHARDING TABLE RULE # including AUDIT_STRATEGY这篇文章介绍了分片审计如何通过具体示例工作。我相信您已经对此功能有了基本的了解,并且可以在需要时使用它,或者使用自定义算法。
也欢迎您向社区提交通用算法。如果您有任何想法要贡献或在使用 ShardingSphere 时遇到问题,请随时在 GitHub 上发布它们。
本文最初出现在 ShardingSphere 5.2.0:分片审计在多分片场景中拦截不合理的请求 上,并经许可重新发布。
 
 
 
 
 
 

评论已关闭。