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

SpringBoot3實(shí)現(xiàn)MySQL讀寫分離的完整指南

 更新時(shí)間:2025年11月12日 08:35:00   作者:IT橘子皮  
在高并發(fā)場(chǎng)景下,數(shù)據(jù)庫往往成為系統(tǒng)瓶頸,讀寫分離通過將寫操作定向到主庫、讀操作分發(fā)到從庫,顯著提升系統(tǒng)讀性能和數(shù)據(jù)可用性,本文給大家介紹了SpringBoot3實(shí)現(xiàn)MySQL讀寫分離的完整指南,需要的朋友可以參考下

讀寫分離的核心價(jià)值

在高并發(fā)場(chǎng)景下,數(shù)據(jù)庫往往成為系統(tǒng)瓶頸。讀寫分離通過將寫操作定向到主庫、讀操作分發(fā)到從庫,顯著提升系統(tǒng)讀性能和數(shù)據(jù)可用性。當(dāng)主庫出現(xiàn)故障時(shí),從庫可以繼續(xù)提供讀服務(wù),提高系統(tǒng)的穩(wěn)定性。

項(xiàng)目依賴配置

首先在pom.xml中添加必要依賴:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
    </dependency>
</dependencies>

核心實(shí)現(xiàn)代碼詳解

1. 配置文件設(shè)置(application.yml)

spring:
  datasource:
    # 主庫配置(寫操作)
    master:
      jdbc-url: jdbc:mysql://localhost:3306/master_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
      username: root
      password: master_password
      driver-class-name: com.mysql.cj.jdbc.Driver
      # Hikari連接池配置
      hikari:
        maximum-pool-size: 10
        minimum-idle: 5
        idle-timeout: 30000
        max-lifetime: 1800000
        connection-timeout: 30000
        connection-test-query: SELECT 1
    # 從庫配置(讀操作)
    slave:
      jdbc-url: jdbc:mysql://localhost:3306/slave_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
      username: root
      password: slave_password
      driver-class-name: com.mysql.cj.jdbc.Driver
      hikari:
        maximum-pool-size: 15  # 從庫可以配置更多連接,因?yàn)樽x操作通常更頻繁
        minimum-idle: 8
        idle-timeout: 30000
        max-lifetime: 1800000
        connection-timeout: 30000
        connection-test-query: SELECT 1

2. 數(shù)據(jù)源枚舉定義

/**
 * 數(shù)據(jù)源類型枚舉
 * 用于標(biāo)識(shí)當(dāng)前操作應(yīng)使用主庫還是從庫
 */
public enum DataSourceType {
    MASTER,  // 主庫:用于寫操作(INSERT、UPDATE、DELETE)
    SLAVE    // 從庫:用于讀操作(SELECT)
}

3. 數(shù)據(jù)源上下文管理器

/**
 * 數(shù)據(jù)源上下文管理器(基于ThreadLocal實(shí)現(xiàn)線程隔離)
 * 功能:保存當(dāng)前線程使用的數(shù)據(jù)源類型,確保多線程環(huán)境下數(shù)據(jù)源切換不會(huì)相互干擾
 */
public class DataSourceContextHolder {
    
    // 使用ThreadLocal保證線程安全,每個(gè)線程有獨(dú)立的數(shù)據(jù)源上下文
    private static final ThreadLocal<DataSourceType> CONTEXT_HOLDER = new ThreadLocal<>();
    
    /**
     * 設(shè)置當(dāng)前線程的數(shù)據(jù)源類型
     * @param dataSourceType 數(shù)據(jù)源類型(MASTER或SLAVE)
     */
    public static void setDataSourceType(DataSourceType dataSourceType) {
        CONTEXT_HOLDER.set(dataSourceType);
    }
    
    /**
     * 獲取當(dāng)前線程的數(shù)據(jù)源類型
     * @return 當(dāng)前數(shù)據(jù)源類型,默認(rèn)為MASTER(保證寫操作可靠性)
     */
    public static DataSourceType getDataSourceType() {
        return CONTEXT_HOLDER.get() == null ? DataSourceType.MASTER : CONTEXT_HOLDER.get();
    }
    
    /**
     * 清除當(dāng)前線程的數(shù)據(jù)源類型
     * 防止內(nèi)存泄漏,特別是在線程池場(chǎng)景下
     */
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }
}

4. 動(dòng)態(tài)路由數(shù)據(jù)源

/**
 * 動(dòng)態(tài)路由數(shù)據(jù)源(繼承Spring的AbstractRoutingDataSource)
 * 核心功能:根據(jù)當(dāng)前上下文動(dòng)態(tài)選擇主庫或從庫
 */
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
    
    /**
     * 決定當(dāng)前數(shù)據(jù)源查找鍵(Spring在每次數(shù)據(jù)庫操作前調(diào)用此方法)
     * @return 數(shù)據(jù)源查找鍵(MASTER或SLAVE)
     */
    @Override
    protected Object determineCurrentLookupKey() {
        DataSourceType dataSourceType = DataSourceContextHolder.getDataSourceType();
        System.out.println("當(dāng)前使用的數(shù)據(jù)源: " + dataSourceType);
        return dataSourceType;
    }
}

5. 數(shù)據(jù)源配置類

/**
 * 數(shù)據(jù)源配置類(核心配置)
 * 配置主從數(shù)據(jù)源并初始化路由數(shù)據(jù)源
 */
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
    basePackages = "com.example.repository",
    entityManagerFactoryRef = "entityManagerFactory",
    transactionManagerRef = "transactionManager"
)
public class DataSourceConfig {
    
    /**
     * 主庫數(shù)據(jù)源(寫操作)
     */
    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    /**
     * 從庫數(shù)據(jù)源(讀操作)
     */
    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    /**
     * 動(dòng)態(tài)路由數(shù)據(jù)源(優(yōu)先級(jí)最高,作為主數(shù)據(jù)源)
     */
    @Primary
    @Bean(name = "routingDataSource")
    public DataSource routingDataSource(
            @Qualifier("masterDataSource") DataSource masterDataSource,
            @Qualifier("slaveDataSource") DataSource slaveDataSource) {
        
        DynamicRoutingDataSource routingDataSource = new DynamicRoutingDataSource();
        
        // 配置目標(biāo)數(shù)據(jù)源映射
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER, masterDataSource);
        targetDataSources.put(DataSourceType.SLAVE, slaveDataSource);
        
        routingDataSource.setTargetDataSources(targetDataSources);
        routingDataSource.setDefaultTargetDataSource(masterDataSource); // 默認(rèn)使用主庫
        
        return routingDataSource;
    }
    
    /**
     * 實(shí)體管理器工廠
     */
    @Bean(name = "entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            EntityManagerFactoryBuilder builder, 
            @Qualifier("routingDataSource") DataSource dataSource) {
        return builder
            .dataSource(dataSource)
            .packages("com.example.entity")
            .persistenceUnit("mysqlUnit")
            .build();
    }
    
    /**
     * 事務(wù)管理器
     */
    @Bean(name = "transactionManager")
    public PlatformTransactionManager transactionManager(
            @Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

6. AOP切面實(shí)現(xiàn)自動(dòng)路由

/**
 * 數(shù)據(jù)源切面配置(基于AOP自動(dòng)切換數(shù)據(jù)源)
 * 通過方法名自動(dòng)識(shí)別讀寫操作,實(shí)現(xiàn)數(shù)據(jù)源動(dòng)態(tài)路由
 */
@Aspect
@Component
@Order(1) // 確保在事務(wù)切面之前執(zhí)行
public class DataSourceAspect {
    
    /**
     * 寫操作切點(diǎn)(insert、update、delete、save開頭的方法)
     */
    @Before("execution(* com.example.service..*.create*(..)) || " +
            "execution(* com.example.service..*.update*(..)) || " +
            "execution(* com.example.service..*.delete*(..)) || " +
            "execution(* com.example.service..*.save*(..))")
    public void setWriteDataSourceType() {
        DataSourceContextHolder.setDataSourceType(DataSourceType.MASTER);
        System.out.println("切換到主庫(寫操作)");
    }
    
    /**
     * 讀操作切點(diǎn)(select、get、find、query開頭的方法)
     */
    @Before("execution(* com.example.service..*.select*(..)) || " +
            "execution(* com.example.service..*.get*(..)) || " +
            "execution(* com.example.service..*.find*(..)) || " +
            "execution(* com.example.service..*.query*(..))")
    public void setReadDataSourceType() {
        DataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE);
        System.out.println("切換到從庫(讀操作)");
    }
    
    /**
     * 后置處理:清理數(shù)據(jù)源上下文
     */
    @After("execution(* com.example.service..*.*(..))")
    public void clearDataSourceType() {
        DataSourceContextHolder.clearDataSourceType();
        System.out.println("清理數(shù)據(jù)源上下文");
    }
}

7. 業(yè)務(wù)層使用示例

/**
 * 用戶服務(wù)實(shí)現(xiàn)類
 * 演示讀寫分離的實(shí)際應(yīng)用
 */
@Service
@Transactional
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    /**
     * 新增用戶(寫操作自動(dòng)路由到主庫)
     */
    @Override
    public User createUser(User user) {
        // 方法名以"create"開頭,AOP會(huì)自動(dòng)切換到MASTER數(shù)據(jù)源
        return userRepository.save(user);
    }
    
    /**
     * 根據(jù)ID查詢用戶(讀操作自動(dòng)路由到從庫)
     */
    @Override
    @Transactional(readOnly = true) // 只讀事務(wù)優(yōu)化性能
    public User getUserById(Long id) {
        // 方法名以"get"開頭,AOP會(huì)自動(dòng)切換到SLAVE數(shù)據(jù)源
        return userRepository.findById(id).orElse(null);
    }
    
    /**
     * 查詢所有用戶(讀操作)
     */
    @Override
    @Transactional(readOnly = true)
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }
    
    /**
     * 更新用戶信息(寫操作)
     */
    @Override
    public User updateUser(User user) {
        return userRepository.save(user);
    }
}

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

單元測(cè)試類

/**
 * 讀寫分離測(cè)試類
 */
@SpringBootTest
class ReadWriteSeparationTest {
    
    @Autowired
    private UserService userService;
    
    /**
     * 測(cè)試寫操作(應(yīng)路由到主庫)
     */
    @Test
    void testWriteOperation() {
        User user = new User();
        user.setUsername("testUser");
        user.setPassword("password");
        
        User savedUser = userService.createUser(user);
        
        Assertions.assertNotNull(savedUser.getId());
        System.out.println("寫操作測(cè)試通過(路由到主庫)");
    }
    
    /**
     * 測(cè)試讀操作(應(yīng)路由到從庫)
     */
    @Test
    void testReadOperation() {
        List<User> users = userService.getAllUsers();
        
        Assertions.assertNotNull(users);
        System.out.println("讀操作測(cè)試通過(路由到從庫)");
    }
    
    /**
     * 測(cè)試讀寫混合操作
     */
    @Test
    void testReadWriteMix() {
        // 寫操作
        User user = new User();
        user.setUsername("mixUser");
        userService.createUser(user);
        
        // 讀操作
        User foundUser = userService.getUserById(1L);
        
        Assertions.assertNotNull(foundUser);
        System.out.println("讀寫混合操作測(cè)試通過");
    }
}

關(guān)鍵注意事項(xiàng)

1. 主從同步延遲處理

在讀寫分離架構(gòu)中,主從同步存在延遲可能性。剛寫入主庫的數(shù)據(jù)可能不會(huì)立即在從庫中可用。

解決方案:

/**
 * 強(qiáng)制讀主庫的場(chǎng)景
 */
@Service
public class CriticalService {
    
    @Autowired
    private UserRepository userRepository;
    
    /**
     * 重要業(yè)務(wù):寫入后立即讀取,強(qiáng)制走主庫
     */
    public User createAndGetUser(User user) {
        // 寫入主庫
        User savedUser = userRepository.save(user);
        
        // 強(qiáng)制從主庫讀?。ū苊馔窖舆t)
        DataSourceContextHolder.setDataSourceType(DataSourceType.MASTER);
        try {
            return userRepository.findById(savedUser.getId()).orElse(null);
        } finally {
            DataSourceContextHolder.clearDataSourceType();
        }
    }
}

2. 事務(wù)中的數(shù)據(jù)處理

在事務(wù)中,所有操作應(yīng)使用同一數(shù)據(jù)源。

解決方案:

@Service
public class TransactionalService {
    
    /**
     * 事務(wù)內(nèi)強(qiáng)制使用主庫
     */
    @Transactional
    public void complexBusinessOperation() {
        // 方法開始時(shí)顯式設(shè)置主庫,確保事務(wù)內(nèi)一致性
        DataSourceContextHolder.setDataSourceType(DataSourceType.MASTER);
        
        try {
            // 一系列數(shù)據(jù)庫操作...
            // 所有這些操作都在同一事務(wù)中,使用同一數(shù)據(jù)源
        } finally {
            // 事務(wù)結(jié)束后清理
            DataSourceContextHolder.clearDataSourceType();
        }
    }
}

方案優(yōu)缺點(diǎn)分析

優(yōu)勢(shì)挑戰(zhàn)應(yīng)對(duì)策略
提升讀性能:將讀請(qǐng)求分發(fā)到從庫主從同步延遲關(guān)鍵業(yè)務(wù)強(qiáng)制讀主庫
提高可用性:主庫故障時(shí)從庫可讀事務(wù)內(nèi)數(shù)據(jù)源一致性 事務(wù)中強(qiáng)制使用主庫
減輕主庫壓力復(fù)雜SQL路由明確的讀寫操作分離

總結(jié)

通過以上完整的Spring Boot 3實(shí)現(xiàn)方案,你可以成功配置MySQL讀寫分離。關(guān)鍵在于理解動(dòng)態(tài)數(shù)據(jù)源路由原理,合理處理主從同步延遲和事務(wù)一致性等挑戰(zhàn)。這種架構(gòu)能顯著提升系統(tǒng)性能,特別適合讀多寫少的應(yīng)用場(chǎng)景。

以上就是SpringBoot3實(shí)現(xiàn)MySQL讀寫分離的完整指南的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot3 MySQL讀寫分離的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java二叉樹遍歷與遞歸詳解(從入門到精通)

    Java二叉樹遍歷與遞歸詳解(從入門到精通)

    二叉樹是一棵空樹,或者是一棵由一個(gè)根節(jié)點(diǎn)和兩棵互不相交的,分別稱作根的左子樹和右子樹組成的非空樹,這篇文章主要介紹了Java二叉樹遍歷與遞歸的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2026-03-03
  • Java8 forEach常用場(chǎng)景代碼實(shí)例

    Java8 forEach常用場(chǎng)景代碼實(shí)例

    這篇文章主要介紹了Java8 forEach常用場(chǎng)景代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Elasticsearch聚合查詢概念及字段類型示例

    Elasticsearch聚合查詢概念及字段類型示例

    這篇文章主要為大家介紹了Elasticsearch聚合查詢概念及字段類型示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • springboot啟動(dòng)讀取外部配置文件實(shí)現(xiàn)方式

    springboot啟動(dòng)讀取外部配置文件實(shí)現(xiàn)方式

    Spring Boot通過外部配置文件優(yōu)先級(jí)(外部>內(nèi)部)加載運(yùn)維定義的配置,需將文件放置在jar包同級(jí)或config目錄下,相同配置項(xiàng)以外部為準(zhǔn),不同配置項(xiàng)互補(bǔ)生效
    2025-09-09
  • Java簡(jiǎn)單計(jì)算器的實(shí)現(xiàn)

    Java簡(jiǎn)單計(jì)算器的實(shí)現(xiàn)

    這篇文章主要為大家詳細(xì)介紹了Java簡(jiǎn)單計(jì)算器的實(shí)現(xiàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • Java經(jīng)典排序算法之冒泡排序代碼實(shí)例

    Java經(jīng)典排序算法之冒泡排序代碼實(shí)例

    這篇文章主要介紹了Java經(jīng)典排序算法之冒泡排序代碼實(shí)例,相鄰兩元素進(jìn)行比較,如過左側(cè)元素大于右側(cè)元素,則進(jìn)行交換,每完成一次循環(huán)就將最大元素排在最后,下一次循環(huán)是將其它的數(shù)進(jìn)行類似操作,需要的朋友可以參考下
    2023-11-11
  • SpringBoot整合RocketMQ批量發(fā)送消息的實(shí)現(xiàn)代碼

    SpringBoot整合RocketMQ批量發(fā)送消息的實(shí)現(xiàn)代碼

    這篇文章主要介紹了SpringBoot整合RocketMQ批量發(fā)送消息的實(shí)現(xiàn),文中通過代碼示例講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-04-04
  • Java BigInteger類,BigDecimal類,Date類,DateFormat類及Calendar類用法示例

    Java BigInteger類,BigDecimal類,Date類,DateFormat類及Calendar類用法示例

    這篇文章主要介紹了Java BigInteger類,BigDecimal類,Date類,DateFormat類及Calendar類用法,結(jié)合實(shí)例形式詳細(xì)分析了Java使用BigInteger類,BigDecimal類,Date類,DateFormat類及Calendar類進(jìn)行數(shù)值運(yùn)算與日期運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下
    2019-03-03
  • mybatis?resultMap之collection聚集兩種實(shí)現(xiàn)方式

    mybatis?resultMap之collection聚集兩種實(shí)現(xiàn)方式

    本文主要介紹了mybatis?resultMap之collection聚集兩種實(shí)現(xiàn)方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-09-09
  • 基于Servlet實(shí)現(xiàn)技術(shù)問答網(wǎng)站系統(tǒng)

    基于Servlet實(shí)現(xiàn)技術(shù)問答網(wǎng)站系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了基于Servlet實(shí)現(xiàn)技術(shù)問答網(wǎng)站系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-04-04

最新評(píng)論

同德县| 梁河县| 新巴尔虎右旗| 湛江市| 鄂尔多斯市| 申扎县| 阿坝县| 康乐县| 阿坝县| 色达县| 西平县| 文成县| 邢台县| 云梦县| 晋江市| 句容市| 边坝县| 松溪县| 淳安县| 丹寨县| 淮滨县| 颍上县| 秀山| 苍南县| 济南市| 锦州市| 什邡市| 安平县| 广水市| 利津县| 鹿泉市| 邮箱| 镶黄旗| 旅游| 福贡县| 林州市| 环江| 襄垣县| 景洪市| 临海市| 常宁市|