环境概览

  前言介绍

  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. 进程Process之join、daemon(守护)、terminate(关闭)

    一.Process 参数介绍: 1 group参数未使用,值始终为None 2 target表示调用对象,即子进程要执行的任务 3 args表示调用对象的位置参数元组,args=(1,2,'a',) ...

  2. HTML容易遗忘内容(三)

  3. 可能引起「We Were Unable to Load Disqus」错误的一种情况分析

    今天想往Octopress里加评论,之前看了网上的教程,应该是简单的事情,因为Octopress本来为你准备好了评论插件Disqus的代码,只需要去Disqus注册个帐号,然后在_config.yml ...

  4. LVS-DR 配置测试

    LVS Lvs体系结构 Lvs工作模式(3种) NAT-网络地址转换模式 当用户请求到达调度器时,调度器将请求报文的目标地址(即虚拟IP地址)改写成选定的Real Server地址,同时报文的目标端口 ...

  5. docker镜像上传到阿里云

    目前上传本地镜像到网上有多种途径,一个是上传到hub上,一个是阿里云镜像仓库,还要其他服务器. 上传到hub上实在是太慢了,我的服务器用的是阿里云,所以选择上传到阿里云镜像仓库中. 前提条件:linu ...

  6. 如何修改opencart的模版适合为mycncart系统使用

    如何修改opencart的模版适合为mycncart系统使用 mycncart跟随opencart的最新代码不断进行升级,并改造和不断加入中国特色的功能,因此opencart的模版均不能够拿来直接套用 ...

  7. smarty模板(转载)

    一.smarty的程序设计部分: 在smarty的模板设计部分我简单的把smarty在模板中的一些常用设置做了简单的介绍,这一节主要来介绍一下如何在smarty中开始我们程序设计.下载Smarty文件 ...

  8. [Windows Powershell]-学习笔记(1)

      Powershell 快捷键 Powershell的快捷键和cmd,linux中的shell,都比较像.   ALT+F7     清除命令的历史记录 PgUp PgDn     显示当前会话的第 ...

  9. PKU 2352 Stars(裸一维树状数组)

    题目大意:原题链接 就是求每个小星星左小角的星星的个数.坐标按照Y升序,Y相同X升序的顺序给出由于y轴已经排好序,可以按照x坐标建立一维树状数组 关键是要理解树状数组中的c[maxn]数组的构成方式, ...

  10. PKU 1035 Spell checker(Vector+String应用)

    题目大意:原题链接 1输入一个字符串,如果字典中存在该字符串,直接输出,否则; 2.删除,替换,或插入一个字母.如果使得输入字符串==字典中已经有的单词,输出该单词,否则. 3.直接输入下一个字符串, ...