使用Spring Data Redis实现数据缓存

引言

目前很多系统为了解决数据读写的性能瓶颈,在系统架构设计中使用Redis实现缓存,Spring框架为了让开发人员更加方便快捷的使用Redis实现缓存,对Redis的操作进行了包装。

0.缓存

个人理解的缓存是指用于存储频繁使用的数据的空间,关注点是存储数据的空间和使用频繁的数据。缓存技术,简单的说就是先从缓存中查询数据是否存在,存在则直接返回,不存在再执行相应的操作获取数据,并将获取的数据存储到缓存中,它是一种提升系统性能的重要方法。

1.Redis

Redis是一个开源的、内存存储key-value类型的数据结构服务器,可用作数据库、高速缓存和消息队列代理。它支持的数据类型有字符串、哈希表、列表、集合、有序集合等,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供分区功能。

2.jedis

jedis是Redis的Java版客户端实现,也是官方推荐的Java版客户端。它封装了对Redis的各种操作,并且支持事务、管道及有jedis自身实现的分布式。

3.Spring Data Redis

Spring Data是Spring框架中的一个主要项目,目的是为了简化构建基于Spring框架应用的数据访问,包括非关系数据库、Map-Reduce框架、云数据服务等,另外也包含对关系数据库的访问支持。

Spring Data Redis是Spring Data项目中的一个主要模块,实现了对jedis客户端API的高度封装,使对Redis的操作更加便捷。

4.关系图

Redis、jedis、Spring Data Redis三者之间的关系图如下所示。

5.Spring Cache

从Spring3.1开始,Spring框架提供了对Cache的支持,提供了一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的作用。提供的主要注解有@Cacheable、@CachePut、@CacheEvict和@Caching,具体见表1。

@Cacheable的常用属性及说明如表2所示。

@CacheEvict的常用属性见表4。@CachePut的常用属性同@Cacheable。

当需要在类上或方法上同时使用多个注解时,可以使用@Caching,如@Caching(cacheable = @Cacheable("User"), evict = {@CacheEvict("Member"), @CacheEvict(value = "Customer", allEntries = true)})

6.使用示例

下面使用Spring Data Reds、Redis和jedis实现一个简单的数据缓存。

1)依赖配置

示例使用了gradle,所以需要在build.gradle中加入如下依赖配置来管理所需要的jar。

compile "org.springframework.data:spring-data-redis:1.7.2.RELEASE"

compile "redis.clients:jedis:2.7.2"

testCompile "junit:junit:4.12"

2)Redis配置

示例连接的是本地的Redis,redis.properties配置如下。

# Redis settings

redis.host=127.0.0.1

redis.port=6379

redis.pass=

redis.dbIndex=0

redis.expiration=3000

redis.maxIdle=300

redis.maxActive=600

redis.maxWait=1000

redis.testOnBorrow=true

3)Spring配置

Spring的配置文件如下。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://www.springframework.org/schema/beans"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:cache="http://www.springframework.org/schema/cache"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">

<context:component-scan base-package="redis.cache"/>

<context:annotation-config/>

<cache:annotation-driven cache-manager="redisCacheManager"/>

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

<property name="locations">

<list>

<value>classpath:redis.properties</value>

</list>

</property>

</bean>

<!-- 配置JedisPoolConfig实例 -->

<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">

<property name="maxIdle" value="${redis.maxIdle}"/>

<property name="maxTotal" value="${redis.maxActive}"/>

<property name="maxWaitMillis" value="${redis.maxWait}"/>

<property name="testOnBorrow" value="${redis.testOnBorrow}"/>

</bean>

<!-- 配置JedisConnectionFactory -->

<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">

<property name="hostName" value="${redis.host}"/>

<property name="port" value="${redis.port}"/>

<property name="password" value="${redis.pass}"/>

<property name="database" value="${redis.dbIndex}"/>

<property name="poolConfig" ref="poolConfig"/>

</bean>

<!-- 配置RedisTemplate -->

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">

<property name="connectionFactory" ref="jedisConnectionFactory"/>

</bean>

<!-- 配置RedisCacheManager -->

<bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">

<constructor-arg name="redisOperations" ref="redisTemplate"/>

<property name="defaultExpiration" value="${redis.expiration}"/>

</bean>

</beans>

4)Service

示例代码的Servicer如下。

@Service("userService")

public class UserService {

@Cacheable(value = "User", key = "'UserId_' + #id",condition = "#id<=110")

public String queryFullNameById(long id) {

System.out.println("execute queryFullNameById method");

return "ZhangSanFeng";

}

@CacheEvict(value = "User", key = "'UserId_' + #id")

public void deleteById(long id) {

System.out.println("execute deleteById method");

}

@CachePut(value = "User", key = "'UserId_' + #id")

public String modifyFullNameById(long id, String newName) {

System.out.println("execute modifyFullNameById method");

return newName;

}

}

5)测试

@Test

public void test() {

ApplicationContext context = new ClassPathXmlApplicationContext("redisCacheContext.xml");

UserService userService = (UserService) context.getBean("userService");

System.out.println("第一次执行查询:" + userService.queryFullNameById(110L));

System.out.println("----------------------------------");

System.out.println("第二次执行查询:" + userService.queryFullNameById(110L));

System.out.println("----------------------------------");

userService.deleteById(110L);

System.out.println("----------------------------------");

System.out.println("清除缓存后查询:" + userService.queryFullNameById(110L));

System.out.println("----------------------------------");

System.out.println(userService.modifyFullNameById(110L, "ZhangJunBao"));

System.out.println("----------------------------------");

System.out.println("修改数据后查询:" + userService.queryFullNameById(110L));

System.out.println("----------------------------------");

System.out.println("第一次执行查询:" + userService.queryFullNameById(112L));

System.out.println("----------------------------------");

System.out.println("第二次执行查询:" + userService.queryFullNameById(112L));

System.out.println("----------------------------------");

}

6)测试结果

输出结果如下。

execute queryFullNameById method

第一次执行查询:ZhangSanFeng

----------------------------------

第二次执行查询:ZhangSanFeng

----------------------------------

execute deleteById method

----------------------------------

execute queryFullNameById method

清除缓存后查询:ZhangSanFeng

----------------------------------

execute modifyFullNameById method

ZhangJunBao

----------------------------------

修改数据后查询:ZhangJunBao

----------------------------------

execute queryFullNameById method

第一次执行查询:ZhangSanFeng

----------------------------------

execute queryFullNameById method

第二次执行查询:ZhangSanFeng

----------------------------------

从结果可以看到,使用缓存后,第二次查询没有执行查询方法体,直接返回了缓存中的数据;清除缓存后,再次查询就执行了查询方法体;修改数据后,相应的缓存数据也被修改了;不符合缓存条件的数据没有被缓存。

本文分享自微信公众号 - JavaQ(Java-Q)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-09-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序人生

应用开发中的网络安全

最近有个朋友让我帮忙看看他系统中的一个问题:他给了我一个用户名密码,让我ssh到他的某台服务器上。那是一台redis server,里面存放数据库查询的缓存和其...

38640
来自专栏智能大石头

借助Redis做秒杀和限流的思考

最近群里聊起秒杀和限流,我自己没有做过类似应用,但是工作中遇到过更大的数据和并发。 于是提出了一个简单的模型: var count = rds.inc(key)...

1.6K40
来自专栏Porschev[钟慰]的专栏

Nodejs学习笔记(九)--- 与Redis的交互(mranney/node_redis)入门

简介和安装 redis简介: 开源高性能key-value存储;采用内存中(in-memory)数据集的方式,也可以采用磁盘存储方式(前者性能高,但数据可能丢失...

46880
来自专栏美团技术团队

Redis高负载下的中断优化

Redis 服务端的总体请求量从年初最开始日访问量百亿次级别上涨到高峰时段的万亿次级别,给运维和架构团队都带来了极大的挑战。

1.4K110
来自专栏Golang语言社区

golang基于redis lua封装的优先级去重队列

前言: 前两天由于某几个厂商的api出问题,导致后台任务大量堆积,又因为我这边任务流系统会重试超时任务,所以导致队列中有大量的重复任务。这时候我们要临时解决两个...

384110
来自专栏LanceToBigData

SpringBoot(三)整合Redis

spring boot对常用的数据库支持外,对nosql 数据库也进行了封装自动化。 redis介绍 Redis是目前业界使用最广泛的内存数据存储。相比memc...

35940
来自专栏云技术

性能超前,详解腾讯云新一代Redis缓存数据库

当前内存数据库发展迅速,用户对于存储系统的要求也越来越高,为了满足各类业务场景的需要,腾讯云设计了新一代的内存数据库,不但保留了原来系统的高性能,高可用等特性,...

681160
来自专栏Golang语言社区

golang基于redis lua封装的优先级去重队列

作者: 峰云 博客: http://xiaorui.cc 前言: 前两天由于某几个厂商的api出问题,导致后台任务大量堆积,又因为我这边任务流系统会重试超时任务...

45790
来自专栏智能大石头

大数据分析中Redis怎么做到220万ops

大数据时代,海量数据分析就像吃饭一样,成为了我们每天的工作。为了更好的为公司提供运营决策,各种抖机灵甚至异想天开的想法都会紧跟着接踵而来!业务多变,决定了必须每...

70960
来自专栏开源项目

signalR+redis 分布式聊天服务器搭建

最近在搞一个直播项目需要聊天服务器,之前是以小打小闹来做的,并没有想太多就只有一台服务器。前几天一下子突然来了5000人,服务器瞬间 gg,作为开发人员的我很尴...

42060

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励