11、Spring技术栈-整合Redis,通过Redis的Master-Slave实现缓存数据读写分离
2017-11-13 14:15
966 查看
1、Redis主从复制(Master-Salve Replication)简介
Redis 支持简单且易用的主从复制(master-slave replication)功能, 该功能可以让从服务器(slave server)成为主服务器(master server)的精确复制品。
以下是关于 Redis 复制功能的几个重要方面:
Redis 使用异步复制。 从 Redis 2.8 开始, 从服务器会以每秒一次的频率向主服务器报告复制流(replication stream)的处理进度。
一个主服务器可以有多个从服务器。
不仅主服务器可以有从服务器, 从服务器也可以有自己的从服务器, 多个从服务器之间可以构成一个图状结构。
复制功能不会阻塞主服务器: 即使有一个或多个从服务器正在进行初次同步, 主服务器也可以继续处理命令请求。
复制功能也不会阻塞从服务器: 只要在 redis.conf 文件中进行了相应的设置, 即使从服务器正在进行初次同步, 服务器也可以使用旧版本的数据集来处理命令查询。
不过, 在从服务器删除旧版本数据集并载入新版本数据集的那段时间内, 连接请求会被阻塞。
你还可以配置从服务器, 让它在与主服务器之间的连接断开时, 向客户端发送一个错误。
复制功能可以单纯地用于数据冗余(data redundancy), 也可以通过让多个从服务器处理只读命令请求来提升扩展性(scalability): 比如说, 繁重的 SORT 命令可以交给附属节点去运行。
可以通过复制功能来让主服务器免于执行持久化操作: 只要关闭主服务器的持久化功能, 然后由从服务器去执行持久化操作即可。
更多请参考:Redis主从(Master-Slave)复制(Replication)设置
2、实例介绍
在(Spring技术栈-整合Redis,使用RedisTemplate实现数据缓存实战)一文中我们已经介绍如何整合Redis,使用RedisTemplate实现数据的缓存。本文将在此基础上通过修改,实现缓存数据的读写分离。
首先需要安装两台虚拟机(本文只讲述一主一从的方式,关于一主多从的方式,各位可自行研究)并在虚拟机上安装Redis,具体如何将Redis配置成为主从复制(Master-Salve Replication)模式,请参考Redis主从(Master-Slave)复制(Replication)设置。
具体场景描述:以博客系统为例,用户在发布博客时,系统将博客写入Redis,在详细页面读取博客时,系统从Redis中读取博客信息展示。其中写入时写入到的是Master服务器,读则是从Slave服务器读取。
2.1 在resources目录新建spring-context-redis-ms.xml,写入如下内容
在以上的配置文件中,我们配置了主(Master)服务器和从(Slave)服务器的相关信息,并配置了id为redisTemplate的RedisTemplate对象来操作主(Master)服务器,id为slaveRedisTemplate的RedisTemplate对象来操作从(Slave)服务器。所有对Redis的操作我们都是通过一个叫做MsRedisUtils的帮助类来完成,该类的对象初始化时,我们往构造函数中传入主从(Master-Slave)服务器的RedisTemplate操作对象。
2.2 MsRedisUtils类构建如下
在该类中,所有写操作(set、update、delete)都使用redisTemplate进行操作,所有的读操作(get)都是用slaveRedisTemplate进行操作。
2.3 在spring-context.xml中导入配置信息
在spring-context.xml文件中,加入如下配置信息:
2.4 使用MsRedisUtils类操作Redis
在服务中(本实例是BlogService类),使用MsRedisUtils作为Redis的操作类,实现往Redis服务器写入和读取数据的功能。
源码请见:http://download.csdn.net/download/zyhlwzy/10116300
Redis 支持简单且易用的主从复制(master-slave replication)功能, 该功能可以让从服务器(slave server)成为主服务器(master server)的精确复制品。
以下是关于 Redis 复制功能的几个重要方面:
Redis 使用异步复制。 从 Redis 2.8 开始, 从服务器会以每秒一次的频率向主服务器报告复制流(replication stream)的处理进度。
一个主服务器可以有多个从服务器。
不仅主服务器可以有从服务器, 从服务器也可以有自己的从服务器, 多个从服务器之间可以构成一个图状结构。
复制功能不会阻塞主服务器: 即使有一个或多个从服务器正在进行初次同步, 主服务器也可以继续处理命令请求。
复制功能也不会阻塞从服务器: 只要在 redis.conf 文件中进行了相应的设置, 即使从服务器正在进行初次同步, 服务器也可以使用旧版本的数据集来处理命令查询。
不过, 在从服务器删除旧版本数据集并载入新版本数据集的那段时间内, 连接请求会被阻塞。
你还可以配置从服务器, 让它在与主服务器之间的连接断开时, 向客户端发送一个错误。
复制功能可以单纯地用于数据冗余(data redundancy), 也可以通过让多个从服务器处理只读命令请求来提升扩展性(scalability): 比如说, 繁重的 SORT 命令可以交给附属节点去运行。
可以通过复制功能来让主服务器免于执行持久化操作: 只要关闭主服务器的持久化功能, 然后由从服务器去执行持久化操作即可。
更多请参考:Redis主从(Master-Slave)复制(Replication)设置
2、实例介绍
在(Spring技术栈-整合Redis,使用RedisTemplate实现数据缓存实战)一文中我们已经介绍如何整合Redis,使用RedisTemplate实现数据的缓存。本文将在此基础上通过修改,实现缓存数据的读写分离。
首先需要安装两台虚拟机(本文只讲述一主一从的方式,关于一主多从的方式,各位可自行研究)并在虚拟机上安装Redis,具体如何将Redis配置成为主从复制(Master-Salve Replication)模式,请参考Redis主从(Master-Slave)复制(Replication)设置。
具体场景描述:以博客系统为例,用户在发布博客时,系统将博客写入Redis,在详细页面读取博客时,系统从Redis中读取博客信息展示。其中写入时写入到的是Master服务器,读则是从Slave服务器读取。
2.1 在resources目录新建spring-context-redis-ms.xml,写入如下内容
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xmlns:task="http://www.springframework.org/schema/task" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:c='http://www.springframework.org/schema/c' xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd" default-lazy-init="true"> <!-- 开启spring cache注解功能 --> <cache:annotation-driven cache-manager="redisCacheManager" /> <context:annotation-config /> <context:property-placeholder ignore-unresolvable="true" location="classpath:config.properties" /> <!-- Redis --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" > <property name="maxTotal" value="${redis.maxTotal}"/> <property name="maxIdle" value="${redis.maxIdle}" /> <property name="maxWaitMillis" value="${redis.maxWait}" /> <property name="testOnBorrow" value="${redis.testOnBorrow}" /> </bean> <!-- redis主服务器中心 --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" > <property name="poolConfig" ref="poolConfig" /> <property name="port" value="${redis.master.port}" /> <property name="hostName" value="${redis.master.host}" /> <property name="timeout" value="${redis.timeout}" ></property> </bean> <!-- redis从服务器中心 --> <bean id="slaveJedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" > <property name="poolConfig" ref="poolConfig" /> <property name="port" value="${redis.slave.port}" /> <property name="hostName" value="${redis.slave.host}" /> <property name="timeout" value="${redis.timeout}" ></property> </bean> <!-- 主RedisTemplate --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="jedisConnectionFactory" /> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="valueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> </property> </bean> <!-- 从RedisTemplate --> <bean id="slaveRedisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="slaveJedisConnectionFactory" /> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="valueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> </property> </bean> <!-- 主redis缓存管理器 --> <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"> <constructor-arg name="redisOperations" ref="redisTemplate" /> </bean> <!-- 从redis缓存管理器 --> <bean id="slaveRedisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"> <constructor-arg name="redisOperations" ref="slaveRedisTemplate" /> </bean> <bean id="redisUtils" class="ron.blog.blog_service.utils.MsRedisUtils"> <constructor-arg name="redisTemplate" ref="redisTemplate" /> <constructor-arg name="slaveRedisTemplate" ref="slaveRedisTemplate" /> </bean> </beans>
在以上的配置文件中,我们配置了主(Master)服务器和从(Slave)服务器的相关信息,并配置了id为redisTemplate的RedisTemplate对象来操作主(Master)服务器,id为slaveRedisTemplate的RedisTemplate对象来操作从(Slave)服务器。所有对Redis的操作我们都是通过一个叫做MsRedisUtils的帮助类来完成,该类的对象初始化时,我们往构造函数中传入主从(Master-Slave)服务器的RedisTemplate操作对象。
2.2 MsRedisUtils类构建如下
public class MsRedisUtils { /** * RedisTemplate是一个简化Redis数据访问的一个帮助类, * 此类对Redis命令进行高级封装,通过此类可以调用ValueOperations和ListOperations等等方法。 */ private RedisTemplate<Serializable, Object> redisTemplate; private RedisTemplate<Serializable, Object> slaveRedisTemplate; MsRedisUtils(RedisTemplate<Serializable, Object> redisTemplate,RedisTemplate<Serializable, Object> slaveRedisTemplate){ this.redisTemplate=redisTemplate; this.slaveRedisTemplate=slaveRedisTemplate; } /** * 写入缓存 * * @param key * @param value * @return */ public boolean set(final String key, Object value) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } /** * * @Author Ron * @param key * @param hashKey * @param value * @return */ public boolean set(final String key, final String hashKey, Object value) { boolean result = false; try { HashOperations<Serializable,Object,Object> operations = redisTemplate.opsForHash(); operations.put(key, hashKey, value); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } /** * 写入缓存 * * @param key * @param value * @return */ public boolean set(final String key, Object value, Long expireTime) { boolean result = false; try { ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue(); operations.set(key, value); redisTemplate.expire(key, expireTime, TimeUnit.SECONDS); result = true; } catch (Exception e) { e.printStackTrace(); } return result; } /** * 批量删除对应的value * * @param keys */ public void remove(final String... keys) { for (String key : keys) { remove(key); } } /** * 批量删除key * * @param pattern */ public void removePattern(final String pattern) { Set<Serializable> keys = redisTemplate.keys(pattern); if (keys.size() > 0) redisTemplate.delete(keys); } /** * 删除对应的value * @param key */ public void remove(final String key) { if (exists(key)) { redisTemplate.delete(key); } } /** * 缓存是否存在 * @param key * @return */ public boolean exists(final String key) { return slaveRedisTemplate.hasKey(key); } /** * 读取缓存 * @param key * @return */ public Object get(final String key) { Object result = null; ValueOperations<Serializable, Object> operations = slaveRedisTemplate.opsForValue(); result = operations.get(key); return result; } /** * * @Author Ron * @param key * @param hashKey * @return */ public Object get(final String key, final String hashKey){ Object result = null; HashOperations<Serializable,Object,Object> operations = slaveRedisTemplate.opsForHash(); result = operations.get(key, hashKey); return result; } }
在该类中,所有写操作(set、update、delete)都使用redisTemplate进行操作,所有的读操作(get)都是用slaveRedisTemplate进行操作。
2.3 在spring-context.xml中导入配置信息
在spring-context.xml文件中,加入如下配置信息:
<import resource="classpath:spring-context-redis-ms.xml" />
2.4 使用MsRedisUtils类操作Redis
在服务中(本实例是BlogService类),使用MsRedisUtils作为Redis的操作类,实现往Redis服务器写入和读取数据的功能。
@Autowired MsRedisUtils redisUtils; /** * @Comment 添加博客内容 * @Author Ron * @Date 2017年10月25日 下午2:57:51 * @return */ @Override public Resp insertBlog(BlogContent blogContent) { String bid = IdGenerator.genUUID(); blogContent.setBid(bid); blogContent.setCrtTime(new Date()); blogContentMapper.insert(blogContent); redisUtils.set(bid, blogContent); return new Resp(ResCode.SUCCESS, ""); } /** * @Comment 获取博客内容 * @Author Ron * @Date 2017年10月25日 下午3:05:27 * @return */ @Override public BlogContent getBlog(String bid) { if(redisUtils.exists(bid)){ logger.info("缓存命中博客"+bid); return (BlogContent) redisUtils.get(bid); }else{ logger.info("缓存尚未命中博客"+bid); BlogContent blogContent = blogContentMapper.selectByPrimaryKey(bid); redisUtils.set(bid, blogContent); return blogContent; } }
源码请见:http://download.csdn.net/download/zyhlwzy/10116300
相关文章推荐
- 10、Spring技术栈-整合Redis,使用RedisTemplate实现数据缓存实战
- 分布式缓存技术redis学习系列(五)——redis实战(redis与spring整合,分布式锁实现)
- spring boot 整合redis对查询数据做缓存( 利用spring的AOP技术)
- redis整合spring实现对数据的缓存
- 分布式缓存技术redis学习系列(五)——redis实战(redis与spring整合,分布式锁实现)
- 分布式缓存技术redis学习系列(五)——redis实战(redis与spring整合,分布式锁实现)
- spring整合redis实现数据缓存的实例代码
- spring boot 整合redis对查询数据做缓存( 利用spring的AOP技术)
- spring + redis 实现数据的缓存
- 分布式缓存技术redis学习系列(五)——spring-data-redis与JedisPool的区别、使用ShardedJedisPool与spring集成的实现及一致性哈希分析
- spring boot整合redis实现缓存机制
- spring + redis 实现数据的缓存
- spring结合redis如何实现数据的缓存
- redis作为mysql的缓存服务器(读写分离,通过mysql触发器实现数据同步)
- spring+springmvc+mybatis+redis 实现两重数据缓存
- Spring Boot 整合 Redis 实现缓存操作
- spring + redis 实现数据的缓存
- Spring Boot 整合 Redis 实现缓存操作
- spring + redis 实现数据的缓存