1 什么是更新redis key valueredis是一个key-value 存储系统.和memcached类似

> key-value存储系统
redis是Nosql数据库中使用较为广泛的非关系型内存数据库,redis内部是一个key-value存储系统。
Spring整合Redis用作缓存之注解方式:redis是一个key-value存储系统。和Memcached类似,它支持存
Linux下Redis的安装和使用:Redis是当前比较热门的NOSQL系统之一,它是一个key-value存储系统。
Redis是一个key-value的存储系统,提供的key-value类似与Memcached而数据结构又多于memcached,而且性
linux下redis安装和配置,Redis是当前比较热门的NOSQL系统之一,它是一个key-value存储系统。和M
在Ubuntu上搭建redis服务器。Redis是一个key-value存储系统。和Memcached类似,但是解决了断电后
Redis是当前比较热门的NOSQL系统之一,它是一个key-value存储系统。和Memcache类似,但很大程度
数据库知识学习之redis应用。redis 是key-value存储系统。
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字
在这次实验中,我们将使用Lab2的Raft库来实现容错的key-value存储服务。存储系统将由客户端
SharedPreferences是Android系统提供的一个通用的数据持久化框架,用于存储和读取key-value类型的
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字
Redis是一个高性能的 key-value 对存储系统。它支持存储的value类型很多,包括string(字符串)
Redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字
Java操作Redis。Redis 是一个开源(BSD许可)的,内存中的key-value存储系统,它可以用作数据库
Redis是一个开源的key-value存储系统。与Memcached类似,Redis将大部分数据存储在内存中,支持
REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。 Redis入门。
Redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字
Redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字
热门文章热门标签
10月10日 |
10月10日 |
10月10日 |
10月10日 |
10月10日 |
10月10日 |
10月10日 |
10月10日 |Redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove以及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。
本文将对Redis数据的
Redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove以及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。
本文将对Redis数据的编码方式和底层数据结构进行分析和介绍,帮助读者更好的了解和使用它们。
数据类型和编码方式
Redis中数据对象的定义如下:
typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
其中type对应了Redis中的五种基本数据类型:
\#define OBJ_STRING 0
\#define OBJ_LIST 1
\#define OBJ_SET 2
\#define OBJ_ZSET 3
\#define OBJ_HASH 4
encoding则对应了 Redis 中的十种编码方式:
/* Objects encoding. Some kind of objects like Strings and Hashes can be
* internally represented in multiple ways. The 'encoding' field of the object
* is set to one of this fields for this object. */
\#define OBJ_ENCODING_RAW 0
/* Raw representation */
\#define OBJ_ENCODING_INT 1
/* Encoded as integer */
\#define OBJ_ENCODING_HT 2
/* Encoded as hash table */
\#define OBJ_ENCODING_ZIPMAP 3
/* Encoded as zipmap */ // 已废弃
\#define OBJ_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
\#define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
\#define OBJ_ENCODING_INTSET 6
/* Encoded as intset */
\#define OBJ_ENCODING_SKIPLIST 7
/* Encoded as skiplist */
\#define OBJ_ENCODING_EMBSTR 8
/* Embedded sds string encoding */
\#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
type和encoding的对应关系如下图:
OBJ_ENCODING_RAW
RAW编码方式使用简单动态字符串来保存字符串对象,其具体定义为:
struct sdshdr {
char buf[];
从len字段可以判断并不不依赖于'\0',故可以用与保存二进制对象。
从free字段可以判断其空间分配是采用预分配的方式,避免字符串修改时频繁分配释放内存。具体分配算法为:
sds sdsMakeRoomFor(sds s,size_t addlen) {
if (sdsavail(s) &= addlen)
newlen = sdslen(s)+
if (newlen & SDS_MAX_PREALLOC)
newlen *= 2;
newlen += SDS_MAX_PREALLOC;
sh = (void*) (s-(sizeof(struct sdshdr)));
newsh = zrealloc(sh,sizeof(struct sdshdr)+newlen+1);
if (newsh == NULL) return NULL;
newsh-&free = newlen -
return newsh-&
OBJ_ENCODING_INT
INT编码方式以整数保存字符串数据,仅限能用long类型值表达的字符串。
当robj中的LRU值没有意义的时候(实例没有设置maxmemory限制或者maxmemory-policy设置的淘汰算法中不计算LRU值时), 0-10000之间的OBJ_ENCODING_INT编码的字符串对象将进行共享。
具体算法如下:
len = sdslen(s);
if (len &= 21 && string2l(s,len,&value)) {
/* This object is encodable as a long. Try to use a shared object.
* Note that we avoid using shared integers when maxmemory is used
* because every object needs to have a private LRU field for the LRU
* algorithm to work well. */
if ((server.maxmemory == 0 ||
(server.maxmemory_policy != MAXMEMORY_VOLATILE_LRU &&
server.maxmemory_policy != MAXMEMORY_ALLKEYS_LRU)) &&
value &= 0 &&
value & OBJ_SHARED_INTEGERS)
decrRefCount(o);
incrRefCount(shared.integers[value]);
return shared.integers[value];
if (o-&encoding == OBJ_ENCODING_RAW) sdsfree(o-&ptr);
o-&encoding = OBJ_ENCODING_INT;
o-&ptr = (void*)
OBJ_ENCODING_EMBSTR
从Redis 3.0版本开始字符串引入了EMBSTR编码方式,长度小于OBJ_ENCODING_EMBSTR_SIZE_LIMIT的字符串将以EMBSTR方式存储。
EMBSTR方式的意思是 embedded string ,字符串的空间将会和redisObject对象的空间一起分配,两者在同一个内存块中。
Redis中内存分配使用的是jemalloc,jemalloc分配内存的时候是按照8,16,32,64作为chunk的单位进行分配的。为了保证采用这种编码方式的字符串能被jemalloc分配在同一个chunk中,该字符串长度不能超过64,故字符串长度限制OBJ_ENCODING_EMBSTR_SIZE_LIMIT = 64 - sizeof('\0') - sizeof(robj)为16 - sizeof(struct sdshdr)为8 = 39。
采用这个方式可以减少内存分配的次数,提高内存分配的效率,降低内存碎片率。
robj *createStringObject(const char *ptr,size_t len) {
if (len &= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
return createEmbeddedStringObject(ptr,len);
return createRawStringObject(ptr,len);
robj *createEmbeddedStringObject(const char *ptr,size_t len) {
robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr)+len+1);
o-&type = OBJ_STRING;
o-&encoding = OBJ_ENCODING_EMBSTR;
if (ptr) {
memcpy(sh-&buf,ptr,len);
sh-&buf[len] = '\0';
memset(sh-&buf,0,len+1);
OBJ_ENCODING_ZIPLIST
链表(List),哈希(Hash),有序集合(Sorted Set)在成员较少,成员值较小的时候都会采用压缩列表(ZIPLIST)编码方式进行存储。
这里成员"较少",成员值"较小"的标准可以通过配置项进行配置:
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
压缩列表简单来说就是一系列连续的内存数据块,其内存利用率很高,但增删改查效率较低,所以只会在成员较少,值较小的情况下使用。
其典型结构如下:
|&---- ziplist header ----&|&----------- entries -------------&|&-end-&|
+---------+--------+-------+--------+--------+--------+--------+-------+
| zlbytes | zltail | zllen | entry1 | entry2 |
| entryN | zlend |
+---------+--------+-------+--------+--------+--------+--------+-------+
ZIPLIST_ENTRY_HEAD
ZIPLIST_ENTRY_END
ZIPLIST_ENTRY_TAIL
|&------------------- entry --------------------&|
+------------------+----------+--------+---------+
| pre_entry_length | encoding | length | content |
+------------------+----------+--------+---------+
OBJ_ENCODING_LINKDEDLIST / OBJ_ENCODING_QUICKLIST
在Redis 3.2版本之前,一般的链表使用LINKDEDLIST编码。
在Redis 3.2版本开始,所有的链表都是用QUICKLIST编码。
两者都是使用基本的双端链表数据结构,区别是QUICKLIST每个节点的值都是使用ZIPLIST进行存储的。
// 3.2版本之前
typedef struct list {
listNode *
listNode *
void *(*dup)(void *ptr);
void (*free)(void *ptr);
int (*match)(void *ptr,void *key);
typedef struct listNode {
struct listNode *
struct listNode *
// 3.2版本
typedef struct quicklist {
quicklistNode *
quicklistNode *
/* total count of all entries in all ziplists */
/* number of quicklistNodes */
int fill : 16;
/* fill factor for individual nodes */
unsigned int compress : 16; /* depth of end n0=off */
typedef struct quicklistNode {
struct quicklistNode *
struct quicklistNode *
unsigned char *
/* ziplist size in bytes */
unsigned int count : 16;
/* count of items in ziplist */
unsigned int encoding : 2;
/* RAW==1 or LZF==2 */
unsigned int container : 2;
/* NONE==1 or ZIPLIST==2 */
unsigned int recompress : 1; /* was this node previous compressed? */
unsigned int attempted_compress : 1; /* node can' too small */
unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistN
OBJ_ENCODING_INTSET
整数集合(intset)是集合键的底层实现之一:
当一个集合只包含整数值元素, 并且这个集合的元素数量不多时, Redis 就会使用整数集合作为集合键的底层实现。
robj *setTypeCreate(robj *value) {
if (isObjectRepresentableAsLongLong(value,NULL) == REDIS_OK)
return createIntsetObject();
return createSetObject();
int setTypeAdd(robj *subject,robj *value) {
if (subject-&encoding == REDIS_ENCODING_INTSET) {
if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {
// 当往INTSET里面插入非整数值时,会将集合转成普通的HT编码方式
setTypeConvert(subject,REDIS_ENCODING_HT);
OBJ_ENCODING_HT
字典是Redis中存在最广泛的一种数据结构不仅在哈希对象,集合对象和有序结合对象中都有使用,而且Redis所有的Key,Value都是存在db-&dict这张字典中的。
Redis 的字典使用哈希表作为底层实现。
一个哈希表里面可以有多个哈希表节点,而每个哈希表节点就保存了字典中的一个键值对。
哈希表的定义如下:
typedef struct dictEntry {
uint64_t u64;
int64_t s64;
// Redis 的哈希表使用链地址法(separate chaining)来解决键冲突:
// 每个哈希表节点都有一个 next 指针, 多个哈希表节点可以用 next 指针构成一个单向链表,
// 被分配到同一个索引上的多个节点可以用这个单向链表连接起来, 这就解决了键冲突的问题。
struct dictEntry *
/* This is our hash table structure. */
typedef struct dictht {
dictEntry **
typedef struct dictType {
// 用于计算Hash的函数指针
unsigned int (*hashFunction)(const void *key);
void *(*keyDup)(void *privdata,const void *key);
void *(*valDup)(void *privdata,const void *obj);
int (*keyCompare)(void *privdata,const void *key1,const void *key2);
void (*keyDestructor)(void *privdata,void *key);
void (*valDestructor)(void *privdata,void *obj);
Redis计算Hash值和索引的方式为:
hash = dict-&type-&hashFunction(key);
index = hash & dict-&ht[x].
一个字典里面保存了两个hash表结构,用于哈希表的扩展收缩操作(Rehash)。
/* Every dictionary has two of this as we
* implement incremental rehashing,for the old to the new table.
typedef struct dict {
dictType *
dictht ht[2];
/* rehashing not in progress if rehashidx == -1 */
/* number of iterators currently running */
随着操作的不断执行,哈希表保存的键值对会逐渐地增多或者减少,为了让哈希表的负载(used/size)维持在一个合理的范围之内,当哈希表保存的键值对数量太多或者太少时,程序需要对哈希表的大小进行相应的扩展或者收缩。
具体算法为:
为字典的 ht[1] 哈希表分配空间,这个哈希表的空间大小为:
第一个大于等于(扩展时)或者小于等于(收缩时) ht[0].used * 2 的 2^n(2 的 n 次方幂);
将保存在 ht[0] 中的所有键值对 rehash 到 ht[1] 上面:rehash指的是重新计算键的哈希值和索引值,然后将键值对放置到 ht[1]哈希表的指定位置上;
当 ht[0] 包含的所有键值对都迁移到了 ht[1] 之后(ht[0] 变为空表),释放 ht[0] ,将 ht[1] 设置为 ht[0],并在 ht[1] 新创建一个空白哈希表,为下一次 rehash 做准备。
一次性完成 rehash 过程时间可能很长,Redis采用渐进式 rehash 的方式完成整个过程。
每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定的操作以外,还会顺带将 ht[0] 哈希表在 rehashidx 索引上的所有键值对 rehash 到 ht[1] , 并将 rehashidx 的值增一;
直到整个 ht[0] 全部完成 rehash 后,rehashindex设为-1,释放 ht[0] , ht[1]置为 ht[0], 在 ht[1] 创建一个新的空白表。
OBJ_ENCODING_SKIPLIST
跳跃表(SKIPLIST)编码方式为有序集合对象专用,有序集合对象采用了字典+跳跃表的方式实现。其定义如下:
typedef struct zset {
zskiplist *
其中字典里面保存了有序集合中member与score的键值对,跳跃表则用于实现按score排序的功能。
跳跃表以有序的方式在层次化的链表中保存元素,在一般情况下情况下效率和平衡树媲美, 比起平衡树来说,跳跃表的实现要简单直观得多。
一般的跳跃表结构如下图所示:
跳跃表的基本原理是每一个节点创建的时候层数为随机值,层数越高的几率越小,Redis中每升高一层的概率为1/4。也就是说,一个跳跃表里,一般情况下,每一层的节点数为下一次的 1/4,相当于是一个“随机化”的平衡树。
\#define ZSKIPLIST_P 0.25 /* Skiplist P = 1/4 */ macro
int zslRandomLevel(void) {
int level = 1;
while ((random()&0xFFFF) & (ZSKIPLIST_P * 0xFFFF))
level += 1;
return (level&ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
具体的算法在此就不详细赘述了,与一般的跳跃表实现相比,有序集合中的跳跃表有以下特点:
允许重复的 score 值:多个不同的 member 的 score 值可以相同。
进行对比操作时,不仅要检查 score 值,还要检查 member:当 score 值可以重复时,单靠 score 值无法判断一个元素的身份,所以需要连 member 域都一并检查才行。
每个节点都带有一个高度为1层的后退指针,用于从表尾方向向表头方向迭代:当执行 ZREVRANGE 或ZREVRANGEBYSCORE这类以逆序处理有序集的命令时,就会用到这个属性。
/* ZSETs use a specialized version of Skiplists */
typedef struct zskiplistNode {
// 后退指针,方便 zrev 系列的逆序操作
struct zskiplistNode *
struct zskiplistLevel {
struct zskiplistNode *
} level[];
} zskiplistN
typedef struct zskiplist {
struct zskiplistNode *header,*
知其然,更要知其所以然。
只有深入了解了Redis数据的编码方式和底层数据结构,我们才能更好的了解使用它。在做疑难问题分析和业务优化时,才能更加有的放矢。
云数据库Redis版(ApsaraDB for Redis)是一种稳定可靠、性能卓越、可弹性伸缩的数据库服务。基于飞天分布式系统和全SSD盘高性能存储,支持主备版和集群版两套高可用架构。提供了全套的容灾切换、故障迁移、在线扩容、性能优化的数据库解决方案。欢迎各位购买使用:
用云栖社区APP,舒服~
【云栖快讯】如何应聘互联网公司,拿到高薪Offer?职场菜鸟如何快速成长?阿里学长为你支招,在线直播,足不出户看秘诀,IT新兵快来吧!&&
一种稳定可靠、性能卓越、可弹性伸缩的数据库服务。基于飞天分布式系统和全SSD盘高性能存储,支持主备版和集群版两套...
支持MySQL、SQL Server、PostgreSQL、MongoDB、Redis等关系型数据库和NoSQL...
基于飞天分布式系统和高性能存储,提供三节点副本集的高可用架构,容灾切换,故障迁移完全透明化。并提供专业的数据库在...
为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本...
Loading...如何结合使用Redis? _ 路由器设置|192.168.1.1|无线路由器设置|192.168.0.1 - 路饭网
您的位置: >
> 阅读资讯:如何结合使用Redis?
如何结合使用Redis?
如何结合使用Redis?
一、Redis介绍
什么是Redis?
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
它有什么特点?
(1)Redis数据库完全在内存中,使用磁盘仅用于持久性。
(2)相比许多键值数据存储,Redis拥有一套较为丰富的数据类型。
(3)Redis可以将数据复制到任意数量的从服务器。
Redis 优势?
(1)异常快速:Redis的速度非常快,每秒能执行约11万集合,每秒约81000+条记录。
(2)支持丰富的数据类型:Redis支持最大多数开发人员已经知道像列表,集合,有序集合,散列数据类型。这使得它非常容易解决各种各样的问题,因为我们知道哪些问题是可以处理通过它的数据类型更好。
(3)操作都是原子性:所有Redis操作是原子的,这保证了如果两个客户端同时访问的Redis服务器将获得更新后的值。
(4)多功能实用工具:Redis是一个多实用的工具,可以在多个用例如缓存,消息,队列使用(Redis原生支持发布/订阅),任何短暂的数据,应用程序,如Web应用程序会话,网页命中计数等。
Redis 缺点?
(1)单线程
(2)耗内存
二、使用实例
本文使用maven+eclipse+sping
1、引入jar包
&!--Redis start --&
&dependency&
&groupId&org.springframework.data&/groupId&
&artifactId&spring-data-redis&/artifactId&
&version&1.6.1.RELEASE&/version&
&/dependency&
&dependency&
&groupId&redis.clients&/groupId&
&artifactId&jedis&/artifactId&
&version&2.7.3&/version&
&/dependency&
&!--Redis end --&
2、配置bean
在application.xml加入如下配置
&!-- jedis 配置 --&
&bean id=&poolConfig& class=&redis.clients.jedis.JedisPoolConfig& &
&property name=&maxIdle& value=&${redis.maxIdle}& /&
&property name=&maxWaitMillis& value=&${redis.maxWait}& /&
&property name=&testOnBorrow& value=&${redis.testOnBorrow}& /&
&!-- redis服务器中心 --&
&bean id=&connectionFactory& class=&org.springframework.data.redis.connection.jedis.JedisConnectionFactory& &
&property name=&poolConfig& ref=&poolConfig& /&
&property name=&port& value=&${redis.port}& /&
&property name=&hostName& value=&${redis.host}& /&
&property name=&password& value=&${redis.password}& /&
&property name=&timeout& value=&${redis.timeout}& &&/property&
&bean id=&redisTemplate& class=&org.springframework.data.redis.core.RedisTemplate& &
&property name=&connectionFactory& ref=&connectionFactory& /&
&property name=&keySerializer& &
&bean class=&org.springframework.data.redis.serializer.StringRedisSerializer& /&
&/property&
&property name=&valueSerializer& &
&bean class=&org.springframework.data.redis.serializer.JdkSerializationRedisSerializer& /&
&/property&
&!-- cache配置 --&
&bean id=&methodCacheInterceptor& class=&com.mon.MethodCacheInterceptor& &
&property name=&redisUtil& ref=&redisUtil& /&
&bean id=&redisUtil& class=&com.mon.RedisUtil& &
&property name=&redisTemplate& ref=&redisTemplate& /&
其中配置文件redis一些配置数据redis.properties如下:
#redis中心
redis.host=10.75.202.11
redis.port=6379
redis.password=123456
redis.maxIdle=100
redis.maxActive=300
redis.maxWait=1000
redis.testOnBorrow=true
redis.timeout=100000
# 不需要加入缓存的类
targetNames=xxxRecordManager,xxxSetRecordManager,xxxStatisticsIdentificationManager
# 不需要缓存的方法
methodNames=
#设置缓存失效时间
com.service.impl.xxxRecordManager= 60
com.service.impl.xxxSetRecordManager= 60
defaultCacheExpireTime=3600
fep.local.cache.capacity =10000
要扫这些properties文件,在application.xml加入如下配置
&!-- 引入properties配置文件 --&
&bean id=&propertyConfigurer& class=&org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&&
&property name=&locations&&
&value&classpath:properties/*.properties&/value&
&!--要是有多个配置文件,只需在这里继续添加即可 --&
&/property&
3、一些工具类
(1)RedisUtil
上面的bean中,RedisUtil是用来缓存和去除数据的实例
package com.
import java.io.S
import java.util.S
import java.util.concurrent.TimeU
import org.apache.log4j.L
import org.springframework.data.redis.core.RedisT
import org.springframework.data.redis.core.ValueO
* redis cache 工具类
public final class RedisUtil {
private Logger logger = Logger.getLogger(RedisUtil.class);
private RedisTemplate&Serializable, Object& redisT
* 批量删除对应的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);
* 判断缓存中是否有对应的value
* @param key
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
* 读取缓存
* @param key
public Object get(final String key) {
Object result =
ValueOperations&Serializable, Object& operations = redisTemplate
.opsForValue();
result = operations.get(key);
* 写入缓存
* @param key
* @param value
public boolean set(final String key, Object value) {
boolean result =
ValueOperations&Serializable, Object& operations = redisTemplate
.opsForValue();
operations.set(key, value);
} catch (Exception e) {
e.printStackTrace();
* 写入缓存
* @param key
* @param value
public boolean set(final String key, Object value, Long expireTime) {
boolean result =
ValueOperations&Serializable, Object& operations = redisTemplate
.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
public void setRedisTemplate(
RedisTemplate&Serializable, Object& redisTemplate) {
this.redisTemplate = redisT
(2)MethodCacheInterceptor
切面MethodCacheInterceptor,这是用来给不同的方法来加入判断如果缓存存在数据,从缓存取数据。否则第一次从数据库取,并将结果保存到缓存 中去。
package com.
import java.io.F
import java.io.FileInputS
import java.io.InputS
import java.util.ArrayL
import java.util.L
import java.util.P
import org.aopalliance.intercept.MethodI
import org.aopalliance.intercept.MethodI
import org.apache.log4j.L
public class MethodCacheInterceptor implements MethodInterceptor {
private Logger logger = Logger.getLogger(MethodCacheInterceptor.class);
private RedisUtil redisU
private List&String& targetNamesL // 不加入缓存的service名称
private List&String& methodNamesL // 不加入缓存的方法名称
private Long defaultCacheExpireT // 缓存默认的过期时间
private Long xxxRecordManagerT //
private Long xxxSetRecordManagerT //
* 初始化读取不需要加入缓存的类名和方法名称
public MethodCacheInterceptor() {
File f = new File(&D:\\lunaJee-workspace\\msm\\msm_core\\src\\main\\java\\com\\mucfc\\msm\\common\\cacheConf.properties&);
//配置文件位置直接被写死,有需要自己修改下
InputStream in = new FileInputStream(f);
InputStream in = getClass().getClassLoader().getResourceAsStream(
&D:\\lunaJee-workspace\\msm\\msm_core\\src\\main\\java\\com\\mucfc\\msm\\common\\cacheConf.properties&);
Properties p = new Properties();
p.load(in);
// 分割字符串
String[] targetNames = p.getProperty(&targetNames&).split(&,&);
String[] methodNames = p.getProperty(&methodNames&).split(&,&);
// 加载过期时间设置
defaultCacheExpireTime = Long.valueOf(p.getProperty(&defaultCacheExpireTime&));
xxxRecordManagerTime = Long.valueOf(p.getProperty(&com.service.impl.xxxRecordManager&));
xxxSetRecordManagerTime = Long.valueOf(p.getProperty(&com.service.impl.xxxSetRecordManager&));
// 创建list
targetNamesList = new ArrayList&String&(targetNames.length);
methodNamesList = new ArrayList&String&(methodNames.length);
Integer maxLen = targetNames.length & methodNames.length ? targetNames.length
: methodNames.
// 将不需要缓存的类名和方法名添加到list中
for (int i = 0; i & maxL i++) {
if (i & targetNames.length) {
targetNamesList.add(targetNames[i]);
if (i & methodNames.length) {
methodNamesList.add(methodNames[i]);
} catch (Exception e) {
e.printStackTrace();
public Object invoke(MethodInvocation invocation) throws Throwable {
Object value =
String targetName = invocation.getThis().getClass().getName();
String methodName = invocation.getMethod().getName();
// 不需要缓存的内容
//if (!isAddCache(StringUtil.subStrForLastDot(targetName), methodName)) {
if (!isAddCache(targetName, methodName)) {
// 执行方法返回结果
return invocation.proceed();
Object[] arguments = invocation.getArguments();
String key = getCacheKey(targetName, methodName, arguments);
System.out.println(key);
// 判断是否有缓存
if (redisUtil.exists(key)) {
return redisUtil.get(key);
// 写入缓存
value = invocation.proceed();
if (value != null) {
final String tkey =
final Object tvalue =
new Thread(new Runnable() {
public void run() {
if (tkey.startsWith(&com.service.impl.xxxRecordManager&)) {
redisUtil.set(tkey, tvalue, xxxRecordManagerTime);
} else if (tkey.startsWith(&com.service.impl.xxxSetRecordManager&)) {
redisUtil.set(tkey, tvalue, xxxSetRecordManagerTime);
redisUtil.set(tkey, tvalue, defaultCacheExpireTime);
}).start();
} catch (Exception e) {
e.printStackTrace();
if (value == null) {
return invocation.proceed();
* 是否加入缓存
private boolean isAddCache(String targetName, String methodName) {
boolean flag =
if (targetNamesList.contains(targetName)
|| methodNamesList.contains(methodName)) {
* 创建缓存key
* @param targetName
* @param methodName
* @param arguments
private String getCacheKey(String targetName, String methodName,
Object[] arguments) {
StringBuffer sbu = new StringBuffer();
sbu.append(targetName).append(&_&).append(methodName);
if ((arguments != null) && (arguments.length != 0)) {
for (int i = 0; i & arguments. i++) {
sbu.append(&_&).append(arguments[i]);
return sbu.toString();
public void setRedisUtil(RedisUtil redisUtil) {
this.redisUtil = redisU
4、配置需要缓存的类或方法
在application.xml加入如下配置,有多个类或方法可以配置多个
&!-- 需要加入缓存的类或方法 --&
&bean id=&methodCachePointCut& class=&org.springframework.aop.support.RegexpMethodPointcutAdvisor& &
&property name=&advice& &
&ref local=&methodCacheInterceptor& /&
&/property&
&property name=&patterns& &
&!-- 确定正则表达式列表 --&
&value&com\.mucfc\.msm\.service\.impl\...*ServiceImpl.*&/value &
&/property&
5、执行结果:
写了一个简单的单元测试如下:
public void getSettUnitBySettUnitIdTest() {
String systemId = &CES&;
String merchantId = &133&;
SettUnit configSettUnit = settUnitService.getSettUnitBySettUnitId(systemId, merchantId, &ESP&);
SettUnit configSettUnit1 = settUnitService.getSettUnitBySettUnitId(systemId, merchantId, &ESP&);
boolean flag= (configSettUnit == configSettUnit1);
System.out.println(configSettUnit);
(&查找结果& + configSettUnit.getBusinessType());
// localSecondFIFOCache.put(&configSettUnit&, configSettUnit.getBusinessType());
// String string = localSecondFIFOCache.get(&configSettUnit&);
(&查找结果& + string);
这是第一次执行单元测试的过程:
MethodCacheInterceptor这个类中打了断点,然后每次查询前都会先进入这个方法
依次运行,发现没有缓存,所以会直接去查数据库
打印了出来的SQL语句:
第二次执行:
因为第一次执行时,已经写入缓存了。所以第二次直接从缓存中取数据
3、取两次的结果进行地址的对比:
发现两个不是同一个对象,没错,是对的。如果是使用ehcache的话,那么二者的内存地址会是一样的。那是因为redis和ehcache使用的缓存机制是不一样的。ehcache是基于本地电脑的内存使用缓存,所以使用缓存取数据时直接在本地电脑上龋转换成java对象就会是同一个内存地址,而redis它是在装有redis服务的电脑上(一般是另一台电脑),所以取数据时经过传输到本地,会对应到不同的内存地址,所以用==来比较会返回false。但是它确实是从缓存中去取的,这点我们从上面的断点可以看到。
以上就是本文的全部内容,希望对大家的学习有所帮助。
本文地址:
相关文章列表}

我要回帖

更多关于 redis 通过value找key 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信