springboot整合ShardingSphere5-0-0-

2021-01-09  本文已影响0人  haiyong6

续上篇springboot整合ShardingSphere5-0-0-alpha实现分库分表示例,
在上篇的基础上,略作修改则可实现读写分离配置。

关于读写分离

在平时项目中,我觉得更加常用的是分库分表数据分片,读写分离比较少用到,对于某些特殊场景,还是有一定意义,比如,对于订单或日志这种只增不减,流量较大时,写入场景对数据库的压力会影响到读取速度,就比较适合做读写分离,主从配置,一主多从,主库负责写入和修改删除等操作,从库只负责读取,从而写入数据库不会影响读取的性能,这里要保证主从数据库同步。主从配置是异步更新数据到从库的,所以写入之后肯定没那么快立马在从库看到,会有时间差,所以对于某些即时性要求高的场景,写入主库后,要么还是读主库,要么加redis缓存。
但是不一定非要用读写分离,读写分离的意义是缓解数据库的压力和访问速度,但是也带来了主从数据库同步,数据一致性等问题,缓解数据库压力的方法有很多,比如利用mysql+redis+elasticsearch等也可以达到同样甚至更好的效果,对于多数场景应该优先考虑使用缓存和搜索引擎,有点跑题了下面还是示例读写分离。

ShardingSphere5-0-0-alpha读写分离配置

ShardingSphere5-0-0-alpha提供的读写分离配置比数据库分片配置要简单的多,只要指定主从数据库,指定读库负载均衡算法(ReplicaLoadBalanceAlgorithm)配置就可以了,支持ROUND_ROBIN:轮询算法 RANDOM:随机访问算法两种负载均衡算法。
需要说明的是ShardingSphere5-0-0-alpha只是提供访问数据库的配置,不会做主从数据库数据同步的操作,数据同步可以在数据库里设置,这里先不考虑。
ShardingSphere5-0-0-alpha官方文档里有介绍:
“同一线程且同一数据库连接内,如有写入操作,以后的读操作均从主库读取,用于保证数据一致性”,所以ShardingSphere还是做了不少事。
在上篇的基础上只需要修改如下数据源配置:

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

        // 配置第 1 个数据源
        DruidDataSource dataSource1 = new DruidDataSource();
        dataSource1.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource1.setUrl("jdbc:mysql://127.0.0.1:3306/zhaohy?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=UTC");
        dataSource1.setUsername("root");
        dataSource1.setPassword("root");
        dataSourceMap.put("master", dataSource1);

        // 配置第 2 个数据源
        DruidDataSource dataSource2 = new DruidDataSource();
        dataSource2.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource2.setUrl("jdbc:mysql://127.0.0.1:3306/zhaohy1?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=UTC");
        dataSource2.setUsername("root");
        dataSource2.setPassword("root");
        dataSourceMap.put("slave0", dataSource2);

        List<String> slaveList = new ArrayList<String>();
        slaveList.add("slave0");
        
         //配置读写分离规则
        List<ReplicaQueryDataSourceRuleConfiguration> configurations = new ArrayList<>();
        configurations.add(new ReplicaQueryDataSourceRuleConfiguration("ds", "master", slaveList, "load_balancer"));
        Map<String, ShardingSphereAlgorithmConfiguration> loadBalancers = new HashMap<>();
        //ROUND_ROBIN:轮询算法 RANDOM:随机访问算法
        loadBalancers.put("load_balancer", new ShardingSphereAlgorithmConfiguration("ROUND_ROBIN", new Properties()));
        ReplicaQueryRuleConfiguration ruleConfiguration = new ReplicaQueryRuleConfiguration(configurations, loadBalancers);
        List<RuleConfiguration> ruleConfigurationList = new ArrayList<RuleConfiguration>();
        ruleConfigurationList.add(ruleConfiguration);
        
        DataSource dataSource = null;
        try {
            dataSource = ShardingSphereDataSourceFactory.createDataSource(dataSourceMap, ruleConfigurationList, new Properties());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        logger.info("datasource : {}", dataSource);
        return dataSource;
    }

上面代码中,指定两个数据库,主库master(zhaohy库)和从库slave0(zhaohy1库)(master和slave0名称可以自定义),从库还可以加多个,负载均衡算法指定的是ROUND_ROBIN:轮询算法,配置好之后下面在两个库都存在的t_test_0表里分别插入一条数据:
zhaohy库t_test_0:

insert into t_test_0 (title,author,date,title_id,column_id) values('Replica读写分离测试','zhaohy',now(),4,4);

zhaohy1库t_test_0:

insert into t_test_0 (title,author,date,title_id,column_id) values('Replica读写分离测试','zhaohySlave',now(),4,4);

把t_test_0的表结构贴一下:

CREATE TABLE `t_test_0` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(100) NOT NULL,
  `author` varchar(40) NOT NULL,
  `date` datetime DEFAULT NULL,
  `title_id` varchar(32) DEFAULT NULL,
  `column_id` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8

可以看到主库author字段插入的是'zhaohy',从库author字段插入的是'zhaohySlave',下面写测试接口,一个读取接口,一个插入接口,一个修改接口,一个删除接口,如果查t_test_0表查出来是zhaohySlave,说明读取用的是从库,如果插入之后在主库可以查询到说明插入用的是主库,如果更改之后在主库能看到更改成功说明更改的是主库,如果删除在主库也生效了说明用的也是主库,那就成功了。

测试

controller:

    @RequestMapping("/test/replicaSelect.do")
    public void replicaSelect(HttpServletRequest request) {
        testService.replicaSelect();
    }
    @RequestMapping("/test/insertReplicaSelect.do")
    public void insertReplicaSelect(HttpServletRequest request) {
        testService.insertReplicaSelect();
    }
    @RequestMapping("/test/updateReplicaSelect.do")
    public void updateReplicaSelect(HttpServletRequest request) {
        testService.updateReplicaSelect();
    }
    @RequestMapping("/test/deleteReplicaSelect.do")
    public void deleteReplicaSelect(HttpServletRequest request) {
        testService.deleteReplicaSelect();
    }

serviceImpl:

    @Override
    public void replicaSelect() {
        List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
        Map<String, Object> paramsMap = new HashMap<String, Object>();
        list = testMapper.replicaSelect(paramsMap);
        System.out.println("===" + JSON.toJSONString(list));
        
    }
    @Override
    public void insertReplicaSelect() {
        Map<String, Object> paramsMap = new HashMap<String, Object>();
        //paramsMap.put("id", "2");
        paramsMap.put("columnId", 6);
        paramsMap.put("title", "Replica读写分离测试写入");
        paramsMap.put("author", "zhaohyMaster");
        paramsMap.put("titleId", 6);
        testMapper.insertReplicaSelect(paramsMap);
        System.out.println("插入完成!");
    }


    @Override
    public void updateReplicaSelect() {
        Map<String, Object> paramsMap = new HashMap<String, Object>();
        paramsMap.put("title", "Replica读写分离测试修改");
        paramsMap.put("author", "zhaohyMaster");
        testMapper.updateReplicaSelect(paramsMap);
        System.out.println("修改完成!");
    }


    @Override
    public void deleteReplicaSelect() {
        Map<String, Object> paramsMap = new HashMap<String, Object>();
        paramsMap.put("author", "zhaohyMaster");
        testMapper.deleteReplicaSelect(paramsMap);
        System.out.println("删除完成!");
    }

mapper:

List<Map<String, Object>> replicaSelect(Map<String, Object> paramsMap);
void insertReplicaSelect(Map<String, Object> paramsMap);

void updateReplicaSelect(Map<String, Object> paramsMap);

void deleteReplicaSelect(Map<String, Object> paramsMap);

xml:

<select id="replicaSelect" resultType="java.util.HashMap">
        select * from t_test_0
</select>
<insert id="insertReplicaSelect">
        insert into t_test_0 (title,author,date,title_id,column_id)values(#{title},#{author},now(),#{titleId},#{columnId})
</insert>
    
<update id="updateReplicaSelect">
    update t_test_0 set title = #{title} where author = #{author}
</update>
    
<delete id="deleteReplicaSelect">
    delete from t_test_0 where author = #{author}
</delete>

启动项目请求查询接口,控制台打印出来的json如下:

===[{"date":1610134448000,"column_id":"4","author":"zhaohySlave","title_id":"4","id":6,"title":"Replica读写分离测试"}]

查询出来的author是zhaohySlave,说明查的是从库,插入之后在主库能查到,说明插入的是主库,更改之后也是主库生效了,删除也在主库生效了,读写分离成功。

上一篇 下一篇

猜你喜欢

热点阅读