java程序实现sql读写分离_项目中如何实现读写分离?怎么配置?

主从复制的原理思想也很简单,就是从库不断地同步主库的改动,保持与主库数据一致;应用仅在从库中读数据。

在项目中,使用读写分离本质上就是,增加数据库服务器资源 + 网络带宽,来分摊对数据库的读写请求,从而提高了性能和可用性。主从复制实现读写分离最大的缺点就是从库同步到主库的数据存在延迟,网络不好的时候,延迟比较严重。

如何实现读写分离?

在我们平时开发中,一般不会自己去控制 select 请求从从库拿 Connection,insert、delete、update 请求从主库拿 Connection。当然也有这么干,就是把读写请求按规则命名方法,然后根据方法名通过反射统一处理请求不同的库。

大部分企业在项目中是使用中间件去实现读写分离的,如 mycat、atlas、dbproxy、cetus、Sharding-JDBC......,每种中间件各有优缺点。

Sharding-JDBC 是 apache 旗下的 ShardingSphere 中的一款产品,轻量,引入 jar 即可完成读写分离的需求,可以理解为增强版的 JDBC,现在被使用的较多。

搭建项目

maven 依赖的库

org.apache.shardingsphere

sharding-jdbc-core

4.1.1

com.zaxxer

HikariCP

3.4.5

mysql

mysql-connector-java

8.0.21

获取数据源的工具类

package constxiong;

import com.zaxxer.hikari.HikariDataSource;

/**

* 获取 DataSource 工具类,使用了 Hikari 数据库连接池

*/

import javax.sql.DataSource;

public final class DataSourceUtil {

private static final int PORT = 3306;

/**

* 通过 Hikari 数据库连接池创建 DataSource

* @param ip

* @param username

* @param password

* @param dataSourceName

* @return

*/

public static DataSource createDataSource(String ip, String username, String password, String dataSourceName) {

HikariDataSource result = new HikariDataSource();

result.setDriverClassName(com.mysql.jdbc.Driver.class.getName());

result.setJdbcUrl(String.format("jdbc:mysql://%s:%s/%s?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8", ip, PORT, dataSourceName));

result.setUsername(username);

result.setPassword(password);

return result;

}

}

测试 Sharding-JDBC 读写分离

主库:172.31.32.184

从库:172.31.32.234

观察通过 Sharding-JDBC 获取的 DataSource 是否会自动写入到主库,从库是否主动同步,从库同步数据的延迟时间

测试代码

package constxiong;

import org.apache.shardingsphere.api.config.masterslave.MasterSlaveRuleConfiguration;

import org.apache.shardingsphere.shardingjdbc.api.MasterSlaveDataSourceFactory;

import javax.sql.DataSource;

import java.sql.Connection;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Statement;

import java.time.LocalTime;

import java.util.*;

/**

* 测试 ShardingSphere 读写分离

* 主库:172.31.32.184

* 从库:172.31.32.234

*

* 观察通过 ShardingSphere 获取的 DataSource 是否会自动写入到主库,从库是否主动同步,从库同步数据的延迟时间

*/

public class Test {

//主库 DataSource

private static DataSource dsSlave = DataSourceUtil.createDataSource("172.31.32.234", "root", "constxiong@123", "constxiong");

//从库 DataSource

private static DataSource dsMaster = DataSourceUtil.createDataSource("172.31.32.184", "root", "constxiong@123", "constxiong");

public static void main(String[] args) throws SQLException {

//启动线程打印主库与从库当前 cuser 数据量与时间,观察从库同步数据延迟

printMasterAndSlaveData();

//从 ShardingSphere 获取 DataSource,出入数据,观察插入数据的库是否为主库

DataSource ds = getMasterSlaveDataSource();

Connection conn = ds.getConnection();

Statement stt = conn.createStatement();

stt.execute("insert into cuser values(2, 'fj')");

}

/**

* 启动线程打印,两个主从库 cuser 表的信息、数据量、当前时间

* @throws SQLException

*/

private static void printMasterAndSlaveData() throws SQLException {

Connection masterConn = dsMaster.getConnection();

Connection slaveConn = dsSlave.getConnection();

new Thread(() -> {

while (true) {

try {

System.out.println("------master------" + LocalTime.now());

print(masterConn);

System.out.println("------slave------" + LocalTime.now());

print(slaveConn);

} catch (SQLException e) {

}

}

}).start();

}

private static void print(Connection conn) throws SQLException {

Statement statement = conn.createStatement();

statement.execute("select * from cuser");

ResultSet rs = statement.getResultSet();

int count = 0;

while (rs.next()) {

int id = rs.getInt("id");

String name = rs.getString("name");

System.out.println(id + "-" + name);

count++;

}

System.out.println("total: " + count);

}

/**

* 设置 ShardingSphere 的主从库

* @return

* @throws SQLException

*/

private static DataSource getMasterSlaveDataSource() throws SQLException {

MasterSlaveRuleConfiguration masterSlaveRuleConfig = new MasterSlaveRuleConfiguration("ds_master_slave", "ds_master", Arrays.asList("ds_slave"));

return MasterSlaveDataSourceFactory.createDataSource(createDataSourceMap(), masterSlaveRuleConfig, new Properties());

}

/**

* 用 主从库的 DataSource 构造 map

* @return

*/

private static Map createDataSourceMap() {

Map result = new HashMap<>();

result.put("ds_master", dsMaster);

result.put("ds_slave", dsSlave);

return result;

}

}

分析延迟信息

数据默认配置的情况,在内网从库同步的时间延迟,在 200ms 左右,当然这个统计是不精确的,只是看个大概情况,理论值应该是可以做毫秒级。

886c6ea172dae5776eaac3b609570651.png

参考文档:

代码上传至:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值