续上篇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,说明查的是从库,插入之后在主库能查到,说明插入的是主库,更改之后也是主库生效了,删除也在主库生效了,读写分离成功。
网友评论