MySQL 读写分离

打印 上一主题 下一主题

主题 1016|帖子 1016|积分 3048

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
MySQL 读写分离

一、设置主库(Master)

1.修改主库的设置文件

修改主库的 my.cnf 设置文件,生成二进制日志 (binary log) 和服务器唯一ID,这是实现主从复制的须要设置
  1. [mysqld]
  2. # skip-grant-tables
  3. user=root
  4. port=3306
  5. basedir=/usr/local/mysql
  6. datadir=/data/mysql
  7. socket=/usr/local/mysql/socket/mysql.sock
  8. # Disabling symbolic-links is recommended to prevent assorted security risks
  9. # Settings user and group are ignored when systemd is used.
  10. # If you need to run mysqld under a different user or group,
  11. # customize your systemd unit file for mariadb according to the
  12. # instructions in http://fedoraproject.org/wiki/Systemd
  13. log-error=/usr/local/mysql/logs/mysqld.log
  14. pid_file=/var/run/mysqld/mysqld.pid
  15. symbolic-links=0
  16. # master 配置
  17. server-id=1            # 主库的服务器ID,必须唯一
  18. log-bin=mysqls-bin      # 开启二进制日志,文件名可选
  19. binlog-format=ROW      # (可选,不指定默认 STATEMENT )使用行级复制(推荐)
  20. # 需要重启mysql服务,master配置才会生效
复制代码
2.创建用于复制的用户



  • 登录到Mysql 的主库中,创建一个专门的用户供从库利用,用于复制
  1. # 创建用户
  2. CREATE USER '用户名'@'%' IDENTIFIED BY '用户密码';
  3. e.g
  4. CREATE USER 'replica_user'@'%' IDENTIFIED BY 'replica123456';
  5. # 给用户赋予 '复制' 的权限
  6. GRANT REPLICATION SLAVE ON *.* TO '用户名'@'%';
  7. e.g
  8. GRANT REPLICATION SLAVE ON *.* TO 'replica_user'@'%';
  9. # 这里必须要执行,否则在从库执行 SHOW SLAVE STATUS\G; 时会报错 2061
  10. alter user '用户名'@'%' identified with mysql_native_password by 'mysql数据库登录密码';
  11. e.g
  12. alter user 'replica_user'@'%' identified with mysql_native_password by 'replica123456';
  13. # 刷新权限
  14. FLUSH PRIVILEGES;
复制代码
  Tip
  从库设置启动 复制时报错
  SHOW SLAVE STATUS\G;
  报错:Last_IO_Errno: 2061
Last_IO_Error: error connecting to master ‘replica_user@120.77.27.139:3306’ - rery-time: 60 retries: 1 message: Authentication plugin ‘caching_sha2_password’ reported error: Auhentication requires secure connection.
  3 获取主库的二进制日志位置



  • 锁住主库防止数据变化,获取当前的二进制文件名和位置
  1. # 锁住主库
  2. mysql> FLUSH TABLES WITH READ LOCK;
  3. Query OK, 0 rows affected (0.01 sec)
  4. # 获取当前二进制文件名和位置
  5. mysql> SHOW MASTER STATUS;
  6. +-------------------+----------+--------------+------------------+-------------------+
  7. | File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
  8. +-------------------+----------+--------------+------------------+-------------------+
  9. | mysqls-bin.000001 |      157 |              |                  |                   |
  10. +-------------------+----------+--------------+------------------+-------------------+
  11. 1 row in set (0.00 sec)
  12. # 解锁主库
  13. mysql> UNLOCK TABLES;
  14. Query OK, 0 rows affected (0.00 sec)
复制代码
二、设置从库(Slave)

1.修改从库的设置文件



  • 在从库的MySQL的设置文件 my.cnf 中进行须要的设置,确保其有唯一的服务器ID并启用中继日志
  1. [mysqld]
  2. server-id=2          # 从库的服务器ID,确保唯一
  3. relay-log=relay-bin  # 启用中继日志(用于接收主库的二进制日志)
  4. replicate-do-db=tbb-iov # 指定从主库中需要复制的数据库
  5. read-only=1 # 只读
复制代码
2.毗连到主库并启动复制



  • 登录从库,通过以下设置指定主库的信息并启动复制
  1. CHANGE MASTER TO
  2.   MASTER_HOST='主库的IP地址',           # 主库的IP地址
  3.   MASTER_PORT=3306,  -- 如果主库使用非默认端口,这里需要指定   
  4.   MASTER_USER='replica_user',          # 复制用户
  5.   MASTER_PASSWORD='replica_password',  # 复制用户的密码
  6.   MASTER_LOG_FILE='mysql-bin.000001',  # 主库的二进制日志文件名
  7.   MASTER_LOG_POS=154;                  # 主库的二进制日志位置
  8. e.g
  9. CHANGE MASTER TO
  10.   MASTER_HOST='1x0.xx.xx.13x',
  11.   MASTER_PORT=3306,
  12.   MASTER_USER='replica_user',
  13.   MASTER_PASSWORD='replica123456',
  14.   MASTER_LOG_FILE='mysqls-bin.000001',
  15.   MASTER_LOG_POS=157;
复制代码


  • 启动从库的复制相干命令
  1. # 启动
  2. START SLAVE;
  3. # 停止
  4. STOP SLAVE;
  5. # 查看同步状态
  6. SHOW SLAVE STATUS\G;
复制代码


  • 主从复制状态参数
  1.                Slave_IO_State: Waiting for source to send event
  2.                   Master_Host: xx.xx.xx.xx
  3.                   Master_User: replica_user
  4.                   Master_Port: 3306
  5.                 Connect_Retry: 60
  6.               Master_Log_File: mysqls-bin.000001
  7.           Read_Master_Log_Pos: 7657
  8.                Relay_Log_File: relay-bin.000006
  9.                 Relay_Log_Pos: 2135
  10.         Relay_Master_Log_File: mysqls-bin.000001
  11.              Slave_IO_Running: Yes
  12.             Slave_SQL_Running: Yes
  13.               Replicate_Do_DB: tbb-iov,demo
复制代码
Slave_IO_Running 和 Slave_SQL_Running 必须要为 Yes 才表现成功启动主从复制


  • 查询同步状态的参数阐明
  1. 连接和状态信息
  2. Slave_IO_State: 当前 IO 线程的状态。例如,Waiting for source to send event 表示从库正在等待主库发送事件。
  3. Master_Host: 主库的 IP 地址或主机名。
  4. Master_User: 用于复制的用户名。
  5. Master_Port: 主库的端口号。
  6. Connect_Retry: 从库尝试重新连接到主库的间隔时间(秒)。
  7. Master_Log_File: 当前正在读取的主库二进制日志文件。
  8. Read_Master_Log_Pos: 当前读取的主库二进制日志的位置。
  9. Relay_Log_File: 当前正在使用的中继日志文件。
  10. Relay_Log_Pos: 当前中继日志的位置。
  11. Relay_Master_Log_File: 当前中继日志对应的主库二进制日志文件。
  12. Slave_IO_Running: IO 线程是否正在运行。Yes 表示正在运行,No 表示停止。
  13. Slave_SQL_Running: SQL 线程是否正在运行。Yes 表示正在运行,No 表示停止。
  14. Slave_SQL_Running_State: SQL 线程的当前状态。例如,Replica has read all relay log; waiting for more updates 表示从库已经读取了所有中继日志,正在等待更多的更新。
  15. 复制过滤规则
  16. Replicate_Do_DB: 需要复制的数据库列表。
  17. Replicate_Ignore_DB: 不需要复制的数据库列表。
  18. Replicate_Do_Table: 需要复制的表列表。
  19. Replicate_Ignore_Table: 不需要复制的表列表。
  20. Replicate_Wild_Do_Table: 需要复制的表的通配符模式。
  21. Replicate_Wild_Ignore_Table: 不需要复制的表的通配符模式。
  22. 错误信息
  23. Last_Errno: 最近一次错误的错误码。
  24. Last_Error: 最近一次错误的错误信息。
  25. Skip_Counter: 跳过的错误事务计数器。
  26. Exec_Master_Log_Pos: 当前已执行的主库二进制日志的位置。
  27. Last_IO_Errno: 最近一次 IO 错误的错误码。
  28. Last_IO_Error: 最近一次 IO 错误的错误信息。
  29. Last_SQL_Errno: 最近一次 SQL 错误的错误码。
  30. Last_SQL_Error: 最近一次 SQL 错误的错误信息。
  31. 其他信息
  32. Relay_Log_Space: 中继日志占用的空间大小(字节)。
  33. Until_Condition: 停止复制的条件。
  34. Until_Log_File: 停止复制的日志文件。
  35. Until_Log_Pos: 停止复制的日志位置。
  36. Master_SSL_Allowed: 是否允许 SSL 连接。
  37. Master_SSL_CA_File: SSL 证书颁发机构文件路径。
  38. Master_SSL_CA_Path: SSL 证书颁发机构路径。
  39. Master_SSL_Cert: SSL 证书文件路径。
  40. Master_SSL_Cipher: SSL 密码套件。
  41. Master_SSL_Key: SSL 私钥文件路径。
  42. Seconds_Behind_Master: 从库落后于主库的时间(秒)。
  43. Master_SSL_Verify_Server_Cert: 是否验证主库的 SSL 证书。
  44. Master_Server_Id: 主库的服务器 ID。
  45. Master_UUID: 主库的 UUID。
  46. Master_Info_File: 存储主库信息的文件。
  47. SQL_Delay: SQL 线程延迟时间(秒)。
  48. SQL_Remaining_Delay: 剩余的延迟时间。
  49. Replicate_Ignore_Server_Ids: 不需要复制的服务器 ID 列表。
  50. Master_Retry_Count: 从库尝试重新连接到主库的最大次数。
  51. Master_Bind: 绑定的网络接口。
  52. Last_IO_Error_Timestamp: 最近一次 IO 错误的时间戳。
  53. Last_SQL_Error_Timestamp: 最近一次 SQL 错误的时间戳。
  54. Master_SSL_Crl: SSL 证书吊销列表文件路径。
  55. Master_SSL_Crlpath: SSL 证书吊销列表路径。
  56. Retrieved_Gtid_Set: 已检索的 GTID 集合。
  57. Executed_Gtid_Set: 已执行的 GTID 集合。
  58. Auto_Position: 是否启用自动定位(基于 GTID)。
  59. Replicate_Rewrite_DB: 数据库重写规则。
  60. Channel_Name: 复制通道名称。
  61. Master_TLS_Version: 主库支持的 TLS 版本。
  62. Master_public_key_path: 主库的公钥文件路径。
  63. Get_master_public_key: 是否获取主库的公钥。
  64. Network_Namespace: 网络命名空间。
复制代码
  Tip
  数据库开启主从复制之前,主库和从库的数据需要保持一致,
  三、Spring Boot + MySQL+ Mybatis 主从复制,读写分离

1、Maven 依赖引入

主要依赖如下
  1. <properties>
  2.     <java.version>1.8</java.version>
  3.     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  4.     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  5.     <spring-boot.version>2.6.13</spring-boot.version>
  6.     <mybatis.version>2.2.2</mybatis.version>
  7.     <mysql.version>8.0.30</mysql.version>
  8.     <alibabadruid.version>1.2.16</alibabadruid.version>
  9.     <lombok.version>1.18.26</lombok.version>
  10. </properties>
  11. <dependencies>
  12.     <dependency>
  13.         <groupId>org.springframework.boot</groupId>
  14.         <artifactId>spring-boot-starter</artifactId>
  15.     </dependency>
  16.     <dependency>
  17.         <groupId>org.springframework.boot</groupId>
  18.         <artifactId>spring-boot-starter-test</artifactId>
  19.         <scope>test</scope>
  20.     </dependency>
  21.     <!-- web依赖 -->
  22.     <dependency>
  23.         <groupId>org.springframework.boot</groupId>
  24.         <artifactId>spring-boot-starter-web</artifactId>
  25.     </dependency>
  26.     <!-- aop -->
  27.     <dependency>
  28.         <groupId>org.springframework.boot</groupId>
  29.         <artifactId>spring-boot-starter-aop</artifactId>
  30.     </dependency>
  31.     <!-- mybatis -->
  32.     <dependency>
  33.         <groupId>org.mybatis.spring.boot</groupId>
  34.         <artifactId>mybatis-spring-boot-starter</artifactId>
  35.         <version>${mybatis.version}</version>
  36.     </dependency>
  37.     <!-- mysql -->
  38.     <dependency>
  39.         <groupId>mysql</groupId>
  40.         <artifactId>mysql-connector-java</artifactId>
  41.         <version>${mysql.version}</version>
  42.     </dependency>
  43.     <!-- druid mysql数据库连接池-->
  44.     <dependency>
  45.         <groupId>com.alibaba</groupId>
  46.         <artifactId>druid-spring-boot-starter</artifactId>
  47.         <version>${alibabadruid.version}</version>
  48.     </dependency>
  49.     <!-- lombok 工具 -->
  50.     <dependency>
  51.         <groupId>org.projectlombok</groupId>
  52.         <artifactId>lombok</artifactId>
  53.         <version>${lombok.version}</version>
  54.     </dependency>
  55. </dependencies>
复制代码
2、mysql主从设置

自定义mysql的主从数据源的毗连参数,以及mybatis的设置
  1. server:
  2.   port: 8082
  3. # 自定义mysql配置
  4. mysql:
  5.   datasource:
  6.     master:
  7.       driver-class-name: com.mysql.cj.jdbc.Driver
  8.       url: jdbc:mysql://ip1:port/demo?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
  9.       username: xxx
  10.       password: xxx
  11.       initial-size: 5
  12.       max-active: 20
  13.       min-idle: 5
  14.       max-wait: 60000
  15.       time-between-eviction-runs-millis: 60000
  16.       min-evictable-idle-time-millis: 300000
  17.       max-evictable-idle-time-millis: 900000
  18.       validation-query: SELECT 1 FROM DUAL
  19.       test-while-idle: true
  20.       test-on-borrow: false
  21.       test-on-return: false
  22.       pool-prepared-statements: true
  23.       max-open-prepared-statements: 50
  24.       max-pool-prepared-statement-per-connection-size: 20
  25.       filters: stat,wall
  26.       use-global-data-source-stat: true
  27.       connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
  28.     slave:
  29.       driver-class-name: com.mysql.cj.jdbc.Driver
  30.       url: jdbc:mysql://ip2:port/demo?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
  31.       username: xxxx # 这里的账号最好是只读权限的mysql用户,从库只负责读,不能写入数据
  32.       password: xxxx
  33.       initial-size: 5
  34.       max-active: 25
  35.       min-idle: 5
  36.       max-wait: 60000
  37.       time-between-eviction-runs-millis: 60000
  38.       min-evictable-idle-time-millis: 300000
  39.       max-evictable-idle-time-millis: 900000
  40.       validation-query: SELECT 1 FROM DUAL
  41.       test-while-idle: true
  42.       test-on-borrow: false
  43.       test-on-return: false
  44.       pool-prepared-statements: true
  45.       max-open-prepared-statements: 50
  46.       max-pool-prepared-statement-per-connection-size: 20
  47.       filters: stat,wall
  48.       use-global-data-source-stat: true
  49.       connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
  50. # 指定mapper*.xml加载位置
  51. mybatis:
  52.   config-location: classpath:mybatis/mybatis-config.xml
  53.   mapper-locations: classpath:mybatis/mapper/*.xml
复制代码
3、数据源设置

1. DataSourceConfig 数据源设置类

  1. import com.alibaba.druid.pool.DruidDataSource;
  2. import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
  3. import com.datasource.demo.enums.DataSourceType;
  4. import org.springframework.beans.factory.annotation.Qualifier;
  5. import org.springframework.boot.context.properties.ConfigurationProperties;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.context.annotation.Primary;
  9. import java.util.HashMap;
  10. import java.util.Map;
  11. /**
  12. * ClassName: DataSourceConfig
  13. * Package: com.datasource.demo.config
  14. * Description:
  15. * 数据源配置
  16. *
  17. * @Author wfk
  18. * @Create 2024/11/12 14:15
  19. * @Version 1.0
  20. */
  21. @Configuration
  22. public class DataSourceConfig {
  23.     /**
  24.      * 主库数据源
  25.      *
  26.      * @return
  27.      */
  28.     @Bean("master")
  29.     @ConfigurationProperties(prefix = "mysql.datasource.master")
  30.     public DruidDataSource dataSource1() {
  31.         return DruidDataSourceBuilder.create().build();
  32.     }
  33.     /**
  34.      * 从库数据源
  35.      *
  36.      * @return
  37.      */
  38.     @Bean("slave")
  39.     @ConfigurationProperties(prefix = "mysql.datasource.slave")
  40.     public DruidDataSource dataSource2() {
  41.         return DruidDataSourceBuilder.create().build();
  42.     }
  43.     /**
  44.      * 配置默认数据源
  45.      *
  46.      * @param masterDataSource
  47.      * @param slaveDataSource
  48.      *
  49.      * 必须要加 @Primary 注解,优先下面的配置
  50.      * @return
  51.      */
  52.     @Primary
  53.     @Bean("dynamicDataSource")
  54.     public DynamicDataSource dataSource(@Qualifier("master") DruidDataSource masterDataSource,
  55.                                  @Qualifier("slave") DruidDataSource slaveDataSource) {
  56.         Map<Object, Object> targetDataSources = new HashMap<>();
  57.         targetDataSources.put(DataSourceType.MASTER.getName(), masterDataSource);
  58.         targetDataSources.put(DataSourceType.SLAVE.getName(), slaveDataSource);
  59.         DynamicDataSource dynamicDataSource = new DynamicDataSource();
  60.         dynamicDataSource.setTargetDataSources(targetDataSources);
  61.         dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
  62.         return dynamicDataSource;
  63.     }
  64. }
复制代码
以上源码分析:


  • 采用了阿里云的Druid数据库毗连池,以是需要利用 DruidDataSource
  • @ConfigurationProperties(prefix = “mysql.datasource.slave”) 加载 yml 设置文件的自定义属性,自定义参数名称需要和 druid 的设置的标准名称一样,不然无法主动加载
  • DynamicDataSource 继承了 抽象类 AbstractRoutingDataSource,是实现动态数据源的核心类,将所有数据源注入到这个类中,通过 DataSourceContextHolder 修改数据源,实现动态切换。
  • @Primary 注解必须要加上,标志为优先利用的数据源
2.DataSourceContextHolder 数据源上下文

  1. /**
  2. * ClassName: DataSourceContextHolder
  3. * Package: com.datasource.demo.config.datasource
  4. * Description:
  5. * 本地线程,数据源上下文
  6. * @Author wfk
  7. * @Create 2024/11/12 14:45
  8. * @Version 1.0
  9. */
  10. public class DataSourceContextHolder {
  11.     // 定义一个 ThreadLocal 变量,用于保存当前线程的数据源标识
  12.     private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
  13.     // 设置当前线程的数据源标识
  14.     public static void setDataSource(String dataSource){
  15.         contextHolder.set(dataSource);
  16.     }
  17.     // 获取当前线程的数据源标识
  18.     public static String getDataSource(){
  19.         return contextHolder.get();
  20.     }
  21.     // 清除当前线程的数据源标识
  22.     public static void clearDataSource(){
  23.         contextHolder.remove();
  24.     }
  25. }
复制代码
以上源码分析:


  • ThreadLocal 是一个线程局部变量容器,每个线程都有本身独立的副本。这意味着每个线程都可以独立地设置和获取本身的 ThreadLocal 变量值,而不会影响其他线程。
  • setDataSource(String dataSource) 方法用于设置当火线程的数据源标识。调用这个方法时,传入的数据源标识会被生存在当火线程的 ThreadLocal 变量中。
  • getDataSource() 方法用于获取当火线程的数据源标识。这个方法通常在数据源路由逻辑中被调用,根据当火线程的数据源标识选择合适的数据源进行数据库操作。
  • clearDataSource() 方法用于清除当火线程的数据源标识。这个方法通常在哀求处置处罚完毕后被调用,以开释资源并防止内存泄漏。
3.DynamicDataSource 数据源路由

  1. import lombok.extern.slf4j.Slf4j;
  2. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  3. /**
  4. * ClassName: DynamicDataSource
  5. * Package: com.datasource.demo.config.datasource
  6. * Description:
  7. *
  8. * @Author wfk
  9. * @Create 2024/11/12 14:42
  10. * @Version 1.0
  11. */
  12. @Slf4j
  13. public class DynamicDataSource extends AbstractRoutingDataSource {
  14.     @Override
  15.     protected Object determineCurrentLookupKey() {
  16.         String dataSource = DataSourceContextHolder.getDataSource();
  17.         log.info("当前数据源 {}", dataSource);
  18.         return dataSource;
  19.     }
  20. }
复制代码
以上源码分析:


  • AbstractRoutingDataSource 是 Spring 框架提供的一个抽象类,用于实现数据源的动态切换。它提供了一个 determineCurrentLookupKey 方法,该方法返回一个键值,用于从设置的数据源映射中查找当前应利用的数据源。
  • determineCurrentLookupKey 方法用于确定当火线程应该利用哪个数据源,通过调用DataSourceContextHolder.getDataSource() 来获取当火线程的数据源标识
4.DataSourceType 自定义数据源类别罗列

  1. public enum DataSourceType {
  2.     MASTER("master"),
  3.     SLAVE("slave");
  4.     private String name;
  5.     DataSourceType(String name) {
  6.         this.name = name;
  7.     }
  8.     public String getName() {
  9.         return name;
  10.     }
  11. }
复制代码
4、数据源切换

1.自定义注解实现

自定义注解,作用于方法上,通过AOP切面拦截,根据注解的value所对应的数据库源类别,实现数据源切换。
  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface DBDataSource {
  4.     DataSourceType value() default DataSourceType.MASTER;
  5. }
复制代码
2.AOP切面实现

  1. import com.datasource.demo.annotions.DBDataSource;
  2. import com.datasource.demo.config.datasource.DataSourceContextHolder;
  3. import com.datasource.demo.enums.DataSourceType;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.aspectj.lang.JoinPoint;
  6. import org.aspectj.lang.annotation.Before;
  7. import org.aspectj.lang.annotation.Pointcut;
  8. import org.aspectj.lang.reflect.MethodSignature;
  9. import org.springframework.stereotype.Component;
  10. import org.aspectj.lang.annotation.Aspect;
  11. import java.lang.reflect.Method;
  12. /**
  13. * ClassName: DataSourceAspect
  14. * Package: com.datasource.demo.aspect
  15. * Description:
  16. * 数据源AOP切面
  17. *
  18. * @Author wfk
  19. * @Create 2024/11/12 16:05
  20. * @Version 1.0
  21. */
  22. @Slf4j
  23. @Aspect
  24. @Component
  25. public class DataSourceAspect {
  26.     @Pointcut("execution(* com.datasource.demo.service..*.*(..))")
  27.     public void aspect() {
  28.     }
  29.     @Before("aspect()")
  30.     private void doBefore(JoinPoint joinPoint) {
  31.         Object target = joinPoint.getTarget();
  32.         Class<?> clazz = target.getClass();
  33.         // 获取方法签名
  34.         MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
  35.         // 获取方法名称
  36.         String methodName = methodSignature.getName();
  37.         // 获取方法参数列表
  38.         Class<?>[] parameterTypes = methodSignature.getMethod().getParameterTypes();
  39.         try {
  40.             // 通过方法名称和参数列表可以唯一获取方法(可能有重载的同名方法)
  41.             Method method = clazz.getMethod(methodName, parameterTypes);
  42.             // 判断方法是否存在指定的注解
  43.             if (method !=null && method.isAnnotationPresent(DBDataSource.class)){
  44.                 DBDataSource annotation = method.getAnnotation(DBDataSource.class);
  45.                 // 如果是从库那就切换当前线程的数据源
  46.                 if (DataSourceType.SLAVE == annotation.value()) {
  47.                     DataSourceContextHolder.setDataSource(DataSourceType.SLAVE.getName());
  48.                     return;
  49.                 }
  50.             }
  51.         } catch (NoSuchMethodException e) {
  52.             e.printStackTrace();
  53.         }
  54.         // 默认是使用主库
  55.         DataSourceContextHolder.setDataSource(DataSourceType.MASTER.getName());
  56.     }
  57. }
复制代码
以上源码分析:
1.@Pointcut("execution( com.datasource.demo.service….(…))")*


  • execution():这是定义切入点表达式的关键字,用来匹配Java方法的执行毗连点。
  • *:第一个星号表现返回值类型,这里的星号意味着匹配任何返回类型的方法。‘com.datasource.demo.service…*.*(…):这部分是切入点表达式的主体,指定了要匹配的方法的位置和名称。

    • com.datasource.demo.service:这是包名,指明了要匹配的方法地点的包。
    • ..:两个点号表现该包下的所有子包。
    • *.*:第一个星号代表类名,第二个星号代表方法名,这里利用两个星号表现匹配该包及其子包下所有类的所有方法。
    • (..):括号内的两个点号表现参数列表,这里表现匹配任何参数列表的方法。

2.方法调用和注解查抄


  • 获取目标对象和方法签名。
  • 通过反射获取方法对象,并查抄方法是否标注了 DBDataSource 注解。
  • 假如方法标注了 DBDataSource 注解且注解值为 DataSourceType.SLAVE,则设置当火线程的数据源为从库。
  • 否则,默认设置当火线程的数据源为主库。
3.最终实现如下

在service层的实现类中,添加注解,查询相干的业务,通过注解指定从库,写入数据默认利用主库,实现读写分离。
  1. @DBDataSource(DataSourceType.SLAVE)
  2. @Override
  3. public List<BookInfoPO> getBookInfoList() {
  4.     return bookInfoMapper.getBookInfoListMapper();
  5. }
复制代码
总结

关于数据源切换另有很多种方式


  • 在mapper层做拦截,对insert、update、delete 和 select 完全分离,可以通过MyCat、shardingsphere 等数据库中间件实现主动分离。
  • 也可以通过规范方法名前缀,对get、find、query 开头等方法进行拦截,也可以实现读写分离。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

盛世宏图

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表