侧边栏壁纸
  • 累计撰写 52 篇文章
  • 累计创建 22 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

数据库优化——读写分离与分库分表

镇沛
2025-02-19 / 0 评论 / 0 点赞 / 13 阅读 / 0 字

在Spring Boot应用中,MySQL数据库的读写分离和分库分表是两种常见的优化手段,用于提升数据库性能、扩展性和高可用性。以下是对这两种方案的详细讲解,包括实现原理、具体步骤以及代码示例。


一、读写分离

1. 什么是读写分离?

读写分离是一种通过主从复制技术将数据库的读操作和写操作分离到不同的实例上的策略:

  • 主库(Master):负责处理写操作(INSERT、UPDATE、DELETE)。

  • 从库(Slave):负责处理读操作(SELECT),并定期同步主库的数据。

这种架构可以有效减轻主库的压力,提高系统的吞吐量。


2. 实现原理

  • 主从复制:MySQL支持基于二进制日志(binlog)的主从复制机制。主库将所有写操作记录到binlog中,从库通过拉取binlog进行数据同步。

  • 负载均衡:通过中间件或应用程序层逻辑,将读请求路由到从库,写请求路由到主库。


3. 实现方案

3.1 使用中间件(如ShardingSphere、MyCat)

中间件可以自动完成读写分离,开发者无需修改业务代码。

ShardingSphere 示例
  1. 引入依赖:

    <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
        <version>5.1.0</version>
    </dependency>
    
  2. 配置 application.yml​:

    spring:
      shardingsphere:
        datasource:
          names: master,slave1,slave2
          master:
            type: com.zaxxer.hikari.HikariDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            jdbc-url: jdbc:mysql://master-host:3306/db_name
            username: root
            password: root
          slave1:
            type: com.zaxxer.hikari.HikariDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            jdbc-url: jdbc:mysql://slave1-host:3306/db_name
            username: root
            password: root
          slave2:
            type: com.zaxxer.hikari.HikariDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            jdbc-url: jdbc:mysql://slave2-host:3306/db_name
            username: root
            password: root
        rules:
          replica-query:
            data-sources:
              ds:
                primary-data-source-name: master
                replica-data-source-names: slave1,slave2
                load-balancer-name: round_robin
            load-balancers:
              round_robin:
                type: ROUND_ROBIN
    
  3. 在代码中正常使用JPA或MyBatis,ShardingSphere会自动完成读写分离。


3.2 手动实现读写分离

如果不想使用中间件,可以通过自定义注解和动态数据源切换来实现。

实现步骤
  1. 配置多个数据源:
    在 application.yml​ 中配置主库和从库:

    spring:
      datasource:
        master:
          url: jdbc:mysql://master-host:3306/db_name
          username: root
          password: root
        slave:
          url: jdbc:mysql://slave-host:3306/db_name
          username: root
          password: root
    
  2. 创建动态数据源:

    @Configuration
    public class DataSourceConfig {
    
        @Bean(name = "masterDataSource")
        @ConfigurationProperties(prefix = "spring.datasource.master")
        public DataSource masterDataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean(name = "slaveDataSource")
        @ConfigurationProperties(prefix = "spring.datasource.slave")
        public DataSource slaveDataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Primary
        @Bean(name = "dynamicDataSource")
        public DataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
                                            @Qualifier("slaveDataSource") DataSource slaveDataSource) {
            Map<Object, Object> targetDataSources = new HashMap<>();
            targetDataSources.put("master", masterDataSource);
            targetDataSources.put("slave", slaveDataSource);
    
            AbstractRoutingDataSource routingDataSource = new AbstractRoutingDataSource() {
                @Override
                protected Object determineCurrentLookupKey() {
                    return DataSourceContextHolder.getDataSourceType();
                }
            };
            routingDataSource.setDefaultTargetDataSource(masterDataSource);
            routingDataSource.setTargetDataSources(targetDataSources);
            return routingDataSource;
        }
    }
    
  3. 切换数据源上下文:

    public class DataSourceContextHolder {
        private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
    
        public static void setDataSourceType(String type) {
            contextHolder.set(type);
        }
    
        public static String getDataSourceType() {
            return contextHolder.get();
        }
    
        public static void clearDataSourceType() {
            contextHolder.remove();
        }
    }
    
  4. AOP拦截读写操作:

    @Aspect
    @Component
    public class DataSourceAspect {
    
        @Before("@annotation(org.springframework.transaction.annotation.Transactional)")
        public void switchToMaster() {
            DataSourceContextHolder.setDataSourceType("master");
        }
    
        @Before("execution(* com.example.service.*.*(..)) && !@annotation(org.springframework.transaction.annotation.Transactional)")
        public void switchToSlave() {
            DataSourceContextHolder.setDataSourceType("slave");
        }
    
        @After("execution(* com.example.service.*.*(..))")
        public void clearDataSource() {
            DataSourceContextHolder.clearDataSourceType();
        }
    }
    

二、分库分表

1. 什么是分库分表?

分库分表是将一个大表的数据拆分到多个数据库或多个表中,以解决单表数据量过大导致的性能问题。

  • 分库:将数据分布到多个物理数据库中。

  • 分表:将数据分布到多个物理表中。


2. 实现原理

  • 水平拆分:按某种规则(如用户ID、时间戳)将数据分散到不同的库或表中。

  • 垂直拆分:按业务模块将数据分散到不同的库中。


3. 实现方案

3.1 使用中间件(如ShardingSphere)

ShardingSphere 提供了强大的分库分表功能。

ShardingSphere 示例
  1. 引入依赖(同上)。

  2. 配置 application.yml​:

    spring:
      shardingsphere:
        datasource:
          names: ds0,ds1
          ds0:
            type: com.zaxxer.hikari.HikariDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            jdbc-url: jdbc:mysql://host1:3306/db0
            username: root
            password: root
          ds1:
            type: com.zaxxer.hikari.HikariDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            jdbc-url: jdbc:mysql://host2:3306/db1
            username: root
            password: root
        rules:
          sharding:
            tables:
              user:
                actual-data-nodes: ds$->{0..1}.user_$->{0..1}
                table-strategy:
                  standard:
                    sharding-column: user_id
                    sharding-algorithm-name: user-inline
                key-generate-strategy:
                  column: user_id
                  key-generator-name: snowflake
            sharding-algorithms:
              user-inline:
                type: INLINE
                props:
                  algorithm-expression: user_$->{user_id % 2}
            key-generators:
              snowflake:
                type: SNOWFLAKE
    
  3. 在代码中正常使用JPA或MyBatis,ShardingSphere会自动完成分库分表。


3.2 手动实现分库分表

手动实现需要在业务代码中编写分库分表逻辑,复杂度较高,通常不推荐。


三、总结

  • 读写分离:适合解决主库写压力过大的问题,可以通过中间件或手动实现。

  • 分库分表:适合解决单表数据量过大的问题,建议优先使用中间件(如ShardingSphere)。

  • 最佳实践:结合缓存、索引优化等手段,进一步提升系统性能。

希望以上内容对你有所帮助!

0

评论区