(1)业务数据常用吗?使用率如何?
如果使用率较低,就没必要写入缓存。
(2)该业务是读操作多,还是写操作多?
如果写操作多,频繁需要写入数据库,也没必要使用缓存。
(3)业务数据大小如何?
如果要存储几百兆字节的文件,会给缓存带来很大的压力,这样也没必要。
在考虑了这些问题之后,如果觉得有必要使用缓存,那么就使用它!使用 Redis 作为缓存的读取逻辑如下图所示:
从上图我们可以知道以下两点:
(1)当第一次读取数据的时候,读取Redis的数据就会失败,此时就会触发程序读取数据库,把数据读取出来,并且写入Redis中
(2)当第二次以及以后需要读取数据时,就会直接读取Redis,读取数据后就结束了流程,这样速度大大提高了。
从上面的分析可以知道,读操作的可能性是远大于写操作的,所以使用 Redis 来处理日常中需要经常读取的数据,速度提升是显而易见的,同时也降低了对数据库的依赖,使得数据库的压力大大减少。
分析了读操作的逻辑,下面我们来看看写操作流程:
从流程可以看出,更新或者写入的操作,需要多个 Redis 的操作,如果业务数据写次数远大于读次数那么就没有必要使用 Redis。
)2、高速读写场合
在如今的互联网中,越来越多的存在高并发的情况,比如天猫双11、抢红包、抢演唱会门票等,这些场合都是在某一个瞬间或者是某一个短暂的时刻有成千上万的请求到达服务器,如果单纯的使用数据库来进行处理,就算不崩,也会很慢的,轻则造成用户体验极差用户量流水,重则数据库瘫痪,服务宕机,而这样的场合都是不允许的!
所以我们需要使用 Redis 来应对这样的高并发需求的场合,我们先来看看一次请求操作的流程:
我们来进一步阐述这个过程:
(1)当一个请求到达服务器时,只是把业务数据在Redis上进行读写,而没有对数据库进行任何的操作,这样就能大大提高读写的速度,从而满足高速相应的需求。
(2)但是这些缓存的数据仍然需要持久化,也就是存入数据库之中,所以在一个请求操作完Redis的读写之后,会去判断该高速读写的业务是否结束,这个判断通常会在秒杀商品为0,红包金额为0时成立,如果不成立,则不会操作数据库;如果成立,则触发事件将Redis的缓存的数据以批量的形式一次性写入数据库,从而完成持久化的工作。
九、在spring中使用Redis
上面说到了 Redis 无法操作对象的问题,无法在那些基础类型和 Java 对象之间方便的转换,但是在 Spring 中,这些问题都可以通过使用RedisTemplate得到解决!
想要达到这样的效果,除了 Jedis 包以外还需要在 Spring 引入 spring-data-redis 包。
)1、使用spring配置JedisPoolConfig对象
大部分的情况下,我们还是会用到连接池的,于是先用 Spring 配置一个 JedisPoolConfig 对象:
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!--最大空闲数--> <property name="maxIdle" value="50"/> <!--最大连接数--> <property name="maxTotal" value="100"/> <!--最大等待时间--> <property name="maxWaitMillis" value="20000"/> </bean>
好了,我们现在配置好了连接池的相关属性,那么具体使用哪种工厂实现呢?在Spring Data Redis中有四种可供我们选择的工厂模型,它们分别是:
JredisConnectionFactory
JedisConnectionFactory
LettuceConnectionFactory
SrpConnectionFactory
我们这里就简单配置成JedisConnectionFactory:
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <!--Redis服务地址--> <property name="hostName" value="localhost"/> <!--端口号--> <property name="port" value="6379"/> <!--如果有密码则需要配置密码--> <!--<property name="password" value="password"/>--> <!--连接池配置--> <property name="poolConfig" ref="poolConfig"/> </bean>
普通的连接根本没有办法直接将对象直接存入 Redis 内存中,我们需要替代的方案:将对象序列化(可以简单的理解为继承Serializable接口)。我们可以把对象序列化之后存入Redis缓存中,然后在取出的时候又通过转换器,将序列化之后的对象反序列化回对象,这样就完成了我们的要求:
RedisTemplate可以帮助我们完成这份工作,它会找到对应的序列化器去转换Redis的键值:
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="connectionFactory"/>
首先编写好支持我们测试的POJO类:
/** * @author: 素小暖 * @create: 2020-2-12 */ public class Student implements Serializable{ private String name; private int age; /** * 给该类一个服务类用于测试 */ public void service() { System.out.println("学生名字为:" + name); System.out.println("学生年龄为:" + age); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
然后编写测试类:
@Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); RedisTemplate redisTemplate = context.getBean(RedisTemplate.class); Student student = new Student(); student.setName("我没有三颗心脏"); student.setAge(21); redisTemplate.opsForValue().set("student_1", student); Student student1 = (Student) redisTemplate.opsForValue().get("student_1"); student1.service(); }
十、springboot中使用Redis
)1、在springboot中添加Redis依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
# REDIS (RedisProperties) # Redis数据库索引(默认为0) spring.redis.database=0 # Redis服务器地址 spring.redis.host=localhost # Redis服务器连接端口 spring.redis.port=6379 # Redis服务器连接密码(默认为空) spring.redis.password= # 连接池最大连接数(使用负值表示没有限制) spring.redis.pool.max-active=8 # 连接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.pool.max-wait=-1 # 连接池中的最大空闲连接 spring.redis.pool.max-idle=8 # 连接池中的最小空闲连接 spring.redis.pool.min-idle=0 # 连接超时时间(毫秒) spring.redis.timeout=0
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest() public class ApplicationTests { @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void test() throws Exception { // 保存字符串 stringRedisTemplate.opsForValue().set("aaa", "111"); Assert.assertEquals("111", stringRedisTemplate.opsForValue().get("aaa")); } }
通过上面这段极为简单的测试案例演示了如何通过自动配置的StringRedisTemplate对象进行Redis的读写操作,该对象从命名中就可注意到支持的是String类型。原本是RedisTemplate<K, V>接口,StringRedisTemplate就相当于RedisTemplate<String, String>的实现。
这一步跟上面使用Spring一样,只需要将POJO类实现Serializable接口就可以了,我这里就贴一下测试代码:
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest() public class ApplicationTests { @Autowired private RedisTemplate redisTemplate; @Test public void test() throws Exception { User user = new User(); user.setName("我没有三颗心脏"); user.setAge(21); redisTemplate.opsForValue().set("user_1", user); User user1 = (User) redisTemplate.opsForValue().get("user_1"); System.out.println(user1.getName()); } }