环境概览

  前言介绍

  Sharding-JDBC是当当网的一个开源项目,只需引入jar即可轻松实现读写分离与分库分表。与MyCat不同的是,Sharding-JDBC致力于提供轻量级的服务框架,无需额外部署,底层是对JDBC进行增强,兼容各种连接池和ORM框架。不仅如此还提供分布式事务及分布式治理功能,即将出世的3.X版本可能会提供更加全面的功能。有兴趣的小伙伴们,可以去了解下,这里提供官方文档和GitHub地址。

  读写分离

  引自Sharding-JDBC官方文档

  面对日益增加的系统访问量,数据库的吞吐量面临着巨大瓶颈。 对于同一时间有大量并发读操作和较少写操作类型的应用系统来说,将单一的数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善。 通过一主多从的配置方式,可以将查询请求均匀的分散到多个数据副本,能够进一步的提升系统的处理能力。 使用多主多从的方式,不但能够提升系统的吞吐量,还能够提升系统的可用性,可以达到在任何一个数据库宕机,甚至磁盘物理损坏的情况下仍然不影响系统的正常运行。

  虽然读写分离可以提升系统的吞吐量和可用性,但同时也带来了数据不一致的问题,这包括多个主库之间的数据一致性,以及主库与从库之间的数据一致性的问题。并且,读写分离也带来了与数据分片同样的问题,它同样会使得应用开发和运维人员对数据库的操作和运维变得更加复杂。透明化读写分离所带来的影响,让使用方尽量像使用一个数据库一样使用主从数据库,是读写分离中间件的主要功能。

  读写分离,简单来说,就是将DML交给主数据库去执行,将更新结果同步至各个从数据库保持主从数据一致,DQL分发给从数据库去查询,从数据库只提供读取查询操作。读写分离特别适用于读多写少的场景下,通过分散读写到不同的数据库实例上来提高性能,缓解单机数据库的压力。

  这里解释一下什么是DML和DQL?SQL语言四大分类:DQL、DML、DDL、DCL。

  DQL(Data QueryLanguage):数据查询语言,比如select查询语句

  DML(Data Manipulation Language):数据操纵语言,比如insert、delete、update更新语句

  DDL():数据定义语言,比如create/drop/alter等语句

  DCL():数据控制语言,比如grant/rollback/commit等语句

  实现步骤

  实现步骤非常简单,仅需两步,即可在代码上实现读写分离功能,感觉非常带劲。

  1.引入jar包

  

  io.shardingjdbc

  sharding-jdbc-core-spring-boot-starter

  2.0.3

  2.配置读写分离

  sharding: jdbc:

  # 配置真实数据源 datasource: names: ds_master_0,ds_slave_0_1,ds_slave_0_2 # 配置主库 ds_master_0: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://ip:3306/test?useSSL=falseuseUnicode=truecharacterEncoding=utf8autoReconnect=true username: username password: password maxPoolSize: 20

  # 配置第一个从库 ds_slave_0_1: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://ip:3307/test?useSSL=falseuseUnicode=truecharacterEncoding=utf8autoReconnect=true username: username password: password maxPoolSize: 20

  # 配置第二个从库 ds_slave_0_2: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://ip:3308/test?useSSL=falseuseUnicode=truecharacterEncoding=utf8autoReconnect=true username: username password: password maxPoolSize: 20

  # 配置读写分离 config: masterslave:

  # 配置从库选择策略,提供轮询与随机,这里选择用轮询 load-balance-algorithm-type: round_robin name: ds_m_1_s_2 master-data-source-name: ds_master_0 slave-data-source-names: ds_slave_0_1,ds_slave_0_2 sharding: props:

  # 开启SQL显示,默认值: false,注意:仅配置读写分离时不会打印日志!!! sql: show: true

  准备测试

  在测试开始之前,我们先明确一点,由于只配置了读写分离,即使上文中配置了sql.show=true也不会有日志打印出来(如果配置了分库/分表就不会有这种情况),那么我们怎么知道数据库操作到底是走的主库还是主库呢?怎么知道如果走从库有没有遵循轮询算法走的具体是哪个从库呢?

  带着上述的疑问,追溯源码进入MasterSlaveDataSource这个类中(友情提示:IDEA连续按两次shift在弹框中输入MasterSlaveDataSource即可查看该类),主要关注其中的getDataSource()方法。下面贴出关键源码。

  /**

  * Get data source from master-slave data source.

  *

  * @param sqlType SQL type

  * @return data source from master-slave data source

  */

  public NamedDataSource getDataSource(final SQLType sqlType) { if (isMasterRoute(sqlType)) {

  DML_FLAG.set(true); return new NamedDataSource(masterSlaveRule.getMasterDataSourceName(), masterSlaveRule.getMasterDataSource());

  }

  String selectedSourceName = masterSlaveRule.getStrategy().getDataSource(masterSlaveRule.getName(),

  masterSlaveRule.getMasterDataSourceName(), new ArrayList(masterSlaveRule.getSlaveDataSourceMap().keySet()));

  DataSource selectedSource = selectedSourceName.equals(masterSlaveRule.getMasterDataSourceName())

  ? masterSlaveRule.getMasterDataSource() : masterSlaveRule.getSlaveDataSourceMap().get(selectedSourceName);

  Preconditions.checkNotNull(selectedSource, ); return new NamedDataSource(selectedSourceName, selectedSource);

  } private boolean isMasterRoute(final SQLType sqlType) { return SQLType.DQL != sqlType || DML_FLAG.get() || HintManagerHolder.isMasterRouteOnly();

  }

  isMasterRoute() 方法判断当前操作是否应该路由到主库数据源,如果SQL类型是DML则返回true

  getDataSource() 方法根据SQL类型返回一个数据源。如果SQL类型是DQL则通过配置的算法返回一个从库数据源,如果SQL类型是DML则返回主库数据源。

  那么了解了以上两个方法后,通过打断点DEBUG的方式,我们可以很容易的得知,执行SQL时到底走的是哪个库。

  开始测试

  这边我准备了两个测试接口,一个用于测试读操作,一个用于测试写操作。

  @RestController@RequestMapping(/users)public class UserController { @Autowired

  private IUserService userService; /**

  * 查询用户列表

  * @return

  */

  @GetMapping

  public ListgetUser() { return userService.selectList(null);

  } /**

  * 创建/修改用户信息

  * @param user

  * @return

  */

  @PostMapping

  public User saveUser(@RequestBody User user) { return userService.insertOrUpdate(user) ? userService.selectById(user.getId()) : null;

  }

  }

  发起GET请求/users接口,期望通过轮询算法去从库中查询获取数据

  

  第一次,通过上图我们可以很容易发现SQL类型是DQL,走的是ds_slave_0_1从数据库,且策略是轮询策略

  

  第二次,我们可以发现走的是ds_slave_0_2从数据库,读操作和轮询算法都没毛病

  发起POST请求/users接口,期望从主库中创建或修改用户数据。

  

  可见,写操作时,走的是ds_master_0主数据库。当userService.insertOrUpdate(user)执行成功返回true后,接着再执行userService.selectById(user.getId())时,又会走到ds_slave_0_1从库读取数据。写操作也没毛病,以上我们的测试阶段就大功告成了。

  轮询策略

  有兴趣的小伙伴可以看下轮询策略的源码,非常的简单。这里贴出轮询策略主要源码

  /**

  * Round-robin slave database load-balance algorithm.

  *

  * @author zhangliang

  */public final class RoundRobinMasterSlaveLoadBalanceAlgorithm implements MasterSlaveLoadBalanceAlgorithm {

  private static final ConcurrentHashMapCOUNT_MAP = new ConcurrentHashMap();

  @Override

  public String getDataSource(final String name, final String masterDataSourceName, final ListslaveDataSourceNames) {

  AtomicInteger count = COUNT_MAP.containsKey(name) ? COUNT_MAP.get(name) : new AtomicInteger(0);

  COUNT_MAP.putIfAbsent(name, count); count.compareAndSet(slaveDataSourceNames.size(), 0); return slaveDataSourceNames.get(count.getAndIncrement() % slaveDataSourceNames.size());

  }

  }

  其内部通过并发容器ConcurrentHashMap与AtomicInteger的CAS保障高并发下计数线程安全,使用无所的方式比加锁更加高效。

  灵活性

  Sharding-JDBC使用简单,容易上手且十分灵活,不仅可以使用默认策略,还可以使用自定义的策略。可以说是对Java开发者十分的友好,通过写Java代码的方式就可以实现更加深度的定制化路由规则。这里如果想要自定义轮询策略可以使用如下配置来自定义的轮询策略。

  sharding: jdbc: config: masterslave: load-balance-algorith

SpringBoot 玩转读写分离的更多相关文章

  1. SpringBoot使用Sharding-JDBC读写分离

    本文介绍SpringBoot使用当当Sharding-JDBC进行读写分离. 1.有关Sharding-JDBC 本文还是基于当当网Sharding-Jdbc的依赖,与上一篇使用Sharding-Jd ...

  2. SpringBoot+MyBatis+MySQL读写分离

    1.  引言 读写分离要做的事情就是对于一条SQL该选择哪个数据库去执行,至于谁来做选择数据库这件事儿,无非两个,要么中间件帮我们做,要么程序自己做.因此,一般来讲,读写分离有两种实现方式.第一种是依 ...

  3. SpringBoot+MyBatis+MySQL读写分离(实例)

    ​ 1. 引言 读写分离要做的事情就是对于一条SQL该选择哪个数据库去执行,至于谁来做选择数据库这件事儿,无非两个,要么中间件帮我们做,要么程序自己做.因此,一般来讲,读写分离有两种实现方式.第一种是 ...

  4. Springboot + Mysql8实现读写分离

    在实际的生产环境中,为了确保数据库的稳定性,我们一般会给数据库配置双机热备机制,这样在master数据库崩溃后,slave数据库可以立即切换成主数据库,通过主从复制的方式将数据从主库同步至从库,在业务 ...

  5. SpringBoot + MyBatis + MySQL 读写分离实战

    1. 引言 读写分离要做的事情就是对于一条SQL该选择哪个数据库去执行,至于谁来做选择数据库这件事儿,无非两个,要么中间件帮我们做,要么程序自己做.因此,一般来讲,读写分离有两种实现方式.第一种是依靠 ...

  6. 分库分表(3) ---SpringBoot + ShardingSphere 实现读写分离

    分库分表(3)---ShardingSphere实现读写分离 有关ShardingSphere概念前面写了两篇博客: 1.分库分表(1) --- 理论 2. 分库分表(2) --- ShardingS ...

  7. 分库分表(6)--- SpringBoot+ShardingSphere实现分表+ 读写分离

    分库分表(6)--- ShardingSphere实现分表+ 读写分离 有关分库分表前面写了五篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理论 ...

  8. 分库分表(7)--- SpringBoot+ShardingSphere实现分库分表 + 读写分离

    分库分表(7)--- ShardingSphere实现分库分表+读写分离 有关分库分表前面写了六篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理 ...

  9. Django的数据库读写分离

    Django的数据库读写分离 1.首先是配置数据库 在settings.py文件中增加多个数据库的配置: DATABASES = { 'default': { 'ENGINE': 'django.db ...

随机推荐

  1. (3.10)常用知识-T-SQL优化

    关键字:SQL优化 总结: 1.书写问题 2.表连接方式 3.索引的抉择 4.执行计划之参数嗅探,使用提示强制执行计划 5.子查询与表连接的效率 6.临时表.CTE.表变量的选择 7.常用sp与sel ...

  2. PAT 1125 Chain the Ropes[一般]

    1125 Chain the Ropes (25 分) Given some segments of rope, you are supposed to chain them into one rop ...

  3. wget 用法

    wget -r -p -np -k http://xxx.com/xxx

  4. PKU 3318 Matrix Multiplication(随机化算法||状态压缩)

    题目大意:原题链接 给定三个n*n的矩阵A,B,C,验证A*B=C是否成立. 所有解法中因为只测试一组数据,因此没有使用memset清零 Hint中给的傻乎乎的TLE版本: #include<c ...

  5. android系统和ios系统是如何实现推送的,ios为什么没有后台推送

    ios系统为什么没有后台推送? iOS 为了真正地为用户体验负责,不允许应用在后台活动.有了这个限制,但是对于终端设备,应用又是有必要“通知”到达用户的,随时与用户主动沟通起来的(典型的如聊天应用). ...

  6. intellij-idea打包Scala代码在spark中运行

    .创建好Maven项目之后(记得添加Scala框架到该项目),修改pom.xml文件,添加如下内容: <properties> <spark.version></spar ...

  7. spring + groovy 转

    http://www.ibm.com/developerworks/cn/java/j-groovierspring2.html http://blog.csdn.net/qq362228416/ar ...

  8. 机器学习与R语言:NB

    #---------------------------------------- # 功能描述:演示NB建模过程 # 数据集:SMS文本信息 # tm包:维也纳财经大学提供 #----------- ...

  9. 【Head First Servlets and JSP】笔记 28: 过滤器与包装器

    1.过滤器的执行顺序: <url-pattern> 为第一梯队, <servlet-name> 为第二梯队,梯队内的执行顺序和 DD 里的声明顺序相同. When the co ...

  10. 20145103《JAVA程序设计》第十周学习总结

    网络编程 网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据.程序员所作的事情就是把数据发送到指定的位置,或者接收到指定的数据,这个就是狭义的网络编程范畴.在发送和接收数据时,大部分的程序设 ...