1. 程式人生 > >CentOS7:mysql5.7的安裝&主從&讀寫分離

CentOS7:mysql5.7的安裝&主從&讀寫分離

說明:本文是通過引用 + 原創的形式來介紹CentOS7的mysql5.7的安裝&主從&讀寫分離

1.mysql5.7的 安裝

下面的文章介紹瞭如果已經安裝了MySQL該如何解除安裝,以及一些可以直接複製的常規操作(懶得記),本人已在阿里雲上測試通過了

https://blog.csdn.net/z13615480737/article/details/78906598

2.mysql5.7的主從

我是直接在阿里雲上開了兩臺ECS完成的,如果你是想在一臺虛擬機器上開多個mysql例項,那你可能要換篇文章看看了

下文是來自簡書文章,簡單易懂,並且也已經通過測試

https://www.jianshu.com/p/24c1995e9bd8

3.下面是MYSQL的讀寫分離:

首先為什麼要去做讀寫分離?

原因是:

應用就算做了前端和後端的分離,但是要求可是一點都沒有減少

就算有快取幫著擋一下,但是也檔不了寫的操作啊!

如果有海量的寫操作和讀操作過來,單單一臺MySQL伺服器是擋不住的

所以此時就有了讀寫分離的需求
在這裡插入圖片描述

讀寫分離是什麼?

最簡單的解釋就是將寫操作專門放到一臺機器上,然後將讀操作放到另一臺機器上

在這裡插入圖片描述

如果要讀取資料,那麼去找資料庫B

如果要寫資料,那麼去找資料庫A,

但是一旦在資料庫A上寫了資料,那麼就得立即去通知資料庫B,讓B資料庫做對應的更改

讀寫分離的缺陷

讀寫分離的缺陷,我想大家也應該知道了,就是很容易讀取到髒資料,所以讀寫分離的從資料庫,一般都是接受一些對於髒資料容忍度比較高的資料的讀取

這裡提到了髒資料容忍度的問題

那麼哪些資料是髒資料容忍度比較高的資料呢?

比如:IP登入記錄,首頁新聞等這些資料就算讀到髒資料也沒有關係,所以就可以從 從資料庫 中讀取資料,而不用從 主資料庫 中讀取資料了

其一,讀寫分離的原則

如果我們確定要去做讀寫分離的操作了,那麼首先要明白的一個原則是:

一個Service方法只能對應一個數據庫

堅決不能出現,一個Service方法既對應主資料庫,又對應從資料庫的情況出現

其二,在既有寫又有讀的情況下,資料庫選擇主資料庫

	public class IplogServiceImpl implements IIplogService{ 

		PageResult query(IpLogQueryObject qo){

			int count = mapper.queryForCount(qo);

			List list = mapper.query(qo);
		}

		void add(IpLog ll){

			mapper.insert(ll);
		}		

	}

比如query的方法只是讀就可以對應 從資料庫

insert方法涉及到寫就可以對應 主資料庫

但是query中假如加了個寫的方法,那麼此時該對應哪個資料庫?

答案是:主資料庫,很簡單,你又查又寫,那當然是選擇主資料庫了!

假如在 從資料庫上 又查又寫,那麼從資料庫上資料就改變了!

你想只有主資料庫通知從資料庫的,哪裡有從資料庫通知主資料庫的?

這樣就是雙向更新了,我們的讀寫分離,原則上是單向更新的

簡單的來說就是:只准 主資料庫 放火,不準 從資料庫 點燈

其三,讀寫分離的核心原理

現在的問題就是我要如何通知Service方法,更換資料庫呢?

關鍵就是DataSource和SqlSessionFactory

想一下,我們平時在Spring中寫JDBC四要素的時候是在哪裡寫的?

當然是在DataSource中定義四要素的!

所以我們接下來的思路可以變成這樣:
在這裡插入圖片描述

在這裡插入圖片描述

等於說,此處我們需要一個類似於路由器的東西,我們可以給它一個引數,讓它知道應該訪問哪個資料庫,然後由它來實現資料庫的切換

其四,AbstractRoutingDataSource

其原理這邊文章已經有具體的說明:https://blog.csdn.net/x2145637/article/details/52461198

這裡我只做Spring下的最簡單的演示,並不涉及AOP切換資料庫

首先是建立一個類去繼承AbstractRoutingDataSource

下面的CurrentDataSourceContext是一個LocalThread型別的例項,用於繫結現在的執行緒

package cn.csdn.rw_sep.util;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;


public class RWRoutingDataSource extends AbstractRoutingDataSource {
    /**
        這個方法需要訪問當前的目標的DS的名字
     */
    @Override
    protected Object determineCurrentLookupKey() {

        return CurrentDataSourceContext.get();
    }
}

資料庫切換的關鍵:CurrentDataSourceContext

簡單的來說就是當前執行緒告訴CurrentDataSourceContext我要用什麼樣的資料庫:

比如slaveDs,比如masterDs等,意思是執行緒決定資料庫的型別,執行緒要做的僅僅是

傳入資料庫的名字即可

public class CurrentDataSourceContext {

    private static ThreadLocal<String> dataSourcePool = new ThreadLocal<>();

    public static void set(String dsName){
        dataSourcePool.set(dsName);
    }

    public static String get(){
        return dataSourcePool.get();
    }
}

接下來只要在Controller中說明資料庫即可

但是這個只是基本配置RWRoutingDataSource還必須交給Spring管理,並且還需要獲取Spring管理的DateSource供它進行切換,並且還需要替換Spring的SqlSession的DataSource,換成它,只有這樣做才能實現動態切換資料庫

所以下面的Spring的具體的配置檔案:

	<!-- 連線主dataSource -->
	<bean id="masterDs" class="com.alibaba.druid.pool.DruidDataSource"
		  init-method="init" destroy-method="close">
		<property name="driverClassName" value="${master.driverClassName}" />
		<property name="username" value="${master.username}" />
		<property name="url" value="${master.url}" />
		<property name="password" value="${master.password}" />
	</bean>

	<!-- 連線從dataSource -->
	<bean id="slaveDs" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
		<property name="driverClassName" value="${slave.driverClassName}" />
		<property name="username" value="${slave.username}" />
		<property name="url" value="${slave.url}" />
		<property name="password" value="${slave.password}" />
	</bean>


	<!--配置SqlSessionFactory所需要使用的DataSource-->
	<bean id="dataSource" class="cn.csdn.rw_sep.util.RWRoutingDataSource">
		<!--配置路由器DS的目標DS-->
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<entry key="masterDs" value-ref="masterDs"></entry>
				<entry key="slaveDs" value-ref="slaveDs"></entry>
			</map>
		</property>

		<!--配置預設的目標DS-->
		<property name="defaultTargetDataSource" ref="masterDs"/>
	</bean>


	<!-- 配置SessionFactory -->
	<bean id="sessionFactory"
		class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 1:連線池 -->
		<property name="dataSource" ref="dataSource" />
		<!-- 2:讀取MyBatis總配置檔案 -->
		<property name="configLocation" value="classpath:mybatis.xml"/>
		<!-- 3:配置別名掃描 -->
		<property name="typeAliasesPackage" value="cn.csdn.rw_sep.domain"/>
		<!-- 4:載入mapper檔案 -->
		<property name="mapperLocations" value="classpath:cn/csdn/rw_sep/mapper/UserMapper.xml"/>
	</bean>

後面是具體的配置檔案:

master.driverClassName=com.mysql.jdbc.Driver
master.url=jdbc:mysql://xx.xx.xx.xx:3306/主資料庫名?characterEncoding=utf8&useSSL=false
master.username=root
master.password=xx

slave.driverClassName=com.mysql.jdbc.Driver
slave.url=jdbc:mysql://xx.xx.xx.xx:3306/從資料庫名?characterEncoding=utf8&useSSL=false
slave.username=root
slave.password=xx

接下就是新建一個Controller做測試了

import cn.csdn.rw_sep.domain.User;
import cn.csdn.rw_sep.service.IUserService;
import cn.csdn.rw_sep.util.ConstProperties;
import cn.csdn.rw_sep.util.CurrentDataSourceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class TestController {

    @Autowired
    private IUserService userService;

    @RequestMapping("/testSelect")
    public String testSelect(Long id){

        CurrentDataSourceContext.set(ConstProperties.SLAVE_DS);

        User user = userService.selectById(id);

        System.out.println(user);

        return "select";
    }

    @RequestMapping("/testInsert")
    public String testInsert(User user){

        CurrentDataSourceContext.set(ConstProperties.MASTER_DS);

        userService.insert(user);

        return "insert";
    }
}

在上面你很明顯看到了ConstProperties.MASTER_DS這個東西,

這個是為了便於管理而抽取出來的常量,由於這個在專案中確實到處都要用

一旦修改就是大工程,故將其抽取

public class ConstProperties {

    private ConstProperties(){}

    public static final String MASTER_DS = "masterDs";
    public static final String SLAVE_DS = "slaveDs";
}

啟動Tomcat,開始測試:

首先是讀測試:首先修改從資料庫資料,再觀察究竟是從哪個資料庫讀的資料

在這裡插入圖片描述

測試結果:

在這裡插入圖片描述

接下來是寫測試:

在這裡插入圖片描述

至此讀寫測試完成

下面總結一下讀寫測試常犯的一些錯誤:

注意:需要在Controller層去設定要訪問的資料庫名,而不是在Service層去設定

不然會不生效