Sharding JDBC读写分离实现原理及实例

一、核心功能和不支持项

核心功能

  • 提供一主多从的读写分离配置,可独立使用,也可配合分库分表使用。
  • 独立使用读写分离支持SQL透传。
  • 同一线程且同一数据库连接内,如有写入操作,以后的读操作均从主库读取,用于保证数据一致性。
  • 基于Hint的强制主库路由。

不支持项

  • 主库和从库的数据同步(所以需要另外实现主从同步,如使用Mysql的binlog实现)。
  • 主库和从库的数据同步延迟导致的数据不一致。
  • 主库双写或多写。
  • 跨主库和从库之间的事务的数据不一致。主从模型中,事务中读写均用主库。

#涉及到的库及表

CREATE DATABASE ds_master DEFAULT CHARACTER SET UTF8 COLLATE utf8_general_ci;
CREATE DATABASE ds_slave0 DEFAULT CHARACTER SET UTF8 COLLATE utf8_general_ci;
CREATE DATABASE ds_slave1 DEFAULT CHARACTER SET UTF8 COLLATE utf8_general_ci;

CREATE TABLE t_user(
 user_id BIGINT(20) PRIMARY KEY,
 user_name VARCHAR(40)
);

CREATE TABLE t_order(
 order_id BIGINT(20) PRIMARY KEY,
 user_id BIGINT(20),
 order_num VARCHAR(40)
);

二、不使用Spring

引入maven依赖

<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-core</artifactId>
<version>4.1.1</version>
</dependency>

这里使用到Mysql和dbcp2数据源

<!-- dbcp2 -->
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-dbcp2</artifactId>
  <version>2.7.0</version>
</dependency>
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-pool2</artifactId>
  <version>2.7.0</version>
</dependency>

<!-- mysql-->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.49</version>
</dependency>

基于Java编码的规则配置

/*
 * 读写分离
 * ① 插入、更新、删除只会影响主库的数据,即从库的数据不会被影响(不会同步插入、更新、删除)。因为Sharding-JDBC并没有主从库数据同步的功能。
 *  所以我们如果使用的是MySQL,可以采用binlog的方法进行同步。总之需要开发者额外处理
 * ② 查询时,如果主库没有数据,从库有数据,可以查询到数据,所以删除的时候必须保证主库和从库一起删除。
 */

// 配置真实数据源
Map<String, DataSource> dataSourceMap = new HashMap<>();

// 配置主库
BasicDataSource masterDataSource = new BasicDataSource();
masterDataSource.setDriverClassName("com.mysql.jdbc.Driver");
masterDataSource.setUrl("jdbc:mysql://localhost:3305/ds_master");
masterDataSource.setUsername("root");
masterDataSource.setPassword("123456");
dataSourceMap.put("ds_master", masterDataSource);

// 配置第一个从库
BasicDataSource slaveDataSource1 = new BasicDataSource();
slaveDataSource1.setDriverClassName("com.mysql.jdbc.Driver");
slaveDataSource1.setUrl("jdbc:mysql://localhost:3305/ds_slave0");
slaveDataSource1.setUsername("root");
slaveDataSource1.setPassword("123456");
dataSourceMap.put("ds_slave0", slaveDataSource1);

// 配置第二个从库
BasicDataSource slaveDataSource2 = new BasicDataSource();
slaveDataSource2.setDriverClassName("com.mysql.jdbc.Driver");
slaveDataSource2.setUrl("jdbc:mysql://localhost:3305/ds_slave1");
slaveDataSource2.setUsername("root");
slaveDataSource2.setPassword("123456");

dataSourceMap.put("ds_slave1", slaveDataSource2);

// 配置读写分离规则
MasterSlaveRuleConfiguration masterSlaveRuleConfig = new MasterSlaveRuleConfiguration("ds_master_slave", "ds_master", Arrays.asList("ds_slave0", "ds_slave1"));

// 获取数据源对象
DataSource dataSource = MasterSlaveDataSourceFactory.createDataSource(dataSourceMap, masterSlaveRuleConfig, new Properties());
Connection conn = dataSource.getConnection();

// 插入数据
//ShardingKeyGenerator keyGenerator = new SnowflakeShardingKeyGenerator();
//long orderId = ((Long) keyGenerator.generateKey()).longValue();
//long userId = 1027543L;
//ShardingKeyGenerator orderGenerator = new UUIDShardingKeyGenerator();
//String orderNum = (String) orderGenerator.generateKey();
//
//String insertSql = "insert into t_order(order_id, user_id, order_num) values(?, ?, ?)";
//PreparedStatement ps = conn.prepareStatement(insertSql);
//ps.setLong(1, orderId);
//ps.setLong(2, userId);
//ps.setString(3, orderNum);
//int result = ps.executeUpdate();
//System.out.println("执行结果数:" + result);

//读取数据
String querySql = "select * from t_order";
PreparedStatement qryPs = conn.prepareStatement(querySql);
ResultSet resultSet = qryPs.executeQuery();
while (resultSet.next()){
  String ud = resultSet.getString("user_id");
  String om = resultSet.getString("order_num");
  System.out.println(String.format("user_id = [%s], order_num = [%s]", ud, om));
}

// 删除数据
String deleteSql = "delete from t_order where user_id = 1027543";
PreparedStatement dropPs = conn.prepareStatement(deleteSql);
int delResult = dropPs.executeUpdate();
System.out.println("删除结果数:" + delResult);

基于Yaml的规则配置

配置文件sharddb.yml,内容如下:

dataSources:
 ds_master: !!org.apache.commons.dbcp2.BasicDataSource
  driverClassName: com.mysql.jdbc.Driver
  url: jdbc:mysql://localhost:3305/ds_master
  username: root
  password: 123456
 ds_slave0: !!org.apache.commons.dbcp2.BasicDataSource
  driverClassName: com.mysql.jdbc.Driver
  url: jdbc:mysql://localhost:3305/ds_slave0
  username: root
  password: 123456
 ds_slave1: !!org.apache.commons.dbcp2.BasicDataSource
  driverClassName: com.mysql.jdbc.Driver
  url: jdbc:mysql://localhost:3305/ds_slave1
  username: root
  password: 123456

masterSlaveRule:
 name: ds_ms
 masterDataSourceName: ds_master
 slaveDataSourceNames: [ds_slave0, ds_slave1]

props:
 sql.show: true

读取配置文件sharddb.yml:

ClassPathResource pathResource = new ClassPathResource("sharddb.yml");
DataSource dataSource = YamlMasterSlaveDataSourceFactory.createDataSource(pathResource.getFile());
Connection conn = dataSource.getConnection();

// 插入数据
ShardingKeyGenerator keyGenerator = new SnowflakeShardingKeyGenerator();
long orderId = ((Long) keyGenerator.generateKey()).longValue();
long userId = 1027548L;
ShardingKeyGenerator orderGenerator = new UUIDShardingKeyGenerator();
String orderNum = (String) orderGenerator.generateKey();

//String insertSql = "insert into t_order(order_id, user_id, order_num) values(?, ?, ?)";
//PreparedStatement ps = conn.prepareStatement(insertSql);
//ps.setLong(1, orderId);
//ps.setLong(2, userId);
//ps.setString(3, orderNum);
//int result = ps.executeUpdate();
//System.out.println("执行结果数:" + result);

//读取数据
String querySql = "select * from t_order";
PreparedStatement qryPs = conn.prepareStatement(querySql);
ResultSet resultSet = qryPs.executeQuery();
while (resultSet.next()) {
  String ud = resultSet.getString("user_id");
  String om = resultSet.getString("order_num");
  System.out.println(String.format("user_id = [%s], order_num = [%s]", ud, om));
}

三、使用Spring

基于Spring boot的规则配置

① 引入Maven依赖

<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.1</version>
</dependency>

② application.properties内容如下:

# 一主二从,一般都是部署在不同的机器上,数据库是名称是相同的
# jdbc:mysql://192.168.1.12:3306/am_stock
# jdbc:mysql://192.168.1.13:3306/am_stock
# jdbc:mysql://192.168.1.14:3306/am_stock
spring.shardingsphere.datasource.names=master,slave0,slave1

spring.shardingsphere.datasource.master.type=org.apache.commons.dbcp.BasicDataSource
spring.shardingsphere.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master.url=jdbc:mysql://localhost:3306/ds_master
spring.shardingsphere.datasource.master.username=root
spring.shardingsphere.datasource.master.password=123456

spring.shardingsphere.datasource.slave0.type=org.apache.commons.dbcp.BasicDataSource
spring.shardingsphere.datasource.slave0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave0.url=jdbc:mysql://localhost:3306/ds_slave0
spring.shardingsphere.datasource.slave0.username=root
spring.shardingsphere.datasource.slave0.password=123456

spring.shardingsphere.datasource.slave1.type=org.apache.commons.dbcp.BasicDataSource
spring.shardingsphere.datasource.slave1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave1.url=jdbc:mysql://localhost:3306/ds_slave1
spring.shardingsphere.datasource.slave1.username=root
spring.shardingsphere.datasource.slave1.password=123456

spring.shardingsphere.masterslave.name=ms
spring.shardingsphere.masterslave.master-data-source-name=master
spring.shardingsphere.masterslave.slave-data-source-names=slave0,slave1

spring.shardingsphere.props.sql.show=true

③ 直接通过注入的方式即可使用DataSource,或者将DataSource配置在JPA、Hibernate或MyBatis中使用。

@Resource
private DataSource dataSource;

④ 基于Spring boot + JNDI的规则配置

如果您计划使用Spring boot + JNDI的方式,在应用容器(如Tomcat)中使用Sharding-JDBC时,可使用spring.shardingsphere.datasource.${datasourceName}.jndiName来代替数据源的一系列配置。 如:

spring.shardingsphere.datasource.names=master,slave0,slave1

spring.shardingsphere.datasource.master.jndi-name=java:comp/env/jdbc/master
spring.shardingsphere.datasource.slave0.jndi-name=jdbc/slave0
spring.shardingsphere.datasource.slave1.jndi-name=jdbc/slave1

spring.shardingsphere.masterslave.name=ms
spring.shardingsphere.masterslave.master-data-source-name=master
spring.shardingsphere.masterslave.slave-data-source-names=slave0,slave1

spring.shardingsphere.props.sql.show=true

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 利用Sharding-Jdbc组件实现分表

    看到了当当开源的Sharding-JDBC组件,它可以在几乎不修改代码的情况下完成分库分表的实现.摘抄其中一段介绍: Sharding-JDBC直接封装JDBC API,可以理解为增强版的JDBC驱动,旧代码迁移成本几乎为零: 可适用于任何基于java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC. 可基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid等. 理论上可支持任意实现JDB

  • 简单了解redis常见客户端及Sharding机制原理

    1.redis的几种常见客户端: Jedis:是Redis的Java实现客户端,提供了比较全面的Redis命令的支持; Redisson:实现了分布式和可扩展的Java数据结构. Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器. 1)优点: Jedis:比较全面的提供了Redis的操作特性 Redisson:促使使用者对Redis的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过Redis支持延迟队列 Le

  • 详解Spring Boot中整合Sharding-JDBC读写分离示例

    在我<Spring Cloud微服务-全栈技术与案例解析>书中,第18章节分库分表解决方案里有对Sharding-JDBC的使用进行详细的讲解. 之前是通过XML方式来配置数据源,读写分离策略,分库分表策略等,之前有朋友也问过我,有没有Spring Boot的方式来配置,既然已经用Spring Boot还用XML来配置感觉有点不协调. 其实吧我个人觉得只要能用,方便看,看的懂就行了,mybatis的SQL不也是写在XML中嘛. 今天就给大家介绍下Spring Boot方式的使用,主要讲解读写分

  • SpringBoot 2.0 整合sharding-jdbc中间件实现数据分库分表

    一.水平分割 1.水平分库 1).概念:  以字段为依据,按照一定策略,将一个库中的数据拆分到多个库中. 2).结果  每个库的结构都一样:数据都不一样:  所有库的并集是全量数据: 2.水平分表 1).概念  以字段为依据,按照一定策略,将一个表中的数据拆分到多个表中. 2).结果  每个表的结构都一样:数据都不一样:  所有表的并集是全量数据: 二.Shard-jdbc 中间件 1.架构图 2.特点 1).Sharding-JDBC直接封装JDBC API,旧代码迁移成本几乎为零. 2).适

  • spring boot使用sharding jdbc的配置方式

    本文介绍了spring boot使用sharding jdbc的配置方式,分享给大家,具体如下: 说明 要排除DataSourceAutoConfiguration,否则多数据源无法配置 @SpringBootApplication @EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) public class Application { public static void main(String[] arg

  • Spring Boot 集成 Sharding-JDBC + Mybatis-Plus 实现分库分表功能

    一. Sharding-jdbc简介 " Sharding-jdbc是开源的数据库操作中间件:定位为轻量级Java框架,在Java的JDBC层提供的额外服务.它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架. 官方文档地址:https://shardingsphere.apache.org/document/current/cn/overview/ 本文demo实现了分库分表功能.如有错误,欢迎各位在评论中指出.不

  • Springboot2.x+ShardingSphere实现分库分表的示例代码

    之前一篇文章中我们讲了基于Mysql8的读写分离(文末有链接),这次来说说分库分表的实现过程. 概念解析 垂直分片 按照业务拆分的方式称为垂直分片,又称为纵向拆分,它的核心理念是专库专用. 在拆分之前,一个数据库由多个数据表构成,每个表对应着不同的业务.而拆分之后,则是按照业务将表进行归类,分布到不同的数据库中,从而将压力分散至不同的数据库. 下图展示了根据业务需要,将用户表和订单表垂直分片到不同的数据库的方案. 垂直分片往往需要对架构和设计进行调整.通常来讲,是来不及应对互联网业务需求快速变化

  • Sharding JDBC读写分离实现原理及实例

    一.核心功能和不支持项 核心功能 提供一主多从的读写分离配置,可独立使用,也可配合分库分表使用. 独立使用读写分离支持SQL透传. 同一线程且同一数据库连接内,如有写入操作,以后的读操作均从主库读取,用于保证数据一致性. 基于Hint的强制主库路由. 不支持项 主库和从库的数据同步(所以需要另外实现主从同步,如使用Mysql的binlog实现). 主库和从库的数据同步延迟导致的数据不一致. 主库双写或多写. 跨主库和从库之间的事务的数据不一致.主从模型中,事务中读写均用主库. #涉及到的库及表

  • SpringBoot自定义注解使用读写分离Mysql数据库的实例教程

    需求场景 为了防止代码中有的SQL慢查询,影响我们线上主数据库的性能.我们需要将sql查询操作切换到从库中进行.为了使用方便,将自定义注解的形式使用. mysql导入的依赖 <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency&

  • Linux如何使用 MyCat 实现 MySQL 主从读写分离

    目录 Linux-使用 MyCat 实现 MySQL 主从读写分离 一.MySQL 读写分离 1.MySQL 读写分离的概述 2.读写分离工作原理 3.为什么要读写分离 3.实现读写分离的方式 4.常见的中间件程序 二.MyCAT简述 1.什么是 MyCAT 2.MyCat 服务安装与配置 三.MyCat 服务启动与启动设置 四.配置 MySQL 主从 五.实战节点宕机后自动切换 Slave 节点 1.mycat 配置文件优化调整 2.停主节点: 3.恢复主节点: Linux-使用 MyCat

  • Mysql数据库的主从复制与读写分离精讲教程

    目录 前言 一.MySQL主从复制 1.支持的复制类型 2.主从复制的工作过程是基于日志 3.请求方式 4.主从复制的原理 5.MySQL集群和主从复制分别适合在什么场景下使用 6.为什么使用主从复制.读写分离 7.用途及条件 8.mysql主从复制存在的问题 9.MySQL主从复制延迟 二.主从复制的形式 三.读写分离 1.原理 2.为什么要读写分离呢? 3.什么时候要读写分离? 5.目前较为常见的MySQL读写分离 四.案例实施 1.案例环境 2.实验思路(解决需求) 3.准备 4.搭建My

  • Spring+MyBatis实现数据读写分离的实例代码

    本文介绍了Spring Boot + MyBatis读写分离,有需要了解Spring+MyBatis读写分离的朋友可参考.希望此文章对各位有所帮助. 其最终实现功能: 默认更新操作都使用写数据源 读操作都使用slave数据源 特殊设置:可以指定要使用的数据源类型及名称(如果有名称,则会根据名称使用相应的数据源) 其实现原理如下: 通过Spring AOP对dao层接口进行拦截,并对需要指定数据源的接口在ThradLocal中设置其数据源类型及名称 通过MyBatsi的插件,对根据更新或者查询操作

  • Yii实现MySQL多数据库和读写分离实例分析

    本文实例分析了Yii实现MySQL多数据库和读写分离的方法.分享给大家供大家参考.具体分析如下: Yii Framework是一个基于组件.用于开发大型 Web 应用的高性能 PHP 框架.Yii提供了今日Web 2.0应用开发所需要的几乎一切功能,也是最强大的框架之一,下文我们来介绍Yii实现MySQL多库和读写分离的方法 前段时间为SNS产品做了架构设计,在程序框架方面做了不少相关的压力测试,最终选定了YiiFramework,至于为什么没选用公司内部的 PHP框架,其实理由很充分,公司的框

  • php实现带读写分离功能的MySQL类完整实例

    本文实例讲述了php实现带读写分离功能的MySQL类.分享给大家供大家参考,具体如下: 概述: 1. 根据sql语句判断是连接读库还是写库 2. 链式调用$this->where()->get() 3. 不同的主机对应不同的实例, 不再多次new 具体代码如下: <?php class DBRWmysql { private static $Instance = null; private $links = array();//链接数组 private $link = null; //当

  • MySQL 读写分离实例详解

    MySQL 读写分离 MySQL读写分离又一好办法 使用 com.mysql.jdbc.ReplicationDriver 在用过Amoeba 和 Cobar,还有dbware 等读写分离组件后,今天我的一个好朋友跟我讲,MySQL自身的也是可以读写分离的,因为他们提供了一个新的驱动,叫 com.mysql.jdbc.ReplicationDriver 说明文档:http://dev.mysql.com/doc/refman/5.1/en/connector-j-reference-replic

  • Spring AOP切面解决数据库读写分离实例详解

    Spring AOP切面解决数据库读写分离实例详解 为了减轻数据库的压力,一般会使用数据库主从(master/slave)的方式,但是这种方式会给应用程序带来一定的麻烦,比如说,应用程序如何做到把数据写到master库,而读取数据的时候,从slave库读取.如果应用程序判断失误,把数据写入到slave库,会给系统造成致命的打击. 解决读写分离的方案很多,常用的有SQL解析.动态设置数据源.SQL解析主要是通过分析sql语句是insert/select/update/delete中的哪一种,从而对

  • Python实现自定义读写分离代码实例

    这篇文章主要介绍了Python实现自定义读写分离代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 思路 自定义Session类 重写get_bind方法 根据self._flushing判断读写操作, 选择对应的数据库 自定义SQLAlchemy类 重写create_session, 在其中使用自定义的Session类 from flask import Flask from flask_sqlalchemy import SQLAlch

随机推荐