SQL實(shí)現(xiàn)讀寫(xiě)分離的分配的幾種方式
讀寫(xiě)分離的分配,即如何在應(yīng)用程序中將讀操作和寫(xiě)操作路由到不同的數(shù)據(jù)庫(kù)實(shí)例,可以通過(guò)幾種不同的方法來(lái)實(shí)現(xiàn)。這些方法可以在應(yīng)用程序?qū)?、?shù)據(jù)庫(kù)層或使用中間件來(lái)完成。以下是幾種常見(jiàn)的實(shí)現(xiàn)方法:
應(yīng)用程序?qū)訉?shí)現(xiàn)
在應(yīng)用程序?qū)訉?shí)現(xiàn)讀寫(xiě)分離,通常通過(guò)配置多個(gè)數(shù)據(jù)源并在代碼中顯式地選擇適當(dāng)?shù)臄?shù)據(jù)源。使用 AOP(面向切面編程)來(lái)自動(dòng)選擇數(shù)據(jù)源是一種常見(jiàn)的方法。
具體實(shí)現(xiàn)步驟
- 配置多數(shù)據(jù)源:配置一個(gè)主數(shù)據(jù)源(用于寫(xiě)操作)和多個(gè)從數(shù)據(jù)源(用于讀操作)。
- 實(shí)現(xiàn)路由邏輯:通過(guò) AOP 或其他方式在代碼中選擇適當(dāng)?shù)臄?shù)據(jù)源。
- 使用自定義注解:標(biāo)記需要路由到不同數(shù)據(jù)源的方法。
以下是詳細(xì)的實(shí)現(xiàn)示例:
配置文件
在 application.yml
中配置主庫(kù)和從庫(kù)的信息。
# application.yml spring: datasource: master: url: jdbc:mysql://master-db:3306/mydb username: root password: root slaves: - url: jdbc:mysql://slave-db1:3306/mydb username: root password: root - url: jdbc:mysql://slave-db2:3306/mydb username: root password: root
數(shù)據(jù)源配置
import com.zaxxer.hikari.HikariDataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration public class DataSourceConfig { @Autowired private MasterDataSourceProperties masterProperties; @Autowired private SlaveDataSourceProperties slaveProperties; @Bean @Primary public DataSource dataSource() { AbstractRoutingDataSource routingDataSource = new ReplicationRoutingDataSource(); HikariDataSource masterDataSource = new HikariDataSource(); masterDataSource.setJdbcUrl(masterProperties.getUrl()); masterDataSource.setUsername(masterProperties.getUsername()); masterDataSource.setPassword(masterProperties.getPassword()); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("master", masterDataSource); for (int i = 0; i < slaveProperties.getSlaves().size(); i++) { SlaveProperties slave = slaveProperties.getSlaves().get(i); HikariDataSource slaveDataSource = new HikariDataSource(); slaveDataSource.setJdbcUrl(slave.getUrl()); slaveDataSource.setUsername(slave.getUsername()); slaveDataSource.setPassword(slave.getPassword()); targetDataSources.put("slave" + i, slaveDataSource); } routingDataSource.setTargetDataSources(targetDataSources); routingDataSource.setDefaultTargetDataSource(masterDataSource); return routingDataSource; } }
路由數(shù)據(jù)源
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class ReplicationRoutingDataSource extends AbstractRoutingDataSource { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } public static void clearDataSourceType() { contextHolder.remove(); } @Override protected Object determineCurrentLookupKey() { return contextHolder.get(); } }
數(shù)據(jù)源選擇器
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class DataSourceAspect { @Before("@annotation(com.example.annotation.Master)") public void setWriteDataSourceType() { ReplicationRoutingDataSource.setDataSourceType("master"); } @Before("@annotation(com.example.annotation.Slave) || execution(* com.example.service..*.find*(..))") public void setReadDataSourceType() { ReplicationRoutingDataSource.setDataSourceType("slave0"); // 可實(shí)現(xiàn)負(fù)載均衡策略 } }
自定義注解
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Master { } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Slave { }
示例服務(wù)
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class UserService { @Autowired private UserRepository userRepository; @Master @Transactional public void saveUser(User user) { userRepository.save(user); } @Slave public User findUserById(Long id) { return userRepository.findById(id).orElse(null); } }
使用中間件
使用中間件來(lái)實(shí)現(xiàn)讀寫(xiě)分離也是一種常見(jiàn)的方法。中間件通常位于應(yīng)用程序和數(shù)據(jù)庫(kù)之間,負(fù)責(zé)根據(jù)操作類型將請(qǐng)求路由到適當(dāng)?shù)臄?shù)據(jù)庫(kù)實(shí)例。常見(jiàn)的中間件包括 MySQL 的 ProxySQL 和 MariaDB 的 MaxScale。
ProxySQL 示例配置
- 安裝 ProxySQL:可以通過(guò)包管理器安裝 ProxySQL。
- 配置 ProxySQL:在
proxysql.cnf
文件中配置主從數(shù)據(jù)庫(kù)。
datadir="/var/lib/proxysql" admin_variables= { admin_credentials="admin:admin" mysql_ifaces="0.0.0.0:6032" } mysql_variables= { threads=4 max_connections=1024 } mysql_servers = ( { address="master-db", port=3306, hostgroup=0, max_connections=1000, weight=1 }, { address="slave-db1", port=3306, hostgroup=1, max_connections=1000, weight=1 }, { address="slave-db2", port=3306, hostgroup=1, max_connections=1000, weight=1 } ) mysql_users = ( { username="proxyuser", password="proxypassword", default_hostgroup=0, transaction_persistent=1 } ) mysql_query_rules = ( { rule_id=1, match_pattern="^SELECT", destination_hostgroup=1, apply=1 } )
- 啟動(dòng) ProxySQL:使用
systemctl
或其他方式啟動(dòng) ProxySQL。
systemctl start proxysql
數(shù)據(jù)庫(kù)層實(shí)現(xiàn)
有些數(shù)據(jù)庫(kù)本身提供了讀寫(xiě)分離的功能。例如,MySQL 的復(fù)制機(jī)制允許配置一個(gè)主數(shù)據(jù)庫(kù)和多個(gè)從數(shù)據(jù)庫(kù),然后通過(guò)連接池或驅(qū)動(dòng)程序?qū)崿F(xiàn)讀寫(xiě)分離。
總結(jié)
讀寫(xiě)分離的實(shí)現(xiàn)方法有多種,可以根據(jù)具體需求和技術(shù)棧選擇適合的方法。在應(yīng)用程序?qū)訉?shí)現(xiàn)讀寫(xiě)分離較為靈活,可以精細(xì)控制讀寫(xiě)操作的路由邏輯;使用中間件實(shí)現(xiàn)讀寫(xiě)分離則可以簡(jiǎn)化應(yīng)用程序的邏輯,但需要額外維護(hù)中間件的配置和管理;在數(shù)據(jù)庫(kù)層實(shí)現(xiàn)讀寫(xiě)分離可以利用數(shù)據(jù)庫(kù)本身的功能,減少對(duì)應(yīng)用程序的改動(dòng)。
到此這篇關(guān)于SQL實(shí)現(xiàn)讀寫(xiě)分離的分配的幾種方式的文章就介紹到這了,更多相關(guān)SQL 讀寫(xiě)分離分配內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SQL?Server時(shí)間轉(zhuǎn)換3種方法總結(jié)
SQL?Server中處理日期和時(shí)間的常用方法有三種:FORMAT、CONVERT和DATEADD,這篇文章主要介紹了SQL?Server時(shí)間轉(zhuǎn)換的3種方法,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-09-09分享一下SQL Server執(zhí)行動(dòng)態(tài)SQL的正確方式
這篇文章主要介紹了SQL Server執(zhí)行動(dòng)態(tài)SQL正確方式,需要的朋友可以參考下2017-06-06關(guān)于sql server批量插入和更新的兩種解決方案
對(duì)于sql 來(lái)說(shuō)操作集合類型(一行一行)是比較麻煩的一件事,而一般業(yè)務(wù)邏輯復(fù)雜的系統(tǒng)或項(xiàng)目都會(huì)涉及到集合遍歷的問(wèn)題,通常一些人就想到用游標(biāo),這里我列出了兩種方案,供大家參考2013-04-04sqlserver 存儲(chǔ)過(guò)程動(dòng)態(tài)參數(shù)調(diào)用實(shí)現(xiàn)代碼
sqlserver 存儲(chǔ)過(guò)程動(dòng)態(tài)參數(shù)調(diào)用實(shí)現(xiàn)代碼,需要的朋友可以參考下。2011-10-10SQL Server復(fù)制功能要避開(kāi)缺陷的干擾小結(jié)
SQL Server具有強(qiáng)大的復(fù)制功能,除了將數(shù)據(jù)和數(shù)據(jù)庫(kù)對(duì)象從一個(gè)數(shù)據(jù)庫(kù)復(fù)制并準(zhǔn)確分發(fā)的另一個(gè)數(shù)據(jù)庫(kù)中,還要實(shí)行數(shù)據(jù)庫(kù)之間的同步。2011-03-03sql server日期相減 的實(shí)現(xiàn)詳解
本篇文章是對(duì)sql server日期相減的實(shí)現(xiàn)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06在SQL中使用convert函數(shù)進(jìn)行日期的查詢的代碼
在SQL中使用convert函數(shù)進(jìn)行日期的查詢的代碼...2007-08-08