Redis篇-9--数据结构篇1--五种基本结构(String,List,Set,Sorted Set,Hash,BLPOP阻塞逻辑)

Redis 是一个高性能的键值存储系统,支持多种数据结构。每种数据结构都有其独特的特点和适用场景。

1、String(字符串)

(1)、特点

  • 最简单的数据类型:字符串是最基本的数据类型,可以存储字符串、整数或浮点数。最大长度为 512 MB。
  • 支持原子操作:Redis 提供了多种原子操作,如递增、递减、追加字符串等。
  • 持久化支持:字符串可以持久化到磁盘,确保数据不会因服务器重启而丢失。

(2)、适用场景

  • 缓存:用于缓存网页内容、API 响应等。
  • 计数器:用于实现计数器功能,如点赞数、访问量等。
  • 会话管理:用于存储用户会话信息,如登录状态、购物车等。

(3)、redis-cli示例

1、设置键 "name" 的值为 "Alice"

SET name "Alice"

2、获取键 "name" 的值

GET name

3、递增键 "counter" 的值

INCR counter

4、递减键 "counter" 的值

DECR counter

5、追加字符串到键 "message"

APPEND message " Hello, World!"

(4)、Java示例

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class StringService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public void setString(String key, String value) {
        redisTemplate.opsForValue().set(key, value);   // 设置key
    }

    public String getString(String key) {
        return redisTemplate.opsForValue().get(key);    // 获取key的值
    }

    public Long incrementCounter(String key) {
        return redisTemplate.opsForValue().increment(key);   // 自增某key的值
    }

    public Long decrementCounter(String key) {
        return redisTemplate.opsForValue().decrement(key);   // 自减某key的值
    }

    public void appendString(String key, String value) {
        redisTemplate.opsForValue().append(key, value);  // 追加某key的值
    }
}

2、List(列表)

双端链表,可以从两端插入和删除元素。适用于队列和栈的实现。

(1)、特点

  • 双向链表:列表是一个双向链表,支持从两端插入和删除元素。可以在列表的头部(左端)或尾部(右端)进行操作。
  • 先进先出(FIFO)和后进先出(LIFO):可以通过不同的命令实现队列(FIFO)或栈(LIFO)的行为。
  • 阻塞操作:Redis 提供了阻塞式命令,如 BLPOP 和 BRPOP,可以在没有元素时阻塞等待,适用于消息队列场景。

解释一下如BLPOP的阻塞行为:
1、会阻塞其他客户端的请求吗?

答案:不会 ,其他客户端可以继续正常访问 Redis。
简单说:redis虽然只有一个线程处理命令,但它内部不是使用悲观锁处理数据安全问题,而是采取乐观锁的思路去解决的。对于暂时阻塞的请求可以迅速得到阻塞结果,将阻塞请求挂起,继续执行其他客户端的请求。所以可以同时处理多个客户端的请求。

具体解释:

(1)、Redis 使用单线程来处理所有命令的执行。这意味着在同一时间点上,Redis 只能处理一个命令。然而,Redis 的设计非常高效,能够快速处理每个命令,即使是阻塞的命令也会迅速快得到阻塞的结果,因此在大多数情况下,用户感觉不到延迟。

(2)、Redis是使用多路复用(multiplexing)技术来管理多个客户端连接。每个客户端的请求都被放入事件队列中,Redis 会依次处理这些请求。

(3)、Redis处理事件队列中请求的逻辑是使用了非阻塞 I/O 和事件循环(epoll、kqueue 等),这使得它可以在等待当前任务 I/O 操作完成时,同时继续处理其他其他客户端的请求。

(4)、因此,即使一个客户端被 BLPOP 阻塞,Redis 仍然可以处理来自其他客户端的请求。被 BLPOP 阻塞的请求会被暂时挂起,直到获取到元素或超时。

2、会阻塞当前客户端之后的请求吗?

答案: ,该客户端之后的请求将无法在同一连接上继续执行。即一个客户端的一个连接是一个通道,如果通道内阻塞则会阻塞。但是同一个客户端可以和redis建立多个连接。

解释:

因为在 Redis 的单线程模型中,每个客户端连接是串行处理的,即在一个连接上,Redis 会等待当前命令(如 BLPOP)完成或超时后,才会处理该连接上的下一个命令。

但如果是同一个客户端的其他连接则不会被阻塞。

3、如果阻塞的连接断开了,阻塞的请求会怎样?

答案:请求会立即取消 ,即使是阻塞的请求。
解释:
当 Redis 客户端与 Redis 服务器之间的连接断开时,所有正在该连接上执行的命令(包括阻塞命令如 BLPOP、BRPOP、BZPOPMIN 等)都会被中断。
即:Redis 服务器会立即取消该连接上的所有未完成的命令,包括正在阻塞的命令(如 BLPOP)。这意味着 Redis 不会继续等待这些命令的结果,而是会立即释放与该连接相关的所有资源,包括内存中的临时数据结构(如阻塞队列中的等待状态)。因此,一旦连接断开,Redis 不会保留任何与该连接相关的信息。

(2)、适用场景

  • 消息队列:用于实现任务队列、消息队列等。
  • 历史记录:用于存储用户的操作历史、聊天记录等。
  • 最近使用列表:用于实现 LRU(Least Recently Used)缓存机制。

(3)、redis-cli示例

向列表 "queue1" 的尾部添加元素

RPUSH queue1 task1

向列表 "queue1" 的头部添加元素

LPUSH queue1 task0

从列表 "queue1" 的头部取出元素

LPOP queue1

从列表 "queue1" 的尾部取出元素

RPOP queue1

获取列表 "queue1" 的所有元素 // redis的列表,都是0作为第一个元素的索引

LRANGE queue1 0 -1

阻塞式从列表 "queue" 的头部取出元素(超时 5 秒)

BLPOP queue1 5

(4)、Java示例

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class ListService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private final ListOperations<String, Object> listOps;

    public ListService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.listOps = redisTemplate.opsForList();     // 指定list命令
    }

    public void pushToListRight(String key, Object value) {
        listOps.rightPush(key, value);    // 队尾添加value
    }

    public void pushToListLeft(String key, Object value) {
        listOps.leftPush(key, value);     // 队首添加value
    }

    public Object popFromListLeft(String key) {
        return listOps.leftPop(key);     // 队首取出一个元素
    }

    public Object popFromListRight(String key) {
        return listOps.rightPop(key);    // 队尾取出一个元素
    }

    public List<Object> rangeFromList(String key, long start, long end) {
        return listOps.range(key, start, end);    // 查询队列(0,-1)为查询全部
    }

    public Object blockPopFromListLeft(String key, long timeout, TimeUnit unit) {
        return listOps.leftPop(key, timeout, unit);   // 阻塞获取元素,设置超时时间
    }
}

3、Set(集合)

(1)、特点

  • 无序集合:集合是一个无序的集合,不允许重复元素。每个元素是唯一的。
  • 高效去重:集合非常适合用于去重操作,如获取多个集合的交集、并集、差集等。
  • 成员检查:可以快速检查某个元素是否存在于集合中。

(2)、适用场景

  • 去重:用于去除重复数据,如用户好友列表、标签系统等。
  • 唯一性验证:用于确保某个元素在集合中只出现一次,如黑名单、白名单等。
  • 集合运算:用于执行集合的交集、并集、差集等操作,如推荐系统中的共同爱好的好友等。

(3)、redis-cli示例

向集合 "users" 添加元素 "Alice"

SADD users Alice

检查元素 "Alice" 是否存在于集合 "users"

SISMEMBER users Alice

获取集合 "users" 中的所有元素

SMEMBERS users

从集合 "users" 中随机移除一个元素

SPOP users

获取两个集合 "users" 和 "admins" 的交集

SINTER users admins

获取两个集合 "users" 和 "admins" 的并集

SUNION users admins

获取两个集合 "users" 和 "admins" 的差集 // 即前者独有的元素有哪些

SDIFF users admins

(4)、Java示例

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class SetService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private final SetOperations<String, Object> setOps;

    public SetService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.setOps = redisTemplate.opsForSet();    // 指定set操作
    }

    public void addElementToSet(String key, Object... values) {
        setOps.add(key, values);        // 添加元素
    }

    public boolean isMemberOfSet(String key, Object value) {
        return setOps.isMember(key, value);       // 是否包含value元素
    }

    public Set<Object> getMembersOfSet(String key) {
        return setOps.members(key);    // 获取set集合
    }

    public Object popRandomElementFromSet(String key) {
        return setOps.pop(key);      // 随机取set中的一个元素
    }

    public Set<Object> intersectSets(String... keys) {
        return setOps.intersect(keys);      // 交集
    }

    public Set<Object> unionSets(String... keys) {
        return setOps.union(keys);        // 并集
    }

    public Set<Object> differenceSets(String... keys) {
        return setOps.difference(keys);          // 差集
    }
}

4、Sorted Set(有序集合)

(1)、特点

  • 带分数的集合:有序集合是一个带分数的集合,每个元素都有一个关联的分数(score),根据分数进行排序。
  • 范围查询:可以基于分数或排名进行范围查询,如获取前 N 个最高分的用户。
  • 高效插入和删除:有序集合支持高效的插入、删除和查找操作,适用于需要排序的场景。

(2)、适用场景

  • 排行榜:用于实现排行榜功能,如游戏得分榜、热门文章排行等。
  • 时间序列数据:用于存储带有时间戳的数据,如日志、事件流等。
  • 优先级队列:用于实现优先级队列,如任务调度系统。

(3)、redis-cli示例

向有序集合 "leaderboard" 添加元素 "Alice",分数为 100

ZADD leaderboard 100 Alice

获取有序集合 "leaderboard" 中排名前 3 的元素

ZRANGE leaderboard 0 2 WITHSCORES

获取有序集合 "leaderboard" 中分数在 50 到 150 之间的元素

ZRANGEBYSCORE leaderboard 50 150 WITHSCORES

获取元素 "Alice" 在有序集合 "leaderboard" 中的排名

ZRANK leaderboard Alice

递增元素 "Alice" 的分数

ZINCRBY leaderboard 10 Alice

(4)、Java示例

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class SortedSetService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private final ZSetOperations<String, Object> zSetOps;

    public SortedSetService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.zSetOps = redisTemplate.opsForZSet();    // 指定zset操作
    }

    public void addElementToSortedSet(String key, Object value, double score) {
        zSetOps.add(key, value, score);   // 添加元素和排名
    }

    public Set<ZSetOperations.TypedTuple<Object>> rangeByScore(String key, double min, double max) {
        return zSetOps.rangeByScoreWithScores(key, min, max);   // 获取指定区间的排名
    }

    public Set<Object> rangeByRank(String key, long start, long end) {
        return zSetOps.range(key, start, end);
    }

    public Long getRankInSortedSet(String key, Object value) {
        return zSetOps.rank(key, value);
    }

    public Double incrementScore(String key, Object value, double increment) {
        return zSetOps.incrementScore(key, value, increment);
    }
}

5、Hash(哈希表)

(1)、特点

  • 键值对集合:哈希是一个键值对的集合,非常适用于存储对象。每个哈希字段(field)对应一个值(value),并且可以独立操作。
  • 高效存储:哈希适合存储复杂的对象结构,且占用较少的内存空间。
  • 原子操作:可以对哈希中的字段进行原子操作,如增加、删除、获取单个字段或多字段。

(2)、适用场景

  • 对象存储:用于存储对象的属性,如用户信息、商品详情等。
  • 配置管理:用于存储应用程序的配置项。
  • 会话管理:用于存储用户的会话信息,如登录状态、权限等。

(3)、redis-cli示例

1、设置哈希 "user:1001" 的字段 "name" 为 "Alice"

HSET user:1001 name "Alice"

2、获取哈希 "user:1001" 的字段 "name"

HGET user:1001 name

3、获取哈希 "user:1001" 的所有字段和值

HGETALL user:1001

4、删除哈希 "user:1001" 的字段 "age"

HDEL user:1001 age

5、检查哈希 "user:1001" 是否存在字段 "name"

HEXISTS user:1001 name

(4)、Java示例

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class HashService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    private final HashOperations<String, String, Object> hashOps;

    public HashService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.hashOps = redisTemplate.opsForHash();    // 指定hash命令操作
    }

    public void setHashField(String key, String field, Object value) {
        hashOps.put(key, field, value);    // 设置key对象的field属性为value值
    }

    public Object getHashField(String key, String field) {
        return hashOps.get(key, field);  // 获取key对象的field属性值
    }

    public Map<String, Object> getAllHashFields(String key) {
        return hashOps.entries(key);    // 获取key对象的全部属性值
    }

    public void deleteHashField(String key, String field) {
        hashOps.delete(key, field);    // 删除key对象的field属性
    }

    public boolean existsHashField(String key, String field) {
        return hashOps.hasKey(key, field);   // 校验key对象是否存在field属性
    }
}

学海无涯苦作舟!!!

相关推荐
良木林1 小时前
2024西游新生赛部分题解
c语言·数据结构·算法
zybsjn1 小时前
【实战】提升List性能方法有几何
microsoft·c#·list
东阳马生架构2 小时前
Redis应用—4.在库存里的应用
redis
两袖清风9983 小时前
【算法】—— 前缀和
java·数据结构·算法
和小潘一起学AI3 小时前
数据结构速成
数据结构
lin zaixi()4 小时前
洛谷 P10483 小猫爬山 完整题解
数据结构·算法
chengma_0909095 小时前
linux离线安装部署redis
linux·redis·bootstrap
武昌库里写JAVA7 小时前
Java面试之单例模式浅谈
数据结构·vue.js·spring boot·算法·课程设计
Happy_Traveller7 小时前
Level DB --- coding
数据结构·数据库