最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot+MyBatis-Plus+Dynamic-Datasource讀寫(xiě)分離完整指南

 更新時(shí)間:2025年11月12日 09:04:54   作者:IT橘子皮  
在SpringBoot項(xiàng)目中,使用MyBatis-Plus和Dynamic-Datasource實(shí)現(xiàn)讀寫(xiě)分離是一種常見(jiàn)且高效的架構(gòu)選擇,下面就來(lái)詳細(xì)介紹一下SpringBoot讀寫(xiě)分離,感興趣的可以了解一下

在SpringBoot項(xiàng)目中,使用MyBatis-Plus和Dynamic-Datasource實(shí)現(xiàn)讀寫(xiě)分離是一種常見(jiàn)且高效的架構(gòu)選擇。下面我將為你詳細(xì)講解完整的實(shí)現(xiàn)方案,包括環(huán)境準(zhǔn)備、配置、代碼實(shí)現(xiàn)以及注意事項(xiàng)。

1. 讀寫(xiě)分離概述與核心概念

讀寫(xiě)分離是一種常見(jiàn)的數(shù)據(jù)庫(kù)優(yōu)化方案,其核心思想是將數(shù)據(jù)庫(kù)的寫(xiě)操作(INSERT、UPDATE、DELETE)和讀操作(SELECT)分發(fā)到不同的數(shù)據(jù)庫(kù)節(jié)點(diǎn)上。主數(shù)據(jù)庫(kù)(Master)負(fù)責(zé)處理所有寫(xiě)操作,而從數(shù)據(jù)庫(kù)(Slave)負(fù)責(zé)處理讀操作,通過(guò)數(shù)據(jù)庫(kù)的主從復(fù)制機(jī)制保持?jǐn)?shù)據(jù)同步。

1.1 核心價(jià)值

  • 提升并發(fā)性能:將讀請(qǐng)求分散到多個(gè)從庫(kù),減輕主庫(kù)壓力
  • 提高系統(tǒng)可用性:?jiǎn)蝹€(gè)從庫(kù)故障不影響讀服務(wù)
  • 優(yōu)化響應(yīng)速度:專(zhuān)庫(kù)專(zhuān)用,避免讀寫(xiě)操作相互阻塞

1.2 技術(shù)架構(gòu)

應(yīng)用程序 → Dynamic-Datasource → 主庫(kù)(寫(xiě)操作)
                     ↓
                   從庫(kù)(讀操作)

2. 環(huán)境準(zhǔn)備與依賴配置

2.1 添加Maven依賴

首先在項(xiàng)目的pom.xml中添加必要的依賴:

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- MyBatis-Plus 啟動(dòng)器 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.2</version>
    </dependency>
    
    <!-- Dynamic-Datasource 啟動(dòng)器(核心依賴) -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        <version>3.6.1</version>
    </dependency>
    
    <!-- MySQL 驅(qū)動(dòng) -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
    
    <!-- 連接池(可選,HikariCP已內(nèi)置) -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.16</version>
    </dependency>
</dependencies>

2.2 配置文件設(shè)置

application.yml中配置主從數(shù)據(jù)源:

spring:
  datasource:
    dynamic:
      primary: master  # 設(shè)置默認(rèn)數(shù)據(jù)源為主庫(kù)
      strict: false   # 是否嚴(yán)格匹配數(shù)據(jù)源,false時(shí)未匹配到指定數(shù)據(jù)源則使用默認(rèn)數(shù)據(jù)源
      datasource:
        # 主庫(kù)配置(寫(xiě)操作)
        master:
          url: jdbc:mysql://master-host:3306/core?useSSL=false&serverTimezone=Asia/Shanghai
          username: admin
          password: master@123
          driver-class-name: com.mysql.cj.jdbc.Driver
        # 從庫(kù)配置(讀操作)- 支持多個(gè)從庫(kù)
        slave1:
          url: jdbc:mysql://slave1-host:3306/core?useSSL=false&serverTimezone=Asia/Shanghai
          username: readonly
          password: slave@123
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave2:
          url: jdbc:mysql://slave2-host:3306/core?useSSL=false&serverTimezone=Asia/Shanghai
          username: readonly
          password: slave@123
          driver-class-name: com.mysql.cj.jdbc.Driver
      
      # 負(fù)載均衡策略配置(多個(gè)從庫(kù)時(shí)生效)
      strategy: 
        slave: round_robin  # 從庫(kù)負(fù)載均衡策略:random(隨機(jī))/round_robin(輪詢)
      
      # 連接池配置(HikariCP)
      hikari:
        max-pool-size: 20      # 最大連接數(shù)
        min-idle: 5           # 最小空閑連接
        connection-timeout: 30000  # 連接超時(shí)時(shí)間(ms)
        idle-timeout: 600000  # 空閑連接超時(shí)時(shí)間(ms)
        max-lifetime: 1800000 # 連接最大生命周期(ms)

# MyBatis-Plus 配置
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true  # 開(kāi)啟駝峰命名轉(zhuǎn)換
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 打印SQL日志(調(diào)試用)
  global-config:
    db-config:
      id-type: auto  # 主鍵策略自增

3. 數(shù)據(jù)源與MyBatis-Plus配置類(lèi)

3.1 動(dòng)態(tài)數(shù)據(jù)源自動(dòng)配置

Dynamic-Datasource starter已經(jīng)提供了自動(dòng)配置,大多數(shù)情況下無(wú)需額外配置。但如果需要自定義,可以創(chuàng)建配置類(lèi):

@Configuration
@MapperScan("com.example.mapper")  // 指定MyBatis mapper接口的掃描路徑
public class DataSourceConfig {
    
    /**
     * 配置動(dòng)態(tài)數(shù)據(jù)源
     * Dynamic-Datasource會(huì)自動(dòng)根據(jù)application.yml的配置創(chuàng)建數(shù)據(jù)源
     * 此處可以添加自定義配置
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.dynamic")
    public DynamicDataSourceProperties dynamicDataSourceProperties() {
        return new DynamicDataSourceProperties();
    }
    
    /**
     * 配置MyBatis-Plus攔截器(分頁(yè)插件等)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分頁(yè)插件
        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        paginationInterceptor.setMaxLimit(1000L);  // 設(shè)置最大分頁(yè)限制
        interceptor.addInnerInterceptor(paginationInterceptor);
        
        // 可以添加其他插件,如樂(lè)觀鎖插件等
        return interceptor;
    }
}

4. 業(yè)務(wù)層實(shí)現(xiàn)讀寫(xiě)分離

4.1 使用@DS注解手動(dòng)切換數(shù)據(jù)源

最直接的方式是在Service層使用@DS注解指定數(shù)據(jù)源:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    /**
     * 寫(xiě)操作:使用@DS("master")指定主庫(kù)
     * 注意:寫(xiě)操作建議都使用主庫(kù),確保數(shù)據(jù)一致性
     */
    @DS("master")  // 指定使用主庫(kù)
    @Transactional(rollbackFor = Exception.class)  // 添加事務(wù)管理
    @Override
    public boolean createUser(User user) {
        // 參數(shù)校驗(yàn)
        if (user == null || StringUtils.isBlank(user.getUsername())) {
            throw new IllegalArgumentException("用戶信息不完整");
        }
        
        // 設(shè)置創(chuàng)建時(shí)間
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        
        // 執(zhí)行插入操作,這里會(huì)使用主庫(kù)
        boolean result = save(user);
        
        // 這里可以添加其他業(yè)務(wù)邏輯
        log.info("創(chuàng)建用戶成功,用戶ID: {}", user.getId());
        
        return result;
    }
    
    /**
     * 讀操作:使用@DS("slave")指定從庫(kù)
     * 框架會(huì)根據(jù)負(fù)載均衡策略選擇slave1或slave2
     */
    @DS("slave")  // 指定使用從庫(kù)(負(fù)載均衡)
    @Override
    public User getUserById(Long id) {
        // 參數(shù)校驗(yàn)
        if (id == null || id <= 0) {
            throw new IllegalArgumentException("用戶ID不合法");
        }
        
        // 查詢用戶信息,這里會(huì)使用從庫(kù)
        User user = getById(id);
        
        if (user == null) {
            log.warn("未找到對(duì)應(yīng)用戶,用戶ID: {}", id);
            throw new RuntimeException("用戶不存在");
        }
        
        return user;
    }
    
    /**
     * 批量查詢:同樣使用從庫(kù)
     */
    @DS("slave")
    @Override
    public List<User> listUsersByCondition(UserQuery query) {
        // 構(gòu)建查詢條件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        
        if (StringUtils.isNotBlank(query.getUsername())) {
            wrapper.like(User::getUsername, query.getUsername());
        }
        
        if (query.getStatus() != null) {
            wrapper.eq(User::getStatus, query.getStatus());
        }
        
        // 添加排序
        wrapper.orderByDesc(User::getCreateTime);
        
        // 執(zhí)行查詢,使用從庫(kù)
        return list(wrapper);
    }
    
    /**
     * 更新操作:必須使用主庫(kù)
     */
    @DS("master")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateUser(User user) {
        if (user == null || user.getId() == null) {
            throw new IllegalArgumentException("用戶信息不完整");
        }
        
        // 設(shè)置更新時(shí)間
        user.setUpdateTime(LocalDateTime.now());
        
        // 執(zhí)行更新,使用主庫(kù)
        boolean result = updateById(user);
        
        if (result) {
            log.info("更新用戶成功,用戶ID: {}", user.getId());
        } else {
            log.error("更新用戶失敗,用戶ID: {}", user.getId());
        }
        
        return result;
    }
    
    /**
     * 刪除操作:必須使用主庫(kù)
     */
    @DS("master")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteUser(Long id) {
        if (id == null || id <= 0) {
            throw new IllegalArgumentException("用戶ID不合法");
        }
        
        // 執(zhí)行刪除,使用主庫(kù)
        boolean result = removeById(id);
        
        if (result) {
            log.info("刪除用戶成功,用戶ID: {}", id);
        } else {
            log.warn("刪除用戶失敗,用戶ID: {}", id);
        }
        
        return result;
    }
}

4.2 基于AOP的自動(dòng)數(shù)據(jù)源切換(推薦)

對(duì)于大型項(xiàng)目,手動(dòng)添加@DS注解比較繁瑣,可以通過(guò)AOP自動(dòng)根據(jù)方法類(lèi)型切換數(shù)據(jù)源:

@Aspect
@Component
@Slf4j
public class DataSourceAspect {
    
    /**
     * 定義切點(diǎn):攔截Service層的所有方法
     */
    @Pointcut("execution(* com.example.service..*.*(..))")
    public void servicePointcut() {}
    
    /**
     * 前置通知:在方法執(zhí)行前選擇數(shù)據(jù)源
     */
    @Before("servicePointcut()")
    public void before(JoinPoint joinPoint) {
        // 獲取方法名
        String methodName = joinPoint.getSignature().getName();
        
        // 根據(jù)方法名前綴判斷是讀操作還是寫(xiě)操作
        if (isReadOperation(methodName)) {
            // 讀操作使用從庫(kù)
            DynamicDataSourceContextHolder.push("slave");
            log.debug("切換數(shù)據(jù)源到從庫(kù),方法名: {}", methodName);
        } else {
            // 寫(xiě)操作使用主庫(kù)
            DynamicDataSourceContextHolder.push("master");
            log.debug("切換數(shù)據(jù)源到主庫(kù),方法名: {}", methodName);
        }
    }
    
    /**
     * 后置通知:清理數(shù)據(jù)源上下文
     */
    @After("servicePointcut()")
    public void after() {
        DynamicDataSourceContextHolder.clear();
        log.debug("清除數(shù)據(jù)源上下文");
    }
    
    /**
     * 判斷是否為讀操作
     * 可以根據(jù)方法名前綴進(jìn)行判斷
     */
    private boolean isReadOperation(String methodName) {
        // 常見(jiàn)的讀操作方法前綴
        String[] readPrefixes = {"get", "select", "list", "query", "find", "search", "count", "check"};
        
        for (String prefix : readPrefixes) {
            if (methodName.startsWith(prefix)) {
                return true;
            }
        }
        return false;
    }
}

使用AOP后,Service層的代碼可以簡(jiǎn)化為:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean createUser(User user) {
        // 自動(dòng)使用主庫(kù)(AOP根據(jù)方法名判斷)
        user.setCreateTime(LocalDateTime.now());
        return save(user);
    }
    
    @Override
    public User getUserById(Long id) {
        // 自動(dòng)使用從庫(kù)(AOP根據(jù)方法名前綴"get"判斷)
        return getById(id);
    }
    
    // 其他方法無(wú)需添加@DS注解,由AOP自動(dòng)處理
}

5. 事務(wù)處理策略

事務(wù)處理是讀寫(xiě)分離中的關(guān)鍵問(wèn)題,需要特別注意:

5.1 單數(shù)據(jù)源事務(wù)

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    
    /**
     * 單數(shù)據(jù)源事務(wù):所有操作都在主庫(kù)執(zhí)行
     * 使用@DS("master")確保即使有AOP配置也使用主庫(kù)
     */
    @DS("master")  // 顯式指定主庫(kù)
    @Transactional(rollbackFor = Exception.class)  // 聲明式事務(wù)
    @Override
    public void createOrder(Order order) {
        // 1. 保存訂單主信息(主庫(kù))
        save(order);
        
        // 2. 更新庫(kù)存(主庫(kù))
        updateStock(order);
        
        // 3. 記錄操作日志(主庫(kù))
        logOrderOperation(order);
        
        // 如果任何一步失敗,整個(gè)事務(wù)回滾
    }
    
    /**
     * 只讀事務(wù):可以指定從庫(kù)
     */
    @DS("slave")
    @Transactional(readOnly = true)  // 只讀事務(wù),有優(yōu)化效果
    @Override
    public Order getOrderDetail(Long orderId) {
        return getById(orderId);
    }
}

5.2 多數(shù)據(jù)源事務(wù)處理

對(duì)于涉及多個(gè)數(shù)據(jù)源的復(fù)雜事務(wù),需要使用分布式事務(wù)解決方案:

@Service
@Slf4j
public class DistributedTransactionService {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private OrderService orderService;
    
    /**
     * 使用@DSTransactional實(shí)現(xiàn)多數(shù)據(jù)源分布式事務(wù)
     * 注意:需要集成Seata等分布式事務(wù)框架
     */
    // @DSTransactional  // 分布式事務(wù)注解(需要額外配置)
    @Transactional(rollbackFor = Exception.class)
    public void placeOrder(Order order, User user) {
        try {
            // 1. 用戶操作(主庫(kù))
            userService.updateUser(user);
            
            // 2. 訂單操作(主庫(kù))
            orderService.createOrder(order);
            
            // 模擬業(yè)務(wù)異常
            if (order.getAmount() == null) {
                throw new RuntimeException("訂單金額不能為空");
            }
            
            log.info("下單成功");
        } catch (Exception e) {
            log.error("下單失敗,已回滾", e);
            throw e;  // 拋出異常觸發(fā)回滾
        }
    }
}

6. 高級(jí)功能與優(yōu)化策略

6.1 多從庫(kù)負(fù)載均衡

當(dāng)配置多個(gè)從庫(kù)時(shí),Dynamic-Datasource支持多種負(fù)載均衡策略:

spring:
  datasource:
    dynamic:
      datasource:
        master:
          # 主庫(kù)配置...
        slave1:
          # 從庫(kù)1配置...
        slave2:
          # 從庫(kù)2配置...
        slave3:
          # 從庫(kù)3配置...
      strategy:
        slave: round_robin  # 負(fù)載均衡策略
        # 可選值:
        # random - 隨機(jī)選擇(默認(rèn))
        # round_robin - 輪詢
        # weight_round_robin - 加權(quán)輪詢(需要額外配置權(quán)重)

負(fù)載均衡策略對(duì)比:

策略類(lèi)型描述適用場(chǎng)景
random隨機(jī)選擇從庫(kù)簡(jiǎn)單的讀負(fù)載均衡
round_robin輪詢選擇從庫(kù)從庫(kù)配置相近,需要均勻分布
weight_round_robin根據(jù)權(quán)重選擇從庫(kù)配置不同,按性能分配負(fù)載

6.2 健康檢查與故障轉(zhuǎn)移

@Component
@Slf4j
public class DataSourceHealthCheck {
    
    @Autowired
    private DataSource dataSource;
    
    /**
     * 定時(shí)檢查從庫(kù)健康狀態(tài)
     */
    @Scheduled(fixedRate = 60000)  // 每分鐘執(zhí)行一次
    public void checkSlaveHealth() {
        // 這里可以實(shí)現(xiàn)從庫(kù)健康檢查邏輯
        // 如果某個(gè)從庫(kù)不可用,可以動(dòng)態(tài)將其從負(fù)載均衡池中移除
        
        log.info("執(zhí)行從庫(kù)健康檢查...");
        // 實(shí)際實(shí)現(xiàn)中可以使用JDBC測(cè)試連接或查詢數(shù)據(jù)庫(kù)狀態(tài)
    }
    
    /**
     * 獲取從庫(kù)同步延遲
     */
    public Long getReplicationDelay(String slaveName) {
        // 執(zhí)行SQL查詢從庫(kù)同步狀態(tài)
        // SHOW SLAVE STATUS 可以獲取Seconds_Behind_Master等信息
        // 如果延遲過(guò)大,可以臨時(shí)將該從庫(kù)標(biāo)記為不可用
        
        return 0L;  // 返回延遲秒數(shù)
    }
}

7. 測(cè)試與驗(yàn)證

7.1 單元測(cè)試

@SpringBootTest
@Slf4j
class ReadWriteSeparationTest {
    
    @Autowired
    private UserService userService;
    
    /**
     * 測(cè)試寫(xiě)操作(應(yīng)該路由到主庫(kù))
     */
    @Test
    void testWriteOperation() {
        User user = new User();
        user.setUsername("testUser");
        user.setEmail("test@example.com");
        
        boolean result = userService.createUser(user);
        
        assertTrue(result);
        log.info("寫(xiě)操作測(cè)試通過(guò),應(yīng)路由到主庫(kù)");
    }
    
    /**
     * 測(cè)試讀操作(應(yīng)該路由到從庫(kù))
     */
    @Test
    void testReadOperation() {
        User user = userService.getUserById(1L);
        
        assertNotNull(user);
        log.info("讀操作測(cè)試通過(guò),應(yīng)路由到從庫(kù)");
    }
    
    /**
     * 測(cè)試事務(wù)內(nèi)的數(shù)據(jù)源選擇
     */
    @Test
    void testTransactionalOperation() {
        // 測(cè)試事務(wù)方法,應(yīng)始終使用主庫(kù)
        User user = userService.getUserById(1L);
        assertNotNull(user);
        
        log.info("事務(wù)內(nèi)操作測(cè)試完成");
    }
}

7.2 驗(yàn)證數(shù)據(jù)源路由

application.yml中開(kāi)啟SQL日志,驗(yàn)證讀寫(xiě)分離是否生效:

# 開(kāi)啟MyBatis SQL日志
logging:
  level:
    com.example.mapper: debug  # Mapper接口的包路徑
    com.baomidou.dynamic.datasource: debug  # Dynamic-Datasource日志

觀察控制臺(tái)輸出,應(yīng)該能看到類(lèi)似日志:

2025-11-11 10:00:00 | Master DataSource  | INSERT INTO user ...
2025-11-11 10:00:05 | Slave DataSource   | SELECT * FROM user ...

8. 生產(chǎn)環(huán)境注意事項(xiàng)

8.1 主從同步延遲處理

@Service
public class CriticalReadService {
    
    @Autowired
    private UserMapper userMapper;
    
    /**
     * 對(duì)于一致性要求高的讀操作,強(qiáng)制走主庫(kù)
     */
    @DS("master")  // 強(qiáng)制使用主庫(kù),避免讀取舊數(shù)據(jù)
    public User getCriticalUserInfo(Long userId) {
        // 例如:賬戶余額、訂單狀態(tài)等關(guān)鍵信息
        return userMapper.selectById(userId);
    }
    
    /**
     * 檢查數(shù)據(jù)是否已同步
     */
    public boolean waitForReplication(Long userId, int maxWaitSeconds) {
        for (int i = 0; i < maxWaitSeconds; i++) {
            User masterUser = getFromMaster(userId);
            User slaveUser = getFromSlave(userId);
            
            if (Objects.equals(masterUser, slaveUser)) {
                return true;  // 數(shù)據(jù)已同步
            }
            
            try {
                Thread.sleep(1000);  // 等待1秒
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
        return false;  // 同步超時(shí)
    }
}

8.2 監(jiān)控與告警

# 連接池監(jiān)控配置
spring:
  datasource:
    dynamic:
      druid:
        # 開(kāi)啟監(jiān)控統(tǒng)計(jì)
        filters: stat,wall,log4j
        web-stat-filter:
          enabled: true
        stat-view-servlet:
          enabled: true
          url-pattern: /druid/*
          reset-enable: false
          login-username: admin
          login-password: admin

9. 常見(jiàn)問(wèn)題與解決方案

9.1 事務(wù)內(nèi)數(shù)據(jù)源切換失效

問(wèn)題:在@Transactional方法內(nèi),數(shù)據(jù)源在方法開(kāi)始時(shí)確定,方法內(nèi)部調(diào)用無(wú)法切換數(shù)據(jù)源。

解決方案

@Service
public class TransactionalService {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    @Transactional
    public void transactionalMethod() {
        // 方法內(nèi)需要切換數(shù)據(jù)源時(shí),通過(guò)代理對(duì)象調(diào)用
        TransactionalService self = applicationContext.getBean(TransactionalService.class);
        
        // 通過(guò)代理對(duì)象調(diào)用,可以正常切換數(shù)據(jù)源
        self.readOperation();  // 使用從庫(kù)
        self.writeOperation();  // 使用主庫(kù)
    }
    
    @DS("slave")
    public void readOperation() {
        // 讀操作
    }
    
    @DS("master")
    public void writeOperation() {
        // 寫(xiě)操作
    }
}

9.2 動(dòng)態(tài)增減數(shù)據(jù)源

@Service
public class DynamicDataSourceService {
    
    @Autowired
    private DynamicDataSourceProvider dataSourceProvider;
    
    /**
     * 運(yùn)行時(shí)動(dòng)態(tài)添加數(shù)據(jù)源
     */
    public void addDataSource(String dataSourceName, DataSourceProperty property) {
        DynamicDataSourceContextHolder.addDataSource(dataSourceName, property);
        log.info("動(dòng)態(tài)添加數(shù)據(jù)源: {}", dataSourceName);
    }
    
    /**
     * 運(yùn)行時(shí)移除數(shù)據(jù)源
     */
    public void removeDataSource(String dataSourceName) {
        DynamicDataSourceContextHolder.removeDataSource(dataSourceName);
        log.info("動(dòng)態(tài)移除數(shù)據(jù)源: {}", dataSourceName);
    }
}

總結(jié)

通過(guò)SpringBoot + MyBatis-Plus + Dynamic-Datasource實(shí)現(xiàn)讀寫(xiě)分離,可以顯著提升數(shù)據(jù)庫(kù)讀性能和高可用性。關(guān)鍵點(diǎn)包括:

  1. 正確配置:確保主從數(shù)據(jù)源配置正確,負(fù)載均衡策略合理
  2. 合理使用注解:在適當(dāng)?shù)姆椒ㄉ鲜褂?code>@DS注解或通過(guò)AOP自動(dòng)切換
  3. 事務(wù)管理:注意事務(wù)內(nèi)的數(shù)據(jù)源行為,關(guān)鍵操作強(qiáng)制走主庫(kù)
  4. 監(jiān)控告警:建立完善的監(jiān)控體系,及時(shí)發(fā)現(xiàn)主從延遲等問(wèn)題
  5. 故障處理:實(shí)現(xiàn)從庫(kù)故障自動(dòng)轉(zhuǎn)移和恢復(fù)機(jī)制

這種架構(gòu)在讀寫(xiě)比例高的應(yīng)用中能帶來(lái)顯著的性能提升,但也需要處理好主從同步延遲等一致性問(wèn)題。

到此這篇關(guān)于SpringBoot+MyBatis-Plus+Dynamic-Datasource讀寫(xiě)分離完整指南的文章就介紹到這了,更多相關(guān)SpringBoot MyBatis-Plus 讀寫(xiě)分離內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java使用J4L識(shí)別驗(yàn)證碼的操作方法

    Java使用J4L識(shí)別驗(yàn)證碼的操作方法

    這篇文章主要介紹了Java使用J4L識(shí)別驗(yàn)證碼的操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • jdk8?FunctionalInterface注解源碼解讀

    jdk8?FunctionalInterface注解源碼解讀

    這篇文章主要介紹了jdk8?FunctionalInterface注解源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • Java實(shí)現(xiàn)批量修改文件名和重命名的方法

    Java實(shí)現(xiàn)批量修改文件名和重命名的方法

    這篇文章主要介紹了Java實(shí)現(xiàn)批量修改文件名和重命名的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java的Hibernate框架中的組合映射學(xué)習(xí)教程

    Java的Hibernate框架中的組合映射學(xué)習(xí)教程

    組合映射即是指主對(duì)象和子對(duì)象關(guān)聯(lián)且擁有相同的生命周期的映射關(guān)系,這里我們將舉一些數(shù)據(jù)操作的實(shí)例,來(lái)講解Java的Hibernate框架中的組合映射學(xué)習(xí)教程
    2016-07-07
  • Spring學(xué)習(xí)筆記之RedisTemplate的配置與使用教程

    Spring學(xué)習(xí)筆記之RedisTemplate的配置與使用教程

    這篇文章主要給大家介紹了關(guān)于Spring學(xué)習(xí)筆記之RedisTemplate配置與使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用spring具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-06-06
  • 舉例講解Java中synchronized關(guān)鍵字的用法

    舉例講解Java中synchronized關(guān)鍵字的用法

    這篇文章主要介紹了Java中synchronized關(guān)鍵字的用法,針對(duì)synchronized修飾方法的使用作出了簡(jiǎn)單講解和演示,需要的朋友可以參考下
    2016-04-04
  • Mybatis動(dòng)態(tài)sql超詳細(xì)講解

    Mybatis動(dòng)態(tài)sql超詳細(xì)講解

    動(dòng)態(tài)SQL是MyBatis的強(qiáng)大特性之一,顧名思義就是會(huì)動(dòng)的SQL,即是能夠靈活的根據(jù)某種條件拼接出完整的SQL語(yǔ)句,下面這篇文章主要給大家介紹了關(guān)于Mybatis動(dòng)態(tài)sql的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • SpringBoot接口防重復(fù)提交的三種解決方案

    SpringBoot接口防重復(fù)提交的三種解決方案

    在Web開(kāi)發(fā)中,防止用戶重復(fù)提交表單是一個(gè)常見(jiàn)的需求,用戶可能會(huì)因?yàn)榫W(wǎng)絡(luò)延遲、誤操作等原因多次點(diǎn)擊提交按鈕,導(dǎo)致后臺(tái)接收到多個(gè)相同的請(qǐng)求,本文將介紹幾種在Spring Boot中實(shí)現(xiàn)接口防重復(fù)提交的方法,需要的朋友可以參考下
    2024-11-11
  • java 注解的基礎(chǔ)詳細(xì)介紹

    java 注解的基礎(chǔ)詳細(xì)介紹

    這篇文章主要介紹了java 注解的基礎(chǔ)詳細(xì)介紹的相關(guān)資料,希望通過(guò)本文大家能掌握注解的使用方法,需要的朋友可以參考下
    2017-09-09
  • Java多線程 線程同步與死鎖

    Java多線程 線程同步與死鎖

    這篇文章主要介紹了 Java多線程 線程同步與死鎖的相關(guān)資料,需要的朋友可以參考下
    2017-07-07

最新評(píng)論

富顺县| 保德县| 鄂伦春自治旗| 仁布县| 舒兰市| 高安市| 老河口市| 诸城市| 津南区| 富宁县| 阳谷县| 股票| 黄梅县| 化州市| 衡阳市| 旬邑县| 呼伦贝尔市| 岳阳市| 绩溪县| 沙河市| 堆龙德庆县| 搜索| 尼玛县| 兴海县| 石阡县| 绥滨县| 昌都县| 乌海市| 马尔康县| 日照市| 额济纳旗| 开远市| 北票市| 邓州市| 错那县| 福建省| 卓尼县| 南丰县| 板桥市| 盐边县| 新竹市|