网站后端连接数据库知识点大全(三)

简介: 教程来源 https://ljtgc.cn/article/6.html 本文系统讲解数据库连接安全(SSL加密、敏感信息保护、SQL注入防护)、性能优化(连接池调优、查询优化、读写分离)、监控诊断(连接池/慢查询监控、指标采集)及故障排查(超时处理、连接泄漏检测),涵盖最佳实践与可落地代码示例。

七、数据库连接安全

7.1 连接加密

// SSL 连接配置
public class SSLConnectionDemo {

    public Connection getSSLConnection() throws SQLException {
        Properties props = new Properties();
        props.setProperty("user", "username");
        props.setProperty("password", "password");

        // MySQL SSL 配置
        props.setProperty("useSSL", "true");
        props.setProperty("requireSSL", "true");
        props.setProperty("verifyServerCertificate", "true");
        props.setProperty("trustCertificateKeyStoreUrl", "file:/path/to/truststore");
        props.setProperty("trustCertificateKeyStorePassword", "storepass");
        props.setProperty("clientCertificateKeyStoreUrl", "file:/path/to/keystore");
        props.setProperty("clientCertificateKeyStorePassword", "keypass");

        return DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", props);
    }
}

7.2 敏感信息保护

// 使用环境变量存储密码
public class SecureConnection {

    // 从环境变量读取
    private static final String DB_PASSWORD = System.getenv("DB_PASSWORD");

    // 从配置中心读取
    @Value("${database.password}")
    private String dbPassword;

    // 使用加密工具解密
    public String getDecryptedPassword() {
        String encrypted = "encrypted_password";
        return decrypt(encrypted);
    }

    private String decrypt(String encrypted) {
        // 实现解密逻辑
        return "decrypted";
    }
}

// 配置密码加密(Jasypt)
@Configuration
public class EncryptedConfig {

    @Bean
    public StringEncryptor stringEncryptor() {
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
        encryptor.setPassword(System.getenv("JASYPT_PASSWORD"));
        return encryptor;
    }
}

7.3 SQL 注入防护

// 1. 使用 PreparedStatement(推荐)
public User findUser(String username) {
    String sql = "SELECT * FROM users WHERE username = ?";
    // 使用 PreparedStatement,自动转义
    // ...
}

// 2. 使用参数化查询(MyBatis)
// @Select("SELECT * FROM users WHERE username = #{username}")
// 使用 #{} 而非 ${}

// 3. 输入验证
public void validateInput(String username) {
    if (username == null || !username.matches("^[a-zA-Z0-9_]{3,20}$")) {
        throw new IllegalArgumentException("用户名格式错误");
    }
}

// 4. 使用 ORM 框架
// JPA/Hibernate 自动参数化

八、性能优化

8.1 连接池调优

@Component
public class ConnectionPoolTuner {

    // 根据业务特点调整连接池大小
    public DataSource tunedDataSource() {
        HikariConfig config = new HikariConfig();

        // 计算最佳连接数
        // 公式:连接数 = (核心数 * 2) + 有效磁盘数
        int optimalSize = calculateOptimalPoolSize();
        config.setMaximumPoolSize(optimalSize);

        // 根据响应时间要求设置超时
        config.setConnectionTimeout(calculateTimeout());

        // 根据连接使用模式设置生命周期
        config.setMaxLifetime(calculateMaxLifetime());

        return new HikariDataSource(config);
    }

    private int calculateOptimalPoolSize() {
        int cores = Runtime.getRuntime().availableProcessors();
        // 假设 I/O 密集型应用
        return Math.max(5, cores * 2);
    }

    private long calculateTimeout() {
        // 根据业务 SLA 设置
        return 30000; // 30秒
    }
}

8.2 查询优化

@Repository
public class OptimizedQueryDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    // 1. 只查询需要的字段
    public List<User> findUsersWithLimit() {
        String sql = "SELECT id, username FROM users LIMIT 100";  // 避免 SELECT *
        return jdbcTemplate.query(sql, (rs, rowNum) -> {
            User user = new User();
            user.setId(rs.getLong("id"));
            user.setUsername(rs.getString("username"));
            return user;
        });
    }

    // 2. 使用批处理
    public void batchInsertOptimized(List<User> users) {
        String sql = "INSERT INTO users (username, age) VALUES (?, ?)";
        jdbcTemplate.batchUpdate(sql, users, 1000, (ps, user) -> {
            ps.setString(1, user.getUsername());
            ps.setInt(2, user.getAge());
        });
    }

    // 3. 使用分页查询
    public List<User> findUsersByPage(int page, int size) {
        String sql = "SELECT * FROM users ORDER BY id LIMIT ? OFFSET ?";
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class), 
                                   size, (page - 1) * size);
    }

    // 4. 使用索引优化
    // 确保查询条件字段有索引
    public User findByEmail(String email) {
        // 确保 email 字段有唯一索引
        String sql = "SELECT * FROM users WHERE email = ?";
        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), email);
    }
}

8.3 读写分离

@Configuration
public class ReadWriteSplittingConfig {

    @Bean
    @Primary
    public DataSource routingDataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("write", writeDataSource());
        targetDataSources.put("read1", readDataSource1());
        targetDataSources.put("read2", readDataSource2());

        RoutingDataSource routingDataSource = new RoutingDataSource();
        routingDataSource.setDefaultTargetDataSource(writeDataSource());
        routingDataSource.setTargetDataSources(targetDataSources);

        return routingDataSource;
    }

    @Bean
    public DataSource writeDataSource() {
        // 主库配置
        return DataSourceBuilder.create()
            .url("jdbc:mysql://master:3306/mydb")
            .username("root")
            .password("password")
            .build();
    }

    @Bean
    public DataSource readDataSource1() {
        // 从库1配置
        return DataSourceBuilder.create()
            .url("jdbc:mysql://slave1:3306/mydb")
            .username("readonly")
            .password("readonly")
            .build();
    }

    // 动态数据源路由
    public static class RoutingDataSource extends AbstractRoutingDataSource {
        private static final ThreadLocal<String> context = new ThreadLocal<>();

        public static void setDataSource(String dataSource) {
            context.set(dataSource);
        }

        @Override
        protected Object determineCurrentLookupKey() {
            return context.get();
        }
    }
}

// 使用 AOP 实现读写分离
@Aspect
@Component
public class ReadWriteAspect {

    @Around("@annotation(Transactional)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Transactional transactional = 
            ((MethodSignature) pjp.getSignature()).getMethod().getAnnotation(Transactional.class);

        if (transactional != null && transactional.readOnly()) {
            // 读操作,使用从库
            RoutingDataSource.setDataSource("read");
        } else {
            // 写操作,使用主库
            RoutingDataSource.setDataSource("write");
        }

        try {
            return pjp.proceed();
        } finally {
            RoutingDataSource.setDataSource(null);
        }
    }
}

九、监控与诊断

9.1 连接监控

@Component
public class ConnectionMonitor {

    private static final Logger log = LoggerFactory.getLogger(ConnectionMonitor.class);

    @Autowired
    private DataSource dataSource;

    // 监控连接池状态
    @Scheduled(fixedDelay = 30000)
    public void monitorConnectionPool() {
        if (dataSource instanceof HikariDataSource) {
            HikariDataSource hikari = (HikariDataSource) dataSource;
            HikariPoolMXBean pool = hikari.getHikariPoolMXBean();

            log.info("=== Connection Pool Statistics ===");
            log.info("Active Connections: {}", pool.getActiveConnections());
            log.info("Idle Connections: {}", pool.getIdleConnections());
            log.info("Total Connections: {}", pool.getTotalConnections());
            log.info("Threads Waiting: {}", pool.getThreadsAwaitingConnection());
            log.info("===================================");
        }
    }

    // 慢查询监控
    @Aspect
    @Component
    public static class SlowQueryMonitor {

        private static final long SLOW_THRESHOLD = 1000; // 1秒

        @Around("execution(* com.example.dao.*.*(..))")
        public Object monitor(ProceedingJoinPoint pjp) throws Throwable {
            long start = System.currentTimeMillis();
            try {
                return pjp.proceed();
            } finally {
                long duration = System.currentTimeMillis() - start;
                if (duration > SLOW_THRESHOLD) {
                    log.warn("慢查询: {}.{} - 耗时: {}ms", 
                             pjp.getTarget().getClass().getSimpleName(),
                             pjp.getSignature().getName(), duration);
                }
            }
        }
    }
}

9.2 性能指标收集

@Component
public class DatabaseMetrics {

    private final MeterRegistry meterRegistry;
    private final DataSource dataSource;

    public DatabaseMetrics(MeterRegistry meterRegistry, DataSource dataSource) {
        this.meterRegistry = meterRegistry;
        this.dataSource = dataSource;
        initMetrics();
    }

    private void initMetrics() {
        // 连接池指标
        if (dataSource instanceof HikariDataSource) {
            HikariDataSource hikari = (HikariDataSource) dataSource;
            HikariPoolMXBean pool = hikari.getHikariPoolMXBean();

            Gauge.builder("hikari.active.connections", pool, HikariPoolMXBean::getActiveConnections)
                .register(meterRegistry);

            Gauge.builder("hikari.idle.connections", pool, HikariPoolMXBean::getIdleConnections)
                .register(meterRegistry);

            Gauge.builder("hikari.total.connections", pool, HikariPoolMXBean::getTotalConnections)
                .register(meterRegistry);

            Gauge.builder("hikari.threads.waiting", pool, HikariPoolMXBean::getThreadsAwaitingConnection)
                .register(meterRegistry);
        }

        // 查询计数器
        Timer.Sample sample = Timer.start(meterRegistry);
        // 记录查询耗时
    }
}

十、故障排查与常见问题

10.1 连接超时问题

public class ConnectionTimeoutHandler {

    // 1. 检查网络连通性
    public boolean checkNetwork(String host, int port) {
        try (Socket socket = new Socket()) {
            socket.connect(new InetSocketAddress(host, port), 5000);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    // 2. 检查数据库是否可连接
    public boolean checkDatabase(DataSource dataSource) {
        try (Connection conn = dataSource.getConnection()) {
            return conn.isValid(5);
        } catch (SQLException e) {
            return false;
        }
    }

    // 3. 自动重连机制
    public <T> T executeWithRetry(Supplier<T> operation) {
        int maxRetries = 3;
        int retryDelay = 1000;

        for (int i = 0; i < maxRetries; i++) {
            try {
                return operation.get();
            } catch (Exception e) {
                if (i == maxRetries - 1) throw e;
                try {
                    Thread.sleep(retryDelay);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException(ie);
                }
            }
        }
        throw new RuntimeException("操作失败");
    }
}

10.2 连接泄漏排查

@Component
public class LeakDetection {

    // 使用 ThreadLocal 追踪连接
    private static final ThreadLocal<Map<String, StackTraceElement[]>> CONNECTION_TRACE = 
        new ThreadLocal<>();

    @Autowired
    private DataSource dataSource;

    public Connection getConnection() throws SQLException {
        Connection conn = dataSource.getConnection();

        // 记录获取连接的位置
        Map<String, StackTraceElement[]> trace = CONNECTION_TRACE.get();
        if (trace == null) {
            trace = new HashMap<>();
            CONNECTION_TRACE.set(trace);
        }
        trace.put(conn.toString(), Thread.currentThread().getStackTrace());

        return new LeakAwareConnection(conn);
    }

    // 监控连接泄漏
    @Scheduled(fixedDelay = 60000)
    public void detectLeaks() {
        if (dataSource instanceof HikariDataSource) {
            HikariDataSource hikari = (HikariDataSource) dataSource;
            HikariPoolMXBean pool = hikari.getHikariPoolMXBean();

            // HikariCP 自带泄漏检测
            // 配置 leakDetectionThreshold 参数
        }
    }

    // 包装连接,自动记录关闭
    private static class LeakAwareConnection implements Connection {
        private final Connection delegate;
        private final long createTime;

        LeakAwareConnection(Connection delegate) {
            this.delegate = delegate;
            this.createTime = System.currentTimeMillis();
        }

        @Override
        public void close() throws SQLException {
            long lifeTime = System.currentTimeMillis() - createTime;
            if (lifeTime > 60000) {
                log.warn("连接使用时间过长: {}ms", lifeTime);
            }
            delegate.close();
        }

        // 其他方法委托给 delegate
    }
}

数据库连接的世界广阔而深刻,愿本文成为你后端开发之路上的重要指南。持续学习,深入实践,你一定能成为数据库连接方面的专家!
来源:
https://ljtgc.cn/

相关文章
|
9天前
|
人工智能 JSON 机器人
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
本文带你零成本玩转OpenClaw:学生认证白嫖6个月阿里云服务器,手把手配置飞书机器人、接入免费/高性价比AI模型(NVIDIA/通义),并打造微信公众号“全自动分身”——实时抓热榜、AI选题拆解、一键发布草稿,5分钟完成热点→文章全流程!
11102 95
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
|
9天前
|
人工智能 IDE API
2026年国内 Codex 安装教程和使用教程:GPT-5.4 完整指南
Codex已进化为AI编程智能体,不仅能补全代码,更能理解项目、自动重构、执行任务。本文详解国内安装、GPT-5.4接入、cc-switch中转配置及实战开发流程,助你从零掌握“描述需求→AI实现”的新一代工程范式。(239字)
5213 132
|
5天前
|
人工智能 自然语言处理 供应链
【最新】阿里云ClawHub Skill扫描:3万个AI Agent技能中的安全度量
阿里云扫描3万+AI Skill,发现AI检测引擎可识别80%+威胁,远高于传统引擎。
1369 3
|
7天前
|
人工智能 并行计算 Linux
本地私有化AI助手搭建指南:Ollama+Qwen3.5-27B+OpenClaw阿里云/本地部署流程
本文提供的全流程方案,从Ollama安装、Qwen3.5-27B部署,到OpenClaw全平台安装与模型对接,再到RTX 4090专属优化,覆盖了搭建过程的每一个关键环节,所有代码命令可直接复制执行。使用过程中,建议优先使用本地模型保障隐私,按需切换云端模型补充功能,同时注重显卡温度与显存占用监控,确保系统稳定运行。
1794 5
|
15天前
|
人工智能 JavaScript API
解放双手!OpenClaw Agent Browser全攻略(阿里云+本地部署+免费API+网页自动化场景落地)
“让AI聊聊天、写代码不难,难的是让它自己打开网页、填表单、查数据”——2026年,无数OpenClaw用户被这个痛点困扰。参考文章直击核心:当AI只能“纸上谈兵”,无法实际操控浏览器,就永远成不了真正的“数字员工”。而Agent Browser技能的出现,彻底打破了这一壁垒——它给OpenClaw装上“上网的手和眼睛”,让AI能像真人一样打开网页、点击按钮、填写表单、提取数据,24小时不间断完成网页自动化任务。
2982 6

热门文章

最新文章