ShardingSphere-JDBC实战:轻量级Java框架深度解析

ShardingSphere-JDBC实战:轻量级Java框架深度解析

【免费下载链接】shardingsphere Distributed SQL transaction & query engine for data sharding, scaling, encryption, and more - on any database. 【免费下载链接】shardingsphere 项目地址: https://gitcode.com/GitHub_Trending/sh/shardingsphere

本文深入解析ShardingSphere-JDBC作为轻量级Java框架的核心设计理念和技术实现。文章从JDBC驱动增强原理与兼容性设计入手,详细分析了其通过对标准JDBC接口的增强来实现分布式数据库功能,同时保持与现有应用程序完全兼容性的机制。内容包括驱动注册与URL识别、适配器模式与接口兼容性、状态管理与熔断机制、方法调用记录与重放机制等核心技术,以及Spring Boot集成与自动化配置、多数据源管理与连接池优化、性能调优与监控最佳实践等实战内容。

JDBC驱动增强原理与兼容性设计

ShardingSphere-JDBC作为一款轻量级的Java框架,其核心设计理念是通过对标准JDBC接口的增强来实现分布式数据库功能,同时保持与现有应用程序的完全兼容性。这种设计使得开发者无需修改业务代码即可享受分库分表、读写分离等分布式特性。

驱动注册与URL识别机制

ShardingSphere-JDBC通过实现java.sql.Driver接口来注册自定义驱动,其URL前缀为jdbc:shardingsphere:。当应用程序通过DriverManager.getConnection()方法获取连接时,ShardingSphere驱动会首先检查URL是否匹配自己的前缀:

public final class ShardingSphereDriver implements Driver {
    private static final String DRIVER_URL_PREFIX = "jdbc:shardingsphere:";
    
    @Override
    public boolean acceptsURL(final String url) {
        return null != url && url.startsWith(DRIVER_URL_PREFIX);
    }
    
    @Override
    public Connection connect(final String url, final Properties info) throws SQLException {
        return acceptsURL(url) ? dataSourceCache.get(url, DRIVER_URL_PREFIX).getConnection() : null;
    }
}

这种设计确保了ShardingSphere驱动不会干扰其他数据库驱动的正常工作,只有当URL明确指定使用ShardingSphere时才会介入处理。

适配器模式与接口兼容性

ShardingSphere-JDBC采用了经典的适配器模式来实现JDBC接口的兼容性。通过一系列抽象适配器类,框架确保了所有标准JDBC接口方法的正确实现:

mermaid

状态管理与熔断机制

ShardingSphere-JDBC引入了驱动状态管理机制,通过DriverState接口来管理不同的运行状态:

public interface DriverState {
    Connection getConnection(String databaseName, ContextManager contextManager);
}

框架实现了两种主要状态:

  • OKDriverState: 正常状态,返回标准的ShardingSphere连接
  • CircuitBreakDriverState: 熔断状态,返回熔断器连接,防止系统雪崩

这种状态机设计确保了系统在异常情况下的健壮性和可用性。

方法调用记录与重放机制

为了实现某些JDBC方法的兼容性,ShardingSphere采用了方法调用记录和重放机制:

public class MethodInvocationRecorder<T> {
    private final List<MethodInvocation<T>> methodInvocations = new LinkedList<>();
    
    public void record(String methodName, ForceExecuteCallback<T> callback) {
        methodInvocations.add(new MethodInvocation<>(methodName, callback));
    }
    
    public void replay(T target) {
        for (MethodInvocation<T> each : methodInvocations) {
            each.invoke(target);
        }
    }
}

这种机制特别适用于需要在特定时机批量执行的方法调用场景。

不支持操作的处理策略

对于某些JDBC规范中定义但实际使用较少的方法,ShardingSphere采用了统一的"不支持操作"处理策略:

public abstract class AbstractUnsupportedOperationConnection extends WrapperAdapter {
    @Override
    public final CallableStatement prepareCall(String sql) {
        throw new SQLFeatureNotSupportedException("prepareCall not supported");
    }
    
    @Override
    public final String nativeSQL(String sql) {
        throw new SQLFeatureNotSupportedException("nativeSQL not supported");
    }
}

通过抛出SQLFeatureNotSupportedException异常,框架明确告知调用者该方法不被支持,同时保持了接口的完整性。

数据类型与结果集兼容性

ShardingSphere-JDBC在结果集处理方面提供了全面的数据类型兼容性支持:

数据类型支持情况处理方式
基本类型完全支持直接代理到底层结果集
Blob/Clob完全支持包装器适配
数组类型完全支持数组类型转换
自定义类型条件支持通过unwrap机制

事务兼容性设计

在事务处理方面,ShardingSphere-JDBC保持了与标准JDBC事务模型的完全兼容:

mermaid

性能优化与兼容性平衡

ShardingSphere-JDBC在保持兼容性的同时,也进行了多项性能优化:

  1. 连接池复用: 重用物理数据库连接,减少连接建立开销
  2. 预处理语句缓存: 缓存预处理语句实例,提高重复查询性能
  3. 批量操作优化: 对批量操作进行特殊处理,减少网络往返
  4. 结果集流式处理: 支持内存严格模式和流式模式,平衡内存使用和性能

版本兼容性策略

ShardingSphere-JDBC采用了灵活的版本兼容性策略:

兼容性维度策略说明
JDBC版本向下兼容支持JDBC 4.0+规范
数据库版本驱动适配通过数据库方言适配不同版本
框架版本渐进升级保持主要版本间的兼容性

通过这种全面的兼容性设计,ShardingSphere-JDBC能够在几乎不修改现有代码的情况下,为应用程序提供强大的分布式数据库能力,真正实现了"增强而非颠覆"的设计理念。

Spring Boot集成与自动化配置

Apache ShardingSphere-JDBC作为轻量级的Java框架,提供了与Spring Boot深度集成的官方Starter,使得开发者能够以极简的方式将分布式数据库能力引入到Spring Boot应用中。通过自动化配置机制,ShardingSphere-JDBC能够无缝融入Spring生态,大大降低了分布式数据库的使用门槛。

核心依赖配置

Spring Boot集成ShardingSphere-JDBC的第一步是引入相应的Starter依赖。根据项目使用的ORM框架不同,可以选择不同的Starter模块:

<!-- 核心Spring Boot Starter -->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
    <version>5.5.0</version>
</dependency>

<!-- JPA集成Starter -->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-spring-boot-starter-jpa</artifactId>
    <version>5.5.0</version>
</dependency>

<!-- MyBatis集成Starter -->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-spring-boot-starter-mybatis</artifactId>
    <version>5.5.0</version>
</dependency>

自动化配置原理

ShardingSphere的Spring Boot Starter基于Spring Boot的自动配置机制实现,其核心配置流程如下:

mermaid

YAML配置详解

ShardingSphere-JDBC支持通过YAML文件进行声明式配置,以下是一个完整的分库分表示例:

spring:
  shardingsphere:
    # 数据源配置
    datasource:
      names: ds0, ds1
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db0
        username: root
        password: root
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db1
        username: root
        password: root
    
    # 分片规则配置
    rules:
      - !SHARDING
        tables:
          t_order:
            actual-data-nodes: ds$->{0..1}.t_order_$->{0..1}
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: database-inline
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: table-inline
            key-generate-strategy:
              column: order_id
              key-generator-name: snowflake
        
        # 分片算法配置
        sharding-algorithms:
          database-inline:
            type: INLINE
            props:
              algorithm-expression: ds$->{user_id % 2}
          table-inline:
            type: INLINE
            props:
              algorithm-expression: t_order_$->{order_id % 2}
        
        # 分布式序列算法
        key-generators:
          snowflake:
            type: SNOWFLAKE
    
    # 属性配置
    props:
      sql-show: true

高级配置特性

1. 多租户数据隔离
spring:
  shardingsphere:
    rules:
      - !SHARDING
        tables:
          t_tenant_data:
            actual-data-nodes: ds$->{0..1}.t_tenant_data_$->{tenant_id}
            database-strategy:
              standard:
                sharding-column: tenant_id
                sharding-algorithm-name: tenant-database
            table-strategy:
              standard:
                sharding-column: tenant_id
                sharding-algorithm-name: tenant-table
2. 读写分离配置
spring:
  shardingsphere:
    rules:
      - !READWRITE_SPLITTING
        data-sources:
          readwrite_ds:
            write-data-source-name: ds0
            read-data-source-names: ds1, ds2
            load-balancer-name: round_robin
        load-balancers:
          round_robin:
            type: ROUND_ROBIN
3. 数据加密配置
spring:
  shardingsphere:
    rules:
      - !ENCRYPT
        tables:
          t_user:
            columns:
              password:
                cipher-column: password
                encryptor-name: aes_encryptor
        encryptors:
          aes_encryptor:
            type: AES
            props:
              aes-key-value: 123456abc

自定义配置扩展

对于需要高度定制化的场景,ShardingSphere提供了多种扩展方式:

1. 自定义分片算法
@Slf4j
public class CustomShardingAlgorithm implements StandardShardingAlgorithm<Long> {
    
    @Override
    public String doSharding(Collection<String> availableTargetNames, 
                           PreciseShardingValue<Long> shardingValue) {
        for (String each : availableTargetNames) {
            if (each.endsWith(shardingValue.getValue() % 2 + "")) {
                return each;
            }
        }
        throw new UnsupportedOperationException();
    }
    
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, 
                                        RangeShardingValue<Long> shardingValue) {
        return availableTargetNames;
    }
    
    @Override
    public void init() {
        log.info("Custom sharding algorithm initialized");
    }
    
    @Override
    public String getType() {
        return "CUSTOM";
    }
}
2. 自定义配置加载器
@Component
public class DynamicConfigLoader implements ShardingSphereConfigurationLoader {
    
    @Override
    public ShardingSphereConfiguration load() {
        // 从外部配置中心动态加载配置
        return createDynamicConfiguration();
    }
    
    private ShardingSphereConfiguration createDynamicConfiguration() {
        // 构建动态配置逻辑
        return new ShardingSphereConfiguration();
    }
}

性能优化配置

1. 连接池优化
spring:
  shardingsphere:
    datasource:
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        hikari:
          maximum-pool-size: 20
          minimum-idle: 5
          connection-timeout: 30000
          idle-timeout: 600000
          max-lifetime: 1800000
2. SQL执行优化
spring:
  shardingsphere:
    props:
      sql-show: true
      sql-simple: true
      kernel-executor-size: 20
      max-connections-size-per-query: 1
      check-table-metadata-enabled: false

监控与诊断

ShardingSphere提供了完善的监控指标输出,可以与Spring Boot Actuator集成:

management:
  endpoints:
    web:
      exposure:
        include: health,metrics,shardingsphere
  metrics:
    export:
      prometheus:
        enabled: true

故障排除与最佳实践

1. 常见问题解决
问题现象原因分析解决方案
启动时报Bean冲突多个DataSource存在使用@Primary注解标记ShardingSphereDataSource
分片键值为null业务数据问题配置默认分片策略或验证数据完整性
跨库查询性能差路由策略不当优化分片策略,避免全库扫描
2. 最佳实践建议
  • 配置管理: 将ShardingSphere配置与业务配置分离,使用独立的配置文件
  • 版本兼容: 确保ShardingSphere版本与Spring Boot版本兼容
  • 监控告警: 集成APM工具监控SQL执行性能和数据库连接状态
  • 灰度发布: 新分片策略上线前进行充分的测试和灰度验证

通过Spring Boot Starter的自动化配置,ShardingSphere-JDBC能够以最小的侵入性为应用提供强大的分布式数据库能力,真正实现了"配置即用"的开发体验。

多数据源管理与连接池优化

在现代分布式数据库架构中,多数据源管理和连接池优化是确保系统高性能和稳定性的关键技术。ShardingSphere-JDBC通过其强大的数据源池管理机制,为开发者提供了灵活且高效的多数据源解决方案。

数据源池架构设计

ShardingSphere-JDBC采用分层架构设计,将数据源池管理抽象为独立的基础设施模块。整个架构基于SPI(Service Provider Interface)机制,支持多种连接池实现的无缝集成。

mermaid

多数据源配置管理

ShardingSphere-JDBC支持通过YAML或Java配置方式定义多个数据源,每个数据源可以独立配置连接池参数。以下是一个典型的多数据源配置示例:

dataSources:
  ds_0:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://localhost:3306/db0
    username: root
    password: root
    connectionTimeout: 30000
    idleTimeout: 600000
    maxLifetime: 1800000
    maximumPoolSize: 20
    minimumIdle: 5
    
  ds_1:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://localhost:3306/db1
    username: root
    password: root
    connectionTimeout: 30000
    idleTimeout: 600000
    maxLifetime: 1800000
    maximumPoolSize: 15
    minimumIdle: 3

连接池参数优化策略

针对不同的业务场景,ShardingSphere-JDBC提供了精细化的连接池参数配置能力。以下是一些关键的优化参数:

参数名默认值推荐值说明
maximumPoolSize1020-100最大连接数,根据并发请求量调整
minimumIdle105-10最小空闲连接数,避免频繁创建连接
connectionTimeout3000030000连接超时时间(毫秒)
idleTimeout600000600000空闲连接超时时间(毫秒)
maxLifetime18000001800000连接最大生命周期(毫秒)
validationTimeout50005000连接验证超时时间(毫秒)

动态数据源切换机制

ShardingSphere-JDBC实现了智能的数据源路由和切换机制,支持基于分片键、读写分离策略等多种路由方式:

// 数据源创建流程
public static DataSource create(final DataSourcePoolProperties props) {
    DataSource result = create(props.getPoolClassName());
    Optional<DataSourcePoolMetaData> poolMetaData = 
        TypedSPILoader.findService(DataSourcePoolMetaData.class, props.getPoolClassName());
    
    DataSourcePoolReflection dataSourcePoolReflection = new DataSourcePoolReflection(result);
    if (poolMetaData.isPresent()) {
        setDefaultFields(dataSourcePoolReflection, poolMetaData.get());
        setConfiguredFields(props, dataSourcePoolReflection, poolMetaData.get());
        appendJdbcUrlProperties(props.getCustomProperties(), result, 
                              poolMetaData.get(), dataSourcePoolReflection);
        dataSourcePoolReflection.addDefaultDataSourcePoolProperties(poolMetaData.get());
    }
    return result;
}

连接池性能监控

ShardingSphere-JDBC内置了连接池状态监控功能,可以通过JMX或自定义监控接口获取连接池的运行状态:

// 连接池状态监控示例
public class ConnectionPoolMonitor {
    private final Map<String, DataSource> dataSourceMap;
    
    public Map<String, ConnectionPoolStats> getPoolStats() {
        Map<String, ConnectionPoolStats> stats = new HashMap<>();
        for (Map.Entry<String, DataSource> entry : dataSourceMap.entrySet()) {
            if (entry.getValue() instanceof HikariDataSource) {
                HikariDataSource hikariDS = (HikariDataSource) entry.getValue();
                ConnectionPoolStats poolStats = new ConnectionPoolStats(
                    hikariDS.getHikariPoolMXBean().getActiveConnections(),
                    hikariDS.getHikariPoolMXBean().getIdleConnections(),
                    hikariDS.getHikariPoolMXBean().getTotalConnections()
                );
                stats.put(entry.getKey(), poolStats);
            }
        }
        return stats;
    }
}

故障转移与高可用

在多数据源环境下,ShardingSphere-JDBC提供了完善的故障检测和自动转移机制:

mermaid

最佳实践建议

  1. 连接池大小配置:根据实际业务负载动态调整连接池大小,避免过度配置或不足
  2. 超时参数优化:合理设置连接超时和空闲超时,平衡资源利用和响应速度
  3. 监控告警:建立完善的连接池监控体系,及时发现和处理连接泄漏问题
  4. 版本兼容性:确保使用的连接池版本与ShardingSphere-JDBC兼容
  5. 故障演练:定期进行数据源故障切换演练,确保高可用机制有效

通过ShardingSphere-JDBC的多数据源管理和连接池优化功能,开发者可以构建出高性能、高可用的分布式数据库访问层,有效应对大规模数据访问场景下的各种挑战。

性能调优与监控最佳实践

在分布式数据库环境中,性能调优和监控是确保系统稳定高效运行的关键环节。ShardingSphere-JDBC作为轻量级的Java框架,提供了丰富的性能优化手段和监控能力,帮助开发者构建高性能的分布式数据库应用。

性能调优策略

1. 连接池优化配置

连接池是影响数据库性能的关键因素之一。ShardingSphere-JDBC支持多种连接池实现,合理配置可以显著提升性能:

dataSources:
  ds_0:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    jdbcUrl: jdbc:mysql://localhost:3306/demo_ds_0
    username: root
    password: 
    connectionTimeout: 30000
    idleTimeout: 600000
    maxLifetime: 1800000
    maximumPoolSize: 50
    minimumIdle: 10

关键参数说明:

  • maximumPoolSize: 最大连接数,建议设置为CPU核心数的2-4倍
  • minimumIdle: 最小空闲连接数,避免频繁创建连接的开销
  • connectionTimeout: 连接超时时间,根据网络状况调整
  • maxLifetime: 连接最大生命周期,防止连接老化
2. 分片策略优化

合理的分片策略是提升查询性能的核心:

// 范围分片策略 - 适合时间序列数据
public final class RangeShardingAlgorithm implements RangeShardingAlgorithm<Long> {
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, 
                                       RangeShardingValue<Long> shardingValue) {
        Collection<String> result = new LinkedHashSet<>();
        Long lower = shardingValue.getValueRange().lowerEndpoint();
        Long upper = shardingValue.getValueRange().upperEndpoint();
        
        // 根据范围选择合适的分片
        for (long i = lower; i <= upper; i++) {
            for (String each : availableTargetNames) {
                if (each.endsWith(i % availableTargetNames.size() + "")) {
                    result.add(each);
                }
            }
        }
        return result;
    }
}
3. 批量操作优化

利用批量处理减少网络往返次数:

// 批量插入优化示例
try (Connection connection = dataSource.getConnection();
     PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO t_order (order_id, user_id) VALUES (?, ?)")) {
    
    connection.setAutoCommit(false);
    
    for (int i = 0; i < 1000; i++) {
        preparedStatement.setLong(1, orderIdGenerator.generateId());
        preparedStatement.setInt(2, userId);
        preparedStatement.addBatch();
        
        if (i % 100 == 0) {
            preparedStatement.executeBatch();
            connection.commit();
        }
    }
    
    preparedStatement.executeBatch();
    connection.commit();
}

监控体系构建

1. 指标监控配置

ShardingSphere-JDBC通过Agent模块提供完善的监控能力:

# agent配置示例
plugins:
  metrics:
    basePackages: "org.apache.shardingsphere.agent.plugin.metrics.core"
    prometheus:
      host: "localhost"
      port: 9090
  tracing:
    basePackages: "org.apache.shardingsphere.agent.plugin.tracing.core"
    jaeger:
      host: "localhost"
      port: 6831
2. 关键性能指标

需要重点关注以下性能指标:

指标类型指标名称说明建议阈值
连接池active_connections活跃连接数< 最大连接数的80%
查询性能query_duration_ms查询耗时P95 < 100ms
事务transaction_rate事务处理速率根据业务需求设定
错误率error_rate错误发生率< 0.1%
3. SQL性能分析

通过执行计划分析优化SQL:

-- 使用EXPLAIN分析SQL执行计划
EXPLAIN 
SELECT * FROM t_order 
WHERE user_id = 123 
AND order_time BETWEEN '2024-01-01' AND '2024-01-31';

性能调优实战案例

案例1:慢查询优化

问题现象: 分页查询在数据量较大时性能急剧下降

优化方案:

-- 原始慢查询
SELECT * FROM t_order 
WHERE user_id = 123 
ORDER BY create_time DESC 
LIMIT 100000, 20;

-- 优化后的查询
SELECT * FROM t_order 
WHERE user_id = 123 
AND create_time < '2024-01-01'  -- 使用时间范围缩小查询范围
ORDER BY create_time DESC 
LIMIT 20;
案例2:热点数据优化

问题现象: 某些分片成为热点,负载不均衡

优化方案:

// 使用一致性哈希算法避免热点
public class ConsistentHashShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    private final ConsistentHash<String> consistentHash;
    
    public ConsistentHashShardingAlgorithm() {
        this.consistentHash = new ConsistentHash<>(100, Collections.emptyList());
    }
    
    @Override
    public String doSharding(Collection<String> availableTargetNames, 
                           PreciseShardingValue<Long> shardingValue) {
        consistentHash.addAll(availableTargetNames);
        return consistentHash.get(shardingValue.getValue());
    }
}

监控告警配置

建立完善的监控告警体系:

# Prometheus告警规则示例
groups:
- name: shardingsphere_alerts
  rules:
  - alert: HighQueryLatency
    expr: rate(shardingsphere_query_duration_seconds_sum[5m]) / rate(shardingsphere_query_duration_seconds_count[5m]) > 0.1
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "高查询延迟警告"
      description: "查询平均延迟超过100ms"
  
  - alert: ConnectionPoolExhausted
    expr: shardingsphere_connection_pool_active_connections / shardingsphere_connection_pool_max_connections > 0.8
    for: 2m
    labels:
      severity: critical
    annotations:
      summary: "连接池耗尽警告"
      description: "连接池使用率超过80%"

性能测试方法论

建立科学的性能测试流程:

mermaid

测试关键指标:

  • TPS(每秒事务数)
  • QPS(每秒查询数)
  • 平均响应时间
  • 95分位响应时间
  • 错误率

最佳实践总结

  1. 连接池配置:根据实际业务负载合理设置连接池参数
  2. 分片策略:选择合适的分片算法,避免数据倾斜
  3. 批量操作:充分利用批量处理提升吞吐量
  4. 监控告警:建立完善的监控体系,及时发现性能问题
  5. 定期优化:定期进行性能测试和优化调整

通过系统性的性能调优和监控实践,可以确保ShardingSphere-JDBC在生产环境中发挥最佳性能,为业务系统提供稳定高效的数据库服务支撑。

总结

通过本文的深度解析,我们可以看到ShardingSphere-JDBC作为一款轻量级Java框架,通过精心的架构设计和全面的兼容性考虑,为开发者提供了强大的分布式数据库能力。从JDBC驱动增强原理到Spring Boot集成,从多数据源管理到性能调优监控,ShardingSphere-JDBC展现了其在分布式数据库领域的成熟度和专业性。框架采用'增强而非颠覆'的设计理念,确保开发者无需修改业务代码即可享受分库分表、读写分离等分布式特性,真正实现了轻量级、低侵入的开发体验。对于需要在分布式环境下处理海量数据的Java应用来说,ShardingSphere-JDBC无疑是一个值得深入研究和应用的重要技术选择。

【免费下载链接】shardingsphere Distributed SQL transaction & query engine for data sharding, scaling, encryption, and more - on any database. 【免费下载链接】shardingsphere 项目地址: https://gitcode.com/GitHub_Trending/sh/shardingsphere

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值