亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

MySQL主從復(fù)制的原理圖解及Java語言示例使用

 更新時間:2022年08月12日 10:16:27   作者:Java技術(shù)那些事兒  
這篇文章主要介紹了MySQL的主從復(fù)制原理詳細(xì)分析,讀寫分離是基于主從復(fù)制來實現(xiàn)的。文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下

概述

實際生產(chǎn)的過程中為了實現(xiàn)數(shù)據(jù)庫的高可用,不會只有一個數(shù)據(jù)庫節(jié)點。至少會搭建主從復(fù)制的數(shù)據(jù)庫架構(gòu),從庫可以作為主庫的數(shù)據(jù)備份,以免主數(shù)據(jù)庫損壞的情況下丟失數(shù)據(jù);當(dāng)訪問量增加的時候可以作為讀節(jié)點承擔(dān)部分流量等。下面就進行從零開始搭建MySQL的主從架構(gòu)。

主從復(fù)制原理

以MySQL一主兩從架構(gòu)為為例,也就是一個master節(jié)點下有兩個slave節(jié)點,在這套架構(gòu)下,寫操作統(tǒng)一交給master節(jié)點,讀請求交給slave節(jié)點處理。

為了保證master節(jié)點和slave節(jié)點數(shù)據(jù)一致,在master節(jié)點寫入數(shù)據(jù)后,會同時將數(shù)據(jù)復(fù)制到對應(yīng)的slave節(jié)點。主從復(fù)制數(shù)據(jù)的過程中會用到三個線程,master節(jié)點上的binlog dump線程,slave節(jié)點的I\O線程和SQL線程。

主從復(fù)制的核心流程:

  • 當(dāng)master節(jié)點接收到一個寫請求時,這個寫請求可能是增刪改操作,此時會把寫請求的操作都記錄到binlog日志中。
  • master節(jié)點會把數(shù)據(jù)賦值給slave節(jié)點,如圖中的兩個slave節(jié)點。這個過程首先得要每個slave節(jié)點連接到master節(jié)點上,當(dāng)slave節(jié)點連接到master節(jié)點上時,master節(jié)點會為每一個slave節(jié)點分別創(chuàng)建一個binlog dump線程,用于向每個slave節(jié)點發(fā)送binlog日志。
  • 此時,binlog dump線程會讀取master節(jié)點上的binlog日志,然后將binlog日志發(fā)送給slave節(jié)點上的I/O線程。
  • slave幾點上的I/O線程接收到binlog日之后,會將binlog日志先寫入到本地的relaylog中,relaylog中就保存了master的binlog日志。
  • 最后,slave節(jié)點上的SQL線程會讀取relaylog中的biinlog日志,將其解析成具體的增刪改操作,把這些在master節(jié)點上進行過的操作,重新在slave節(jié)點上也重做一遍,打到數(shù)據(jù)還原的效果,這樣就可以保證master節(jié)點和slave節(jié)點的數(shù)據(jù)一致性了。

主從復(fù)制模式

MySQL的主從復(fù)制模式分為:全同步復(fù)制,異步復(fù)制,半同步復(fù)制,增強半同步復(fù)制。

全同步復(fù)制

全同步復(fù)制,就是當(dāng)主庫執(zhí)行完一個事物之后,要求所有的從庫也都必須執(zhí)行完該事務(wù),才可以返回處理結(jié)果給客戶端;因此雖然全同步復(fù)制數(shù)據(jù)一致性得到保證了,但是主庫完成一個事物需要等待所有從庫也完成,性能就比較低了。

異步復(fù)制

異步復(fù)制,當(dāng)主庫提交事務(wù)后會通知binlog dump線程發(fā)送binlog日志給從庫,一旦binlog dump線程將binlog日志發(fā)送給從庫之后,不需要等到從庫也同步完成事務(wù),主庫就會講處理結(jié)果返回給客戶端。

因為主庫只管自己執(zhí)行完事務(wù),就可以將處理結(jié)果返回給客戶端,而不用關(guān)系從庫是否執(zhí)行完事務(wù),這就可能導(dǎo)致短暫的主從數(shù)據(jù)不一致的問題了,比如剛在主庫插入的數(shù)據(jù),如果馬上在從庫查詢就可能查詢不到。

當(dāng)主庫提交食物后,如果宕機掛掉了,此時可能binlog還沒來得及同步給從庫,這時候如果為了回復(fù)故障切換主從節(jié)點的話,就會出現(xiàn)數(shù)據(jù)丟失的問題,所以異步復(fù)制雖然性能高,但數(shù)據(jù)一致性上是比較弱的。

MySQL默認(rèn)采用的是異步復(fù)制模式。

半同步復(fù)制

半同步復(fù)制就是在同步復(fù)制和異步中做了折中選擇,我們可以結(jié)合著MySQL官網(wǎng)來看下是半同步和主從復(fù)制的過程。

當(dāng)主庫提交事務(wù)后,至少還需要一個從庫返回接收到binlog日志,并成功寫入到relaylog的消息,這個的時候,主庫才會講處理結(jié)果返回給客戶端。

相比前兩種復(fù)制方式,半同步復(fù)制較好地兼顧了數(shù)據(jù)一致性以及性能損耗的問題。

同時,半同步復(fù)制也存在以下幾個問題:

  • 半同步復(fù)制的性能,相比異步復(fù)制而言有所下降,因為需要等到等待至少一個從庫確認(rèn)接收到binlog日志的響應(yīng),所以新能上是有所損耗的。
  • 主庫等待從庫響應(yīng)的最大時長我們是可以配置的,如果超過了我們配置的事件,半同步復(fù)制就會變成異步復(fù)制,那么,異步復(fù)制的問題同樣也就出現(xiàn)了。
  • 在MySQL5.7.2之前的版本中,半同步復(fù)制存在幻讀問題。當(dāng)主庫成功提交事務(wù)并處于等待從庫確認(rèn)的過程中,這個時候,從庫都還沒來得及返回處理結(jié)果給客戶端,但因為主庫存儲引擎內(nèi)部已經(jīng)提交事務(wù)了,所以,其他客戶端是可以到主庫中讀到數(shù)據(jù)的。但是,如果下一秒主庫宕機,下次請求過來只能讀取從庫,因為從庫還沒有從主庫同步數(shù)據(jù),所以從庫中讀取不到這條數(shù)據(jù)了,和上一次讀取數(shù)據(jù)的結(jié)果相比,就造成了幻讀的現(xiàn)象。

增強半同步復(fù)制

增強半同步復(fù)制是MySQL5.7.2后的版本對半同步復(fù)制做的一個改進,原理幾乎是一樣的,主要是解決幻讀的問題。

主庫配置了參數(shù)rpl_semi_sync_master_wait_point=AFTER_SYNC后,主庫在存儲引擎提交事務(wù)前,必須先首都哦啊從庫數(shù)據(jù)同步完成的確認(rèn)信息后,才能提交事務(wù),以此來解決幻讀問題。

主從同步實戰(zhàn)

準(zhǔn)備數(shù)據(jù)源

config/datasource.properties

# masters
spring.datasource.masters.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.masters.url=jdbc:mysql://192.168.1.111:3306/monomer_order?useUnicode=true&characterEncoding=utf8&useSSL=false&autoReconnect=true&zeroDateTimeBehavior=convertToNull
spring.datasource.masters.username=root
spring.datasource.masters.password=123456
# slaves
spring.datasource.slaves[0].driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.slaves[0].url=jdbc:mysql://192.168.1.112:3306/monomer_order?useUnicode=true&characterEncoding=utf8&useSSL=false&autoReconnect=true&zeroDateTimeBehavior=convertToNull
spring.datasource.slaves[0].username=root
spring.datasource.slaves[0].password=123456

配置數(shù)據(jù)源

package com.xinxin.order.context.config;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.util.CollectionUtils;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@Data
@Configuration
@PropertySource("classpath:config/datasource.properties")
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceConfig {
    /**
     * 主庫數(shù)據(jù)源信息
     */
    private Map<String, String> masters;
    /**
     * 從庫數(shù)據(jù)源信息
     */
    private List<Map<String, String>> slaves;
    @SneakyThrows
    @Bean
    public DataSource masterDataSource() {
        log.info("masters:{}", masters);
        if (CollectionUtils.isEmpty(masters)) {
            throw new Exception("主庫數(shù)據(jù)源不能為空");
        }
        return DruidDataSourceFactory.createDataSource(masters);
    }
    @SneakyThrows
    @Bean
    public List<DataSource> slaveDataSources() {
        if (CollectionUtils.isEmpty(slaves)) {
            throw new Exception("從庫數(shù)據(jù)源不能為空");
        }
        final ArrayList<DataSource> dataSources = new ArrayList<>();
        for (Map<String, String> slaveProperties : slaves) {
            log.info("slave:{}", slaveProperties);
            dataSources.add(DruidDataSourceFactory.createDataSource(slaveProperties));
        }
        return dataSources;
    }
    @Bean
    @Primary
    @DependsOn({"masterDataSource", "slaveDataSources"})
    public DataSource routingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
                                        @Qualifier("slaveDataSources") List<DataSource> slaveDataSources) {
        final Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceContextHolder.MASTER, masterDataSource);
        for (int i = 0; i < slaveDataSources.size(); i++) {
            targetDataSources.put(DataSourceContextHolder.SLAVE + i, slaveDataSources.get(i));
        }
        final DataSourceRouter dataSourceRouter = new DataSourceRouter();
        dataSourceRouter.setTargetDataSources(targetDataSources);
        dataSourceRouter.setDefaultTargetDataSource(masterDataSource);
        return dataSourceRouter;
    }
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(
            @Qualifier("routingDataSource") DataSource routingDataSource) {
        return new DataSourceTransactionManager(routingDataSource);
    }
}

數(shù)據(jù)源上下文切換

package com.xinxin.order.context.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@Slf4j
public class DataSourceContextHolder {
    public static final String MASTER = "master";
    public static final String SLAVE = "slave";
    private static ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
    public static void setDatasourceType(String dataSourceType) {
        if (StringUtils.isBlank(dataSourceType)) {
            log.error("dataSourceType為空");
        }
        log.info("設(shè)置dataSource: {}", dataSourceType);
        CONTEXT_HOLDER.set(dataSourceType);
    }
    public static String getDataSourceType() {
        return CONTEXT_HOLDER.get() == null ? MASTER : CONTEXT_HOLDER.get();
    }
    public static void remove() {
        CONTEXT_HOLDER.remove();
    }
}

數(shù)據(jù)源路由實現(xiàn)類

package com.xinxin.order.context.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
@Slf4j
public class DataSourceRouter extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        log.info("當(dāng)前數(shù)據(jù)源為: {}", DataSourceContextHolder.getDataSourceType());
        return DataSourceContextHolder.getDataSourceType();
    }
}

數(shù)據(jù)源切換注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnly {
    String value() default DataSourceContextHolder.MASTER;
}

動態(tài)數(shù)據(jù)源切換切面

package com.xinxin.order.aspect;
import com.xinxin.order.annotation.ReadOnly;
import com.xinxin.order.context.config.DataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class DynamicDataSourceAspect implements Ordered {
    @Before(value = "execution(* *(..))&& @annotation(readOnly)")
    public void before(JoinPoint joinPoint, ReadOnly readOnly) {
        log.info(joinPoint.getSignature().getName() + "走從庫");
        DataSourceContextHolder.setDatasourceType(DataSourceContextHolder.SLAVE);
    }
    @After(value = "execution(* *(..))&& @annotation(readOnly)")
    public void after(JoinPoint joinPoint, ReadOnly readOnly) {
        log.info(joinPoint.getSignature().getName() + "清除數(shù)據(jù)源");
        DataSourceContextHolder.remove();
    }
    @Override
    public int getOrder() {
        return 0;
    }
}

總結(jié)

項目整合讀寫分離主要是通過收到注入數(shù)據(jù)源,并通過攔截器設(shè)置當(dāng)前線程的數(shù)據(jù)源類型,需要使用數(shù)據(jù)源的地方會通過數(shù)據(jù)源路由器讀取當(dāng)前線程的數(shù)據(jù)源類型后返回實際的數(shù)據(jù)源進行數(shù)據(jù)庫的操作。

到此這篇關(guān)于Java MySQL主從復(fù)制的原理圖解及示例使用的文章就介紹到這了,更多相關(guān)Java MySQL主從復(fù)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 磁盤已滿造成的mysql啟動失敗問題分享

    磁盤已滿造成的mysql啟動失敗問題分享

    這篇文章主要介紹了磁盤已滿造成的mysql啟動失敗問題分享,需要的朋友可以參考下
    2014-04-04
  • mysql中如何判斷是否支持分區(qū)

    mysql中如何判斷是否支持分區(qū)

    這篇文章主要介紹了mysql中如何判斷是否支持分區(qū)的相關(guān)資料,需要的朋友可以參考下
    2015-10-10
  • MySQL性能優(yōu)化之max_connections配置參數(shù)淺析

    MySQL性能優(yōu)化之max_connections配置參數(shù)淺析

    這篇文章主要介紹了MySQL性能優(yōu)化之max_connections配置參數(shù)淺析,本文著重講解了3種配置max_connections參數(shù)的方法,需要的朋友可以參考下
    2014-07-07
  • Windows下MySQL安裝配置方法圖文教程

    Windows下MySQL安裝配置方法圖文教程

    這篇文章主要為大家分享了Windows下MySQL安裝配置方法圖文教程,感興趣的朋友可以參考一下
    2016-06-06
  • MySQL Semisynchronous Replication介紹

    MySQL Semisynchronous Replication介紹

    這篇文章主要介紹了MySQL Semisynchronous Replication介紹,本文講解了Semisynchronous Replication 定義、,需要的朋友可以參考下
    2015-05-05
  • MySQL 5.7.29 + Win64 解壓版 安裝教程圖文詳解

    MySQL 5.7.29 + Win64 解壓版 安裝教程圖文詳解

    這篇文章主要介紹了MySQL 5.7.29 + Win64 解壓版 安裝教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-05-05
  • 實例詳解mysql子查詢

    實例詳解mysql子查詢

    這篇文章主要介紹了mysql子查詢的相關(guān)資料,幫助大家更好的理解和使用MySQL數(shù)據(jù)庫,感興趣的朋友可以了解下
    2020-09-09
  • Mysql數(shù)據(jù)庫鎖定機制詳細(xì)介紹

    Mysql數(shù)據(jù)庫鎖定機制詳細(xì)介紹

    這篇文章主要介紹了Mysql數(shù)據(jù)庫鎖定機制詳細(xì)介紹,本文用大量內(nèi)容講解了Mysql中的鎖定機制,例如MySQL鎖定機制簡介、合理利用鎖機制優(yōu)化MySQL等內(nèi)容,需要的朋友可以參考下
    2014-12-12
  • MySQL中使用正則表達式詳情

    MySQL中使用正則表達式詳情

    這牌你文章主要給大家分享的是MySQL中使用正則表達式詳情,MySQL中支持正則表達式匹配,在復(fù)雜的過濾條件中,可以考慮使用正則表達式。使用正則表達式需要掌握一些正則表達式的語法和指令,下面來看看文章的詳細(xì)內(nèi)容介紹吧,希望對你有所幫助
    2021-11-11
  • Centos 6.5 下安裝mysql 5.6.21的方法

    Centos 6.5 下安裝mysql 5.6.21的方法

    本文給大家?guī)鞢entos 6.5 下安裝mysql 5.6.21的方法,操作方法比較簡單,介紹的非常詳細(xì),感興趣的朋友可以參考下操作步驟
    2016-09-09

最新評論