感谢 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:分片审计在多分片场景中拦截不合理的请求 上,并经许可重新发布。
评论已关闭。