Redis 数据类型 和 spring-data-redis使用实例

一、redis数据类型

Redis目前支持5种数据类型,分别是:

String(字符串)
List(列表)
Hash(字典)
Set(集合)
Sorted Set(有序集合)

1.String

image.png

Redis中的字符串是一个字节序列。Redis中的字符串是二进制安全的,这意味着它们的长度不由任何特殊的终止字符决定。因此,可以在一个字符串中存储高达512兆字节的任何内容。

2.List

image.png

Redis列表只是字符串列表,按插入顺序排序。您可以向Redis列表的头部或尾部添加元素。
其实,我觉得这个更像我们java的queue,一样的是先进先出的的原理,线程安全的。很多时候我们在不使用消息中间件的时候,也可以使用列表类型来代替,而且比较轻量

3.Hash

image.png

Redis散列/哈希(Hashes)是键值对的集合。Redis散列/哈希是字符串字段和字符串值之间的映射。因此,它们用于表示对象。

哈希这个就跟我们java的hashMap很像了,将一个对象当做一个hashMap。对象的属性是key值,属性值是value值。我们可以通过这个数据类型很快的拿出指定的某一属性值,而不是一次将有用和无用的属性全部拿出

4.Set

image.png

Redis集合是字符串的无序集合。在Redis中,您可以添加,删除和测试成员存在的时间O(1)复杂性。
这个数据类型,我个人觉得就真的仅仅是一个容器而已。他不需要排序,只需要知道数据是否存在。与List的最大区别应该就是有序和无序

5.Sorted Set

image.png

Redis可排序集合类似于Redis集合,是不重复的字符集合。 不同之处在于,排序集合的每个成员都与分数相关联,这个分数用于按最小分数到最大分数来排序的排序集合。虽然成员是唯一的,但分数值可以重复。

抱歉这里数据是不齐全的,但是这真的是个很有用的数据类型。这个数据类型让我们可以根据score达到自己期望的排序。更关键的是,我们可以利用这个机制在redis中进行分页查询,这个我在后面会给出实例。

二、spring-data-redis

spring-data-redis就是spring在jedis的基础上封装的工具,总的来说感觉比jedis更加易用,而且在都是使用spring生态环境的的情况下,用spring自家的东西总是好的。
至于具体的怎么安装,搭建环境。这么简单的事情,我想就不用在这里废话了。主要是给出几个spring-data-redis 写的比较好的使用实例。
首先,我们在写redis的key值命名时推荐按照"业务名称:唯一值"这种格式来命名。其中 “:” 的作用就是用于分组的,不仅是为了让key业务属性更加清晰,而且在某些视图工具下,工具可以根据分号给你自动分组,更加易于查看,例如:

image.png

![Uploading image_984753.png . . .]

我的key全名是 “TXN_NUM_GENERATOR:DCORF” 但是我有一组string数据都是TXN_NUM_GENERATOR前缀,工具就会自动给我归类分组。这样我们查看起来会清晰不少。试想每个key都是并列的,当有10W个key,我们需要查找时那是多么恐怖的情况。

然后代码使用。可以使用一个枚举类将5种类型定义,如下

image.png

然后,可以根据业务属性建立对应的keyName

   public enum CacheName {

    /**
     * 缓存类型
     */
    private CacheType type;

    /**
     * 缓存前缀
     */
    private String prefix;

    /**
     * 缓存匹配器
     */
    private String regex;

    /**
     * 过期时间
     */
    private long expiration;

    CacheName(CacheType cacheType, long expiration) {
        this.type = cacheType;
        this.expiration = expiration;
        this.prefix = this.name().concat(":");
        this.regex = prefix.concat("*");
    }

    public long getExpiration() {
        return expiration;
    }

    public String getPrefix() {
        return prefix;
    }

    public String getRegex() {
        return regex;
    }

    public CacheType getType() {
        return type;
    }
    
    /**
     * 1天,拍卖中的拍品 key:IN_AUCTION_LOT:lotId value:LotRedisDto TODO 检查生产没有后删除
     */
    //IN_AUCTION_LOT(CacheType.V, 86400),

    /**
     * 5秒,竞价锁
     */
    IN_AUCTION_LOT_BID_LOCK(CacheType.S, 5),

    /**
     * 5秒,竞价订单锁
     */
    IN_AUCTION_LOT_ORDER_LOCK(CacheType.S, 5),

    /**
     * 5秒,竞拍中拍品时间锁
     */
    IN_AUCTION_LOT_TIME_LOCK(CacheType.S, 5) ;
}

在定义一个key的时候,我们就可以将其的数据类型和过期时间通过构造器定义。

以下是spring-data-redis 提供的五种数据类型的service

image.png

以下,附上部分使用代码

public interface RedisService {

/**
 * 从redis获取String类型数据
 *
 * @param cacheName 缓存名称
 * @param key       组成key的变量
 * @return key对应value
 */
Object vGet(CacheUtils.CacheName cacheName, String key);



/**
 * 获取指定缓存名称的String类型数据
 *
 * @param cacheName  缓存名称
 * @param isRedisKey 是否是redis中的key true:是 false:否
 * @param keys       组成key的变量
 * @return value列表
 */
List<Object> vMultiGet(CacheUtils.CacheName cacheName, boolean isRedisKey, String... keys);

/**
 * 往redis中存入String类型数据
 *
 * @param cacheName 缓存名称
 * @param key       组成key的变量
 * @param value     要存入的数据
 */
void vPut(CacheUtils.CacheName cacheName, String key, Object value);


/**
 * 往redis中存入Set类型数据
 *
 * @param cacheName 缓存名称
 * @param key       组成key的变量
 * @param values    要存入的数据
 * @return 添加成功的数量
 */
Long sPut(CacheUtils.CacheName cacheName, String key, Object... values);

/**
 * 查询redis的Set类型中是否有指定对象
 *
 * @param cacheName 缓存名称
 * @param key       组成key的变量
 * @return true 存在,false不存在
 */
Boolean sExist(CacheUtils.CacheName cacheName, String key, Object value);

/**
 * 从redis的Set类型中删除数据
 *
 * @param cacheName 缓存名称
 * @param key       组成key的变量
 * @param values    要删除的数据
 * @return 删除成功的数量
 */
Long sRemove(CacheUtils.CacheName cacheName, String key, Object... values);

/**
 * 查询redis的Set类型中所有数据
 *
 * @param cacheName 缓存名称
 * @param key       组成key的变量
 * @return 数据列表
 */
Set<Object> sGetAll(CacheUtils.CacheName cacheName, String key);

/**
 * 无条件分页查询SortedSet中的全部数据
 *
 * @param cacheName 缓存名称
 * @param key       组成key的变量
 * @param pageable  分页参数
 * @param isAsc     true:升序 false:降序
 * @return 成员列表
 */
Set<Object> zGet(CacheUtils.CacheName cacheName, String key, Pageable pageable, boolean isAsc);

/**
 * 按照分数条件分页查询SortedSet中的数据
 *
 * @param cacheName 缓存名称
 * @param key       组成key的变量
 * @param includeMin  是否包含最小值
 * @param minScore    最小值
 * @param includeMax  是否包含最大值
 * @param maxScore    最大值
 * @param pageable  分页参数
 * @param isAsc     true:升序 false:降序
 * @return 成员列表
 */
Set<Object> zGetByScore(CacheUtils.CacheName cacheName, String key, boolean includeMin, Double minScore, boolean includeMax, Double maxScore, Pageable pageable, boolean isAsc); }

实现类:

 @Service("redisService")
 public class RedisServiceImpl implements RedisService {

@Resource
private RedisTemplate<String, Object> redisTemplate;

/**
 * String(字符串)操作类
 */
@Resource(name = "redisTemplate")
private ValueOperations<String, Object> valueOps;

/**
 * List(列表)操作类
 */
@Resource(name = "redisTemplate")
private ListOperations<String, Object> listOps;

/**
 * Set(集合)操作类
 */
@Resource(name = "redisTemplate")
private SetOperations<String, Object> setOps;

/**
 * SortedSet(有序集合)操作类
 */
@Resource(name = "redisTemplate")
private ZSetOperations<String, Object> zSetOps;

/**
 * Hash(哈希表)操作类
 */
@Resource(name = "redisTemplate")
private HashOperations<String, String, Object> hashOps;

@Override
public Object vGet(CacheUtils.CacheName cacheName, String key) {
    if (cacheName.getType() != CacheUtils.CacheType.V) {
        return null;
    }
    return valueOps.get(cacheName.getPrefix().concat(key));
}


@Override
public List<Object> vMultiGet(CacheUtils.CacheName cacheName, boolean isRedisKey, String... keys) {
    if (cacheName.getType() != CacheUtils.CacheType.V) {
        return null;
    }
    if (CollectionUtilPlus.isNullOrEmptyArray(keys)) {
        return null;
    }
    Set<String> cacheKeys = new HashSet<>();
    for (String key : keys) {
        if (isRedisKey) { //redis中的key
            cacheKeys.add(key);
        } else { //组成key的变量
            cacheKeys.add(cacheName.getPrefix().concat(key));
        }
    }
    List<Object> result = valueOps.multiGet(cacheKeys);
    return result;
}

@Override
public void vPut(CacheUtils.CacheName cacheName, String key, Object value) {
    if (cacheName.getType() != CacheUtils.CacheType.V) {
        return;
    }
    if (cacheName.getExpiration() > 0) {
        valueOps.set(cacheName.getPrefix().concat(key), value, cacheName.getExpiration(), TimeUnit.SECONDS);
    } else {
        valueOps.set(cacheName.getPrefix().concat(key), value);
    }
}




@Override
public Long sPut(CacheUtils.CacheName cacheName, String key, Object... values) {
    if (cacheName.getType() != CacheUtils.CacheType.S) {
        return 0L;
    }
    if (CollectionUtilPlus.isNullOrEmptyArray(values)) {
        return 0L;
    }
    String cacheKey = cacheName.getPrefix().concat(key);
    Long result = setOps.add(cacheKey, values);
    if (cacheName.getExpiration() > 0) {
        redisTemplate.expire(cacheKey, cacheName.getExpiration(), TimeUnit.SECONDS);
    }
    return result;
}

@Override
public Boolean sExist(CacheUtils.CacheName cacheName, String key, Object value) {
    if (cacheName.getType() != CacheUtils.CacheType.S) {
        return false;
    }
    String cacheKey = cacheName.getPrefix().concat(key);
    return setOps.isMember(cacheKey, value);
}

@Override
public Long sRemove(CacheUtils.CacheName cacheName, String key, Object... values) {
    if (cacheName.getType() != CacheUtils.CacheType.S) {
        return null;
    }
    if (CollectionUtilPlus.isNullOrEmptyArray(values)) {
        return null;
    }
    String cacheKey = cacheName.getPrefix().concat(key);
    Long result = setOps.remove(cacheKey, values);
    if (cacheName.getExpiration() > 0) {
        redisTemplate.expire(cacheKey, cacheName.getExpiration(), TimeUnit.SECONDS);
    }
    return result;
}


@Override
public Set<Object> sGetAll(CacheUtils.CacheName cacheName, String key) {
    if (cacheName.getType() != CacheUtils.CacheType.S) {
        return null;
    }
    String cacheKey = cacheName.getPrefix().concat(key);
    return setOps.members(cacheKey);
}

@Override
public Set<Object> zGet(CacheUtils.CacheName cacheName, String key, Pageable pageable, boolean isAsc) {
    if (cacheName.getType() != CacheUtils.CacheType.Z) {
        return null;
    }
    long start = pageable.getOffset();
    long end = pageable.getOffset() + pageable.getPageSize() - 1;
    if (isAsc) {
        return zSetOps.range(cacheName.getPrefix().concat(key), start, end);
    } else {
        return zSetOps.reverseRange(cacheName.getPrefix().concat(key), start, end);
    }
}

@Override
@SuppressWarnings("unchecked")
public Set<Object> zGetByScore(CacheUtils.CacheName cacheName, String key, boolean includeMin, Double minScore, boolean includeMax, Double maxScore, Pageable pageable, boolean isAsc) {
    if (cacheName.getType() != CacheUtils.CacheType.Z) {
        return null;
    }

    RedisZSetCommands.Range range = RedisZSetCommands.Range.range();
    if(minScore != null){
        if(includeMin){
            range.gte(minScore);
        }else{
            range.gt(minScore);
        }
    }

    if(maxScore != null){
        if(includeMax){
            range.lte(maxScore);
        }else{
            range.lt(maxScore);
        }
    }

    RedisZSetCommands.Limit limit = RedisZSetCommands.Limit.limit();
    limit.offset(pageable.getOffset());
    limit.count(pageable.getPageSize());

    RedisSerializer keySerializer = redisTemplate.getKeySerializer();
    byte[] rawKey = keySerializer.serialize(cacheName.getPrefix().concat(key));
    Set<byte[]> rawValues;
    if (isAsc) {
        rawValues = redisTemplate.execute(new RedisCallback<Set<byte[]>>() {
            public Set<byte[]> doInRedis(RedisConnection connection) {
                return connection.zRangeByScore(rawKey, range, limit);
            }
        }, true);
    } else {
        rawValues = redisTemplate.execute(new RedisCallback<Set<byte[]>>() {
            public Set<byte[]> doInRedis(RedisConnection connection) {
                return connection.zRevRangeByScore(rawKey, range, limit);
            }
        }, true);
    }
    RedisSerializer valueSerialize = redisTemplate.getValueSerializer();
    return SerializationUtils.deserialize(rawValues, valueSerialize);
}



@Override
public Boolean zPut(CacheUtils.CacheName cacheName, String key, Object value, double score) {
    if (cacheName.getType() != CacheUtils.CacheType.Z) {
        return Boolean.FALSE;
    }
    String cacheKey = cacheName.getPrefix().concat(key);
    Boolean result = zSetOps.add(cacheKey, value, score);
    if (cacheName.getExpiration() > 0) {
        redisTemplate.expire(cacheKey, cacheName.getExpiration(), TimeUnit.SECONDS);
    }
    return result;
}


@Override
public Long lRightPush(CacheUtils.CacheName cacheName, String key, Object... values) {
    if (CacheUtils.CacheType.L != cacheName.getType()) {
        return 0L;
    }
    if (CollectionUtilPlus.isNullOrEmptyArray(values)) {
        return 0L;
    }
    String cacheKey = cacheName.getPrefix().concat(key);
    Long result = listOps.rightPushAll(cacheKey, values);
    if (cacheName.getExpiration() > 0) {
        redisTemplate.expire(cacheKey, cacheName.getExpiration(), TimeUnit.SECONDS);
    }
    return result;
}

@Override
public Long lLeftPush(CacheUtils.CacheName cacheName, String key, Object... values) {
    if (CacheUtils.CacheType.L != cacheName.getType()) {
        return 0L;
    }
    if (CollectionUtilPlus.isNullOrEmptyArray(values)) {
        return 0L;
    }
    String cacheKey = cacheName.getPrefix().concat(key);
    Long result = listOps.leftPushAll(cacheKey, values);
    if (cacheName.getExpiration() > 0) {
        redisTemplate.expire(cacheKey, cacheName.getExpiration(), TimeUnit.SECONDS);
    }
    return result;
}

@Override
public void lRemove(CacheUtils.CacheName cacheName, String key, Object... values) {
    if (CacheUtils.CacheType.L != cacheName.getType()) {
        return;
    }
    if (CollectionUtilPlus.isNullOrEmptyArray(values)) {
        return;
    }

    String cacheKey = cacheName.getPrefix().concat(key);
    for (Object value : values) {
        listOps.remove(cacheKey, 0, value);
    }
    if (cacheName.getExpiration() > 0) {
        redisTemplate.expire(cacheKey, cacheName.getExpiration(), TimeUnit.SECONDS);
    }
}

@Override
public Object lLeftBlockPop(CacheUtils.CacheName cacheName, String key, long timeout, TimeUnit unit) {
    if (cacheName.getType() != CacheUtils.CacheType.L) {
        return null;
    }
    String cacheKey = cacheName.getPrefix().concat(key);
    Object obj = listOps.leftPop(cacheKey, timeout, unit);
    return obj;
}

@Override
public Object lLeftPop(CacheUtils.CacheName cacheName, String key) {
    String cacheKey = cacheName.getPrefix().concat(key);
    Object value = listOps.leftPop(cacheKey);
    return value;
}


@Override
public void removeKey(CacheUtils.CacheName cacheName, String... keys) {
    List<String> cacheKeys = new ArrayList<>();
    for (String key : keys) {
        cacheKeys.add(cacheName.getPrefix().concat(key));
    }
    redisTemplate.delete(cacheKeys);
}


@Override
public Boolean existsKey(CacheUtils.CacheName cacheName, String key) {
    String cacheKey = cacheName.getPrefix().concat(key);
    Boolean result = redisTemplate.hasKey(cacheKey);
    return result;
}


@Override
public Object hGet(CacheUtils.CacheName cacheName, String key, String field) {
    if (cacheName.getType() != CacheUtils.CacheType.H) {
        return null;
    }
    return hashOps.get(cacheName.getPrefix().concat(key), field);
}

@Override
public void hPut(CacheUtils.CacheName cacheName, String key, String field, Object value) {
    if (cacheName.getType() != CacheUtils.CacheType.H) {
        return;
    }
    String cacheKey = cacheName.getPrefix().concat(key);
    hashOps.put(cacheKey, field, value);
    if (cacheName.getExpiration() > 0) {
        redisTemplate.expire(cacheKey, cacheName.getExpiration(), TimeUnit.SECONDS);
    }
}}

还有许多spring-data-redis的实现方法,暂时未贴出。但是大概的使用方式,及思路都是一样的。可以按照现在的方式去自己实现想要的接口。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容