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

本文介绍了如何在项目中实现MySQL的读写分离,通过主从复制确保数据一致性。文章详细讲解了Sharding-JDBC作为轻量级中间件在读写分离中的应用,包括配置步骤、数据源获取以及延迟测试,提供了相关代码示例和官方文档链接,帮助开发者理解和实践读写分离技术。
摘要由CSDN通过智能技术生成

上篇文章中,在两个 windows 系统的电脑上安装了最新版 8.0.21 MySQL 数据库,并且配置了主从。MySQL如何配置读写分离?

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

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

解决了数据库怎么配的问题,继续探索一下,项目中如何实现读写分离

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

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

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

既然数据库主从环境准备好了,那就搭建项目玩一把。

maven 依赖的库

<dependency>  <groupId>org.apache.shardingspheregroupId>  <artifactId>sharding-jdbc-coreartifactId>  <version>4.1.1version>dependency><dependency>  <groupId>com.zaxxergroupId>  <artifactId>HikariCPartifactId>  <version>3.4.5version>dependency><dependency>  <groupId>mysqlgroupId>  <artifactId>mysql-connector-javaartifactId>  <version>8.0.21version>dependency>

参照官网文档配置与修改读写分离的 demo 程序

70211ca474d2751c014edd1b94282671.png

获取数据源的工具类

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<String, DataSource> createDataSourceMap() {        Map<String, DataSource> result = new HashMap<>();        result.put("ds_master", dsMaster);        result.put("ds_slave", dsSlave);        return result;    }}

分析延迟信息

数据默认配置的情况,在内网从库同步的时间延迟,在 200ms 左右,当然这个统计是不精确的,只是看个大概情况。

0f294fde432ed525dd4f8ec1ba0e2544.png

为了更能看出 Sharding-JDBC 原汁原味的作用,这里并未使用 Spring。结合使用 Spring、Spring Boot 使用 Sharding-JDBC 的配置与代码示例,官网中也已给出,可参照文档实现。

7d5b17bbadf064f634d5ca2a4caac538.png

参考文档:

  • https://shardingsphere.apache.org/document/legacy/4.x/document/en/manual/sharding-jdbc/configuration/config-java/#read-write-split

  • https://shardingsphere.apache.org/document/legacy/4.x/document/en/manual/sharding-jdbc/usage/read-write-splitting/

代码上传至:

  • https://github.com/ConstXiong/toy/tree/master/demo/shardingsphere-read-write-split

面试题均收录于小程序

留言区

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值