IT俱乐部 Redis Redis的五种基本数据类型解读(String、List、Hash、Set、ZSet)

Redis的五种基本数据类型解读(String、List、Hash、Set、ZSet)

1.概述

Redis共有五种基本数据类型:String(字符串)、List(列表)、Hash(散列)、Set(集合)、ZSet(有序集合)。

这些基础数据结构支持丰富的原子操作,底层通过内存压缩算法(如ziplist、intset)实现空间与速度的平衡。本文将详细介绍五种基础数据结构,帮助大家更好地理解Redis基础数据的使用及原理。

2.基本数据类型详解

2.1 String

String类型是Redis中使用最多的类型,key是唯一标识,value代表对应key存储的值,value可以是字符串、数字(整型或浮点数),value最多可以容纳的数据长度是512M。

2.1.1 String类型常用指令

命令 说明
SET key value 设置指定 key 的值
GET key 获取指定 key 的值
MSET key1 value1 key2 value2 …… 批量设置key、value
MGET key1 key2 … 获取一个或多个指定 key 的值
STRLEN key 返回key所存储的字符串长度
SETNX key value key 不存在时设置 key 的值
SETEX key seconds value 设置key、value和过期时间
INCR key 将 key 中储存的数字值增一
DECR key 将 key 中储存的数字值减一
APPEND key value 向当前key的字符串后面追加字符串,key不存在相当于set key value
GETRANGE key start end 截取key以start为起点,end为终点的字符串
KEYS * 获取当前数据库所有的key(通用指令)
EXISTS key 判断指定 key 是否存在(通用指令)
DEL key 删除key(通用指令)
GETSET key value 先get后set

2.1.2 指令实测

> set books java
OK
> get books
java
> MSET name zhangsan age 18
OK
> mget name age
zhangsan
18
> STRLEN name
8
> SETNX name zhangsan
0
> SETEX address 30 beijing
OK
> TTL address
18
> INCR age
19
> INCR age
20
> GET age
20
> DECR age
19
> APPEND name ',hello world'
20
> GET name
zhangsan,hello world
> GETRANGE name 0 4
zhang
> GETRANGE name 0 3
zhan
> keys *
name
key1
count
books
key2
age
> EXISTS count
1
> del count
1
> GETSET db redis
null
> GET db
redis

2.1.3 应用场景

1.‌计数器

  • 例如,可以用来记录网站的访问次数、用户登录次数等。
  • 使用场景:使用 INCR 和 DECR 指令对计数器进行递增或递减操作,实现记录网站的访问次数、用户登录次数等。

‌2.缓存功能

  • 例如,存储用户信息、配置信息等。
  • 使用场景:使用 SET 和 GET 指令实现简单的键值对缓存。

‌3.分布式锁

  • 例如:在分布式系统中确保某个操作在同一时间内只能由一个实例执行。
  • 使用场景:使用 SET 指令的 NX(仅当键不存在时设置)和 EX(设置过期时间)选项实现分布式锁。

2.2 List

2.2.1 List类型常用指令

命令 说明
LPUSH key value1 value2 … 在指定列表的头部(左边)添加一个或多个元素
RPUSH key value1 value2 … 在指定列表的头部(右边)添加一个或多个元素
LSET key index value 将指定列表索引 index 位置的值设置为 value
LPOP key 移除并获取指定列表的第一个元素(最左边)
RPOP key 移除并获取指定列表的最后一个元素(最右边)
LLEN key 获取列表元素数量
LRANGE key start end 获取列表 start 和 end 之间 的元素

2.2.2 List指令实测

> LPUSH books java python go
3
> LLEN books
3
> LRANGE books 0 -1
go
python
java
> LPUSH books javascript
4
> LRANGE books 0 -1
javascript
go
python
java
> RPUSH books c
5
> LRANGE books 0 -1
javascript
go
python
java
c
> RPOP books
c
> LRANGE books 0 -1
javascript
go
python
java
> LPOP books
javascript
> LRANGE books 0 -1
go
python
java
> LINDEX books 0
go
> LINDEX books 2
java
> LSET books 2 javascript
OK
> LRANGE books 0 -1
go
python
javascript

2.2.3 使用场景

1.‌简单消息队列

  • 例如,通过订阅同一个list的key实现消息队列等。
  • 使用场景:通过LPUSH/RPOP或者RPUSH/LPOP可以实现简易消息队列,实现数据先进先出。

‌2.模拟栈实现

  • 例如,通过模拟栈先进后出。
  • 使用场景:通过LPUSH/LPOP或者RPUSH/RPOP可以实现栈,实现数据后进先出,实现特点场景下的规则解析。

2.3 Hash

2.3.1 Hash类型常用指令

命令 说明
HSET key field value 设置指定哈希表中指定字段的值
HGET key field 获取指定哈希表中指定字段的值
HMSET key field1 value1 field2 value2 … 同时设置一个或多个 field-value 到指定哈希表中
HMGET key field1 field2 … 获取指定哈希表中一个或者多个指定字段的值
HGETALL key 获取指定哈希表中所有的键值对
HEXISTS key field 查看指定哈希表中指定的字段是否存在
HDEL key field1 field2 … 删除一个或多个哈希表字段
HLEN key 获取指定哈希表中字段的数量
HSETNX key field value 当指定哈希表中的字段不存在时,才添加值
HINCRBY key field increment 对指定哈希中的指定字段做运算操作(正数为加,负数为减)

2.3.2 Hash指令实测

> HSET userInfo-1 name zhangsan
1
> HSET userInfo-1 age 18
1
> HSET userInfo-1 sex male
1
> HGET userInfo-1 name
zhangsan
> HMSET userInfo-2 name lisi age 20 sex female
OK
> HMGET userInfo-1 name age sex
zhangsan
18
male
> HGETALL userInfo-1
name
zhangsan
age
18
sex
male
> HEXISTS userInfo-1 name
1
> HDEL userInfo-1 sex
1
> HGETALL userInfo-1
name
zhangsan
age
18
> HLEN userInfo-1
2
> HSETNX userInfo-1 name zhangsan
0
> HINCRBY userInfo-1 age 5
23
> HGETALL userInfo-1
name
zhangsan
age
23

2.3.2 Hash使用场景

1.‌对象信息存储

  • 例如,存储用户信息等。
  • 使用场景:以信息标签+用户唯一id作为key,属性分别是作为field,这样相对于String存储的优势是提升了效率(string类型需要进行数据转换后才能获取到值)。

‌2.购物车功能

  • 例如,实现购物车功能
  • 使用场景:将用户id作为key,商品id作为field,field的值为商品数量。

2.4 Set

2.4.1 Set类型常用指令

命令 说明
SADD key member1 member2 … 向指定集合添加一个或多个元素
SMEMBERS key 获取指定集合中的所有元素
SCARD key 获取指定集合的元素数量
SREM key memeber 移除集合中的指定元素
SISMEMBER key member 判断指定元素是否在指定集合中
SINTER key1 key2 … 获取给定所有集合的交集
SINTERSTORE destination key1 key2 … 将给定所有集合的交集存储在 destination 中
SUNION key1 key2 … 获取给定所有集合的并集
SUNIONSTORE destination key1 key2 … 将给定所有集合的并集存储在 destination 中
SDIFF key1 key2 … 获取给定所有集合的差集
SDIFFSTORE destination key1 key2 … 将给定所有集合的差集存储在 destination 中
SPOP key count 随机移除并获取指定集合中一个或多个元素
SRANDMEMBER key count 随机获取指定集合中指定数量的元素

2.4.2 Set指令实测

> sadd db oracle mysql sqlite
3
> SADD db mysql
0
> SMEMBERS db
oracle
mysql
sqlite
> SCARD db
3
> SISMEMBER db oracle
1
> SREM db mysql
1
> SMEMBERS db
oracle
sqlite
> SADD db1 mysql oracle hbase
3
> SINTERSTORE db2 db db1
1
> SMEMBERS db2
oracle
> SUNION db db1
oracle
sqlite
mysql
hbase
> SDIFF db db1
sqlite
> SRANDMEMBER db1 2
oracle
hbase
> SPOP db1 2
oracle
hbase
> SMEMBERS db1
mysql

2.4.3 Set使用场景

1.‌共同关注好友

  • 例如,微博、B站等共同关注博主等。
  • 使用场景:可以将A用户的关注博主、B用户的关注博主分别做一个Set集合,通过取并集获取共同关注对象。

‌2.获取随机值

  • 例如,实现某些具体场景随机值获取
  • 使用场景:将用户id作为一个Set集合,通过SPOP指令随机获取用户id,实现抽奖等场景功能。

‌3.快速去重

  • 在某些应用中,可以使用有序集合来管理定时任务,其中任务的执行时间作为分数存储。
  • 使用场景:获取网站UV数据,将网站域名作为key,用户唯一标识作为集合值,实现快速去重,获取当日、周该网站UV数据。

2.5 ZSet

2.5.1 ZSet类型常用指令

有序集合相对于Set增加了一个权重参数score,集合中的元素能够按照score进行有序排列,也可以按照score的范围来获取集合中的元素。

命令 说明
ZADD key score1 member1 score2 member2 … 向指定有序集合中添加一个或多个元素
ZSCORE key member 获取有序集合中指定元素的score值
ZCARD KEY 获取指定有序集合的元素数量
ZRANGEBYSCORE key min max [WITHSCORES] 根据分数获取有序集合中元素
ZREVRANK key member 返回有序集中成员的排名,排名以0为底,分数值最大的成员排名为0
ZLEXCOUNT key min max 对于一个所有成员的分值都相同的有序集合键 key 来说, 这个命令会返回该集合中, 成员介于 min 和 max 范围内的元素数量。
ZINTERSTORE destination numkeys key1 key2 … 将给定所有有序集合的交集存储在destination中,对相同元素对应的score值进行sum聚合操作,numkeys 为集合数量
ZUNIONSTORE destination numkeys key1 key2 … 求并集,其中给定 key 的数量必须以 numkeys 参数指定,并将该并集(结果集)储存到 destination
ZDIFFSTORE destination numkeys key1 key2 … 求差集,其中给定 key 的数量必须以 numkeys 参数指定,并将该并集(结果集)储存到 destination
ZRANGE key start end 获取指定有序集合 start 和 end 之间的元素,score由低到高排序
ZREVRANGE key start end 获取指定有序集合 start 和 end 之间的元素,score由低到高排序
ZREM key member 移除有序集合中指定元素

2.5.3 ZSet指令实测

> zadd salary 3000 zhangsan 4000 lisi 8000 wangwu
3
> ZSCORE salary zhangsan
3000
> ZADD salary 12000 zhaoliu
1
> ZCARD salary
4
> ZRANGEBYSCORE salary 3000 5000
zhangsan
lisi
> ZRANGEBYSCORE salary 3000 5000 WITHSCORES
zhangsan
3000
lisi
4000
> ZREVRANK salary zhaoliu
0
> ZREVRANK salary wangwu
1
> ZINTERSTORE salary2 2 salary salary1
1
> ZRANGE salary 0 -1 withscores
zhangsan
3000
lisi
4000
wangwu
8000
> ZRANGE salary2 0 -1 withscores
zhangsan
6000
> ZUNIONSTORE salary3 2 salary salary1
5
> ZRANGE salary3 0 -1 withscores
lisi
4000
tony
5000
tom
6000
zhangsan
6000
wangwu
8000
> ZDIFFSTORE salary4 2 salary salary1
2
> ZRANGE salary4 0 -1 withscores
lisi
4000
wangwu
8000
> ZREVRANGE salary 0 -1
wangwu
lisi
zhangsan
> ZREM salary zhangsan
1

2.5.3 ZSet使用场景

1.‌排行榜系统‌

  • 例如,游戏中的玩家分数排行榜、视频网站上的视频点赞数排行榜等。
  • 使用场景:可以实时更新分数,并利用 ZADD 命令添加或更新元素及其分数,使用 ZREVRANGE 或 ZREVRANGEBYSCORE 命令获取排名靠前的元素。

‌2.延迟消息队列‌

  • 使用有序集合存储消息及其延迟时间(以时间戳或相对延迟时间表示),然后通过 ZRANGEBYSCORE 命令获取当前时间之前的所有消息进行处理。
  • 使用场景:可以确保消息按照预定的延迟时间被处理,非常适合需要延迟处理的场景。

‌3.定时任务调度‌

  • 在某些应用中,可以使用有序集合来管理定时任务,其中任务的执行时间作为分数存储。
  • 使用场景:通过定期查询当前时间之前的任务并执行它们,可以实现一个简单的任务调度器。

3.代码实现

3.1 pom文件引入

        org.springframework.bootspring-boot-starter-data-redis

3.2 RedisUtil工具类实现

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.types.RedisClientInfo;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {

    private static final Logger LOG = LoggerFactory.getLogger(RedisUtil.class);
    /**
     * 默认过期时间,单位:秒,即,24个小时 后 过期
     */
    public static final long DEFAULT_EXPIRE = 60 * 60 * 24;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource(name = "strRedisTemplate")
    private RedisTemplate stringRedisTemplate;

    /**
     * set 方法
     *
     * @param key   key
     * @param value value
     */
    public void set(String key, Object value) {
        set(key, value, null);
    }

    public void setWithDefaultExpire(String key, Object value) {
        set(key, value, DEFAULT_EXPIRE);
    }


    /**
     * @param key    redis key
     * @param value  redis值
     * @param expire 过期时间,秒
     */
    public void set(String key, Object value, Long expire) {
        if (expire != null && expire.longValue() != 0L) {
            redisTemplate.boundValueOps(key).set(value, expire, TimeUnit.SECONDS);
        } else {
            redisTemplate.boundValueOps(key).set(value);
        }
    }

    /**
     * @param key    redis key
     * @param value  redis值
     * @param expire 过期时间,秒
     */
    public void setString(String key, String value, Long expire) {
        if (expire != null && expire.longValue() != 0L) {
            stringRedisTemplate.boundValueOps(key).set(value, expire, TimeUnit.SECONDS);
        } else {
            stringRedisTemplate.boundValueOps(key).set(value);
        }
    }

    public Double increment(String key, double score) {
        return redisTemplate.opsForValue().increment(key, score);
    }

    /**
     * 获取redis value
     *
     * @param key
     * @return Object 对象
     */
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 判断key是否存在
     *
     * @param key
     * @return Set 集合
     */
    public Set keys(String key) {
        return redisTemplate.keys(key);
    }

    public void deleteKeys(Set keys) {
        redisTemplate.delete(keys);
    }


    public String getString(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    public void delete(String key) {
        redisTemplate.delete(key);
    }

    public void expire(String key, Long expire) {
        redisTemplate.expire(key, expire, TimeUnit.SECONDS);
    }

    public Boolean hsetAbsent(String key, String hkey, Object value) {
        return redisTemplate.opsForHash().putIfAbsent(key, hkey, value);
    }

    public void hset(String key, String hkey, Object value) {
        redisTemplate.opsForHash().put(key, hkey, value);
    }

    public void hmset(String key, Map, ?> hashMap) {
        redisTemplate.opsForHash().putAll(key, hashMap);
    }

    public Long lpushString(String key, String val) {
        return stringRedisTemplate.boundListOps(key).leftPush(val);
    }

    public String rpopString(String key) {
        return stringRedisTemplate.boundListOps(key).rightPop();
    }

    public Long llen(String key) {
        return stringRedisTemplate.boundListOps(key).size();
    }

    public Properties info() {
        RedisConnection connection = null;
        Properties p = null;
        try {
            connection = redisTemplate.getConnectionFactory().getConnection();
            p = connection.info("memory");
        } catch (Exception e) {
            LOG.error("redis获取连接失败", e);
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
        return p;
    }

    public String clients() {
        List clientList = redisTemplate.getClientList();
        return JSON.toJSONString(clientList);
    }

    /**
     * 左边入队
     */
    public Long lpush(String key, Object val) {
        return redisTemplate.boundListOps(key).leftPush(val);
    }

    /**
     * 右边出队
     */
    public Object rpop(String key) {
        return redisTemplate.boundListOps(key).rightPop();
    }

    /**
     * 右边出队
     */
    public String rpop(String key, Integer timeout) {
        return stringRedisTemplate.opsForList().rightPop(key, timeout, TimeUnit.SECONDS);
    }

    public Object hget(String key, String hkey) {
        return redisTemplate.opsForHash().get(key, hkey);
    }

    public String hgetStr(String key, String hkey) {
        return (String) stringRedisTemplate.opsForHash().get(key, hkey);
    }

    public Long hdel(String key, String hkey) {
        return redisTemplate.opsForHash().delete(key, hkey);
    }

    public void hincrement(String key, String hkey) {
        redisTemplate.opsForHash().increment(key, hkey, 1);
    }

    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    public Double zscore(String key, Object val) {
        return stringRedisTemplate.opsForZSet().score(key, val);
    }

    public boolean zadd(String key, String value, double score) {
        return stringRedisTemplate.opsForZSet().add(key, value, score);
    }

    public Long zadd(String key, Set> tuples) {
        return stringRedisTemplate.opsForZSet().add(key, tuples);
    }

    public void zaddObj(String key, Object value, double score) {
        redisTemplate.opsForZSet().add(key, value, score);
    }

    public Long removeRangeByScoreObj(String key, double minScore, double maxScore) {
        return redisTemplate.opsForZSet().removeRangeByScore(key, minScore, maxScore);
    }

    public Long removeRangeByObj(String key, Object value) {
        return redisTemplate.opsForZSet().remove(key, value);
    }

    public Long removeRangeByScoreStr(String key, double minScore, double maxScore) {
        return stringRedisTemplate.opsForZSet().removeRangeByScore(key, minScore, maxScore);
    }

    public Double zStringScore(String key, Object value) {
        return stringRedisTemplate.opsForZSet().score(key, value);

    }

    public Set zrangeByScore(String key, double minScore, double maxScore) {
        return stringRedisTemplate.opsForZSet().rangeByScore(key, minScore, maxScore);
    }

    public Set zreverseRangeByScore(String key, double minScore, double maxScore) {
        return stringRedisTemplate.opsForZSet().reverseRangeByScore(key, minScore, maxScore);
    }

    public Set zrangeByScore(String key, double minScore, double maxScore, long offset, long count) {
        return stringRedisTemplate.opsForZSet().rangeByScore(key, minScore, maxScore, offset, count);
    }

    public Set zreverseRangeByScore(String key, double minScore, double maxScore, long offset, long count) {
        return stringRedisTemplate.opsForZSet().reverseRangeByScore(key, minScore, maxScore, offset, count);
    }

    public Set zrangeByScoreObj(String key, double minScore, double maxScore) {
        return redisTemplate.opsForZSet().rangeByScore(key, minScore, maxScore);
    }

    public Long zrank(String key, String value) {
        return stringRedisTemplate.opsForZSet().rank(key, value);
    }

    public void zremObj(String key, String member) {
        redisTemplate.opsForZSet().remove(key, member);
    }

    public Long zremStr(String key, String member) {
        return stringRedisTemplate.opsForZSet().remove(key, member);
    }

    public Map hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    public Set hkeys(String key){
        return redisTemplate.opsForHash().keys(key);
    }

    public List hValues(String key) {
        return redisTemplate.opsForHash().values(key);
    }


    public Map hGetAllConvertString(String key) {
        Map tmp = redisTemplate.opsForHash().entries(key);
        return tmp != null ? convertToString(tmp) : null;
    }

    public Set> rangeByScoreWithScores(String name, double min, double max, long offset, long count) {
        return redisTemplate.opsForZSet().rangeByScoreWithScores(name, min, max, offset, count);
    }

    public Set> strRangeByScoreWithScores(String name, double min, double max, long offset, long count) {
        return stringRedisTemplate.opsForZSet().rangeByScoreWithScores(name, min, max, offset, count);
    }

    public Set strZRange(String key, int start, int end) {
        return stringRedisTemplate.opsForZSet().range(key, start, end);
    }

    public Set> strZRangeWithScores(String key, int start, int end) {
        return stringRedisTemplate.opsForZSet().rangeWithScores(key, start, end);
    }

    public Double strIncrementScore(String key, String value, double delta) {
        return stringRedisTemplate.opsForZSet().incrementScore(key, value, delta);
    }

    public Double incrementScore(String key, String value, double score) {
        return redisTemplate.opsForZSet().incrementScore(key, value, score);
    }

    public Set members(String key) {
        return stringRedisTemplate.opsForSet().members(key);
    }

    public boolean isMembers(String key, String value) {
        return stringRedisTemplate.opsForSet().isMember(key, value);
    }

    public Set sMembersStr(String key) {
        return stringRedisTemplate.opsForSet().members(key);
    }

    public void sAddStr(String key, String value) {
        stringRedisTemplate.opsForSet().add(key, value);
    }

    public void sAdd(String key, Object val) {
        redisTemplate.opsForSet().add(key, val);
    }

    public String sPop(String key) {
        return stringRedisTemplate.opsForSet().pop(key);
    }

    public void leftPush(String key, String value) {
        stringRedisTemplate.boundListOps(key).leftPush(value);
    }

    public void sRemoveStr(String key, String value) {
        stringRedisTemplate.opsForSet().remove(key, value);
    }

    public void publish(String channel, String value) {
        stringRedisTemplate.convertAndSend(channel, value);
    }


    public static Map convertToString(Map map) {
        Objects.requireNonNull(map);
        Map result = new ConcurrentHashMap(map.size());
        map.forEach((key, value) -> result.put(key.toString(), value.toString()));
        return result;
    }
}

4.总结

1.本文主要讲解redis的基础数据类型和使用方式,同时实操指令,说明其使用场景;

2.本文利用JAVA语言实现了个Redis工具类,对RedisTemplate做了二次封装,供大家参考;

3.关于Redis的底层数据结构,数据存储原理,本文没有详细叙述,可参考文献部分,写的都十分详细。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持IT俱乐部。

本文收集自网络,不代表IT俱乐部立场,转载请注明出处。https://www.2it.club/database/redis/17475.html
上一篇
下一篇
联系我们

联系我们

在线咨询: QQ交谈

邮箱: 1120393934@qq.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部