看完这篇文章,别再说不会 Redis 的高级特性了(四)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis 作为后端工程师必备的技能,阿粉每次面试的时候都会被问到,阿粉特意把公号前面发过的 Redis 系列文章整理出来成一篇,自己学习同时也帮助大家一起学习。

比如,如果有一个新的客户端 Client 08 要订阅 run 渠道,那么上图就会变成

32.jpg

如果 Client 08 要订阅一个新的渠道 new_sport ,那么就会变成

33.jpg

image-20191222161558999

整个订阅的过程可以采用下面伪代码来实现

Map<String, List<Object>> pubsub_channels = new HashMap<>();
    public void subscribe(String[] subscribeList, Object client) {
        //遍历所有订阅的 channel,检查是否在 pubsub_channels 中,不在则创建新的 key 和空链表
        for (int i = 0; i < subscribeList.length; i++) {
            if (!pubsub_channels.containsKey(subscribeList[i])) {
                pubsub_channels.put(subscribeList[i], new ArrayList<>());
            }
            pubsub_channels.get(subscribeList[i]).add(client);
        }
    }

取消订阅

上面介绍的是单个 Channel 的订阅,相反的如果一个客户端要取消订阅相关 Channel,则无非是找到对应的 Channel 的链表,从中删除对应的客户端,如果该客户端已经是最后一个了,则将对应 Channel 也删除。

public void unSubscribe(String[] subscribeList, Object client) {
        //遍历所有订阅的 channel,依次删除
        for (int i = 0; i < subscribeList.length; i++) {
            pubsub_channels.get(subscribeList[i]).remove(client);
            //如果长度为 0 则清楚 channel
            if (pubsub_channels.get(subscribeList[i]).size() == 0) {
                remove(subscribeList[i]);
            }
        }
    }

模式订阅结构

模式渠道的订阅与单个渠道的订阅类似,不过服务器是将所有模式的订阅关系都保存在服务器状态的pubsub_patterns 属性里面。

struct redisServer{
 //保存所有模式订阅关系
 list *pubsub_patterns;
}

与订阅单个 Channel 不同的是,pubsub_patterns 属性是一个链表,不是字典。节点的结构如下:

struct pubsubPattern{
 //订阅模式的客户端
 redisClient *client;
 //被订阅的模式
 robj *pattern;
} pubsubPattern;

其实 client 属性是用来存放对应客户端信息,pattern 是用来存放客户端对应的匹配模式。

所以对应上面的 Client-06 模式匹配的结构存储如下

34.jpg

image-20191222174528367

pubsub_patterns链表中有一个节点,对应的客户端是 Client-06,对应的匹配模式是run*

订阅模式

当某个客户端通过命令psubscribe 订阅对应模式的 Channel 时候,服务器会创建一个节点,并将 Client 属性设置为对应的客户端,pattern 属性设置成对应的模式规则,然后添加到链表尾部。

对应的伪代码如下:

List<PubSubPattern> pubsub_patterns = new ArrayList<>();
    public void psubscribe(String[] subscribeList, Object client) {
        //遍历所有订阅的 channel,创建节点
        for (int i = 0; i < subscribeList.length; i++) {
            PubSubPattern pubSubPattern = new PubSubPattern();
            pubSubPattern.client = client;
            pubSubPattern.pattern = subscribeList[i];
            pubsub_patterns.add(pubSubPattern);
        }
    }
  1. 创建新节点;
  2. 给节点的属性赋值;
  3. 将节点添加到链表的尾部;

退订模式

退订模式的命令是punsubscribe,客户端使用这个命令来退订一个或者多个模式 Channel。服务器接收到该命令后,会遍历pubsub_patterns链表,将匹配到的 client 和 pattern 属性的节点给删掉。这里需要判断 client 属性和 pattern 属性都合法的时候再进行删除。

伪代码如下:

public void punsubscribe(String[] subscribeList, Object client) {
        //遍历所有订阅的 channel 相同 client 和 pattern 属性的节点会删除
        for (int i = 0; i < subscribeList.length; i++) {
            for (int j = 0; j < pubsub_patterns.size(); j++) {
                if (pubsub_patterns.get(j).client == client
                && pubsub_patterns.get(j).pattern == subscribeList[i]) {
                    remove(pubsub_patterns);
                }
            }
        }
    }

遍历所有的节点,当匹配到相同 client 属性和 pattern 属性的时候就进行节点删除。

发布消息

发布消息比较好容易理解,当一个客户端执行了publish channelName message 命令的时候,服务器会从pubsub_channelspubsub_patterns 两个结构中找到符合channelName 的所有 Channel,进行消息的发送。在 pubsub_channels 中只要找到对应的 Channel 的 key 然后向对应的 value 链表中的客户端发送消息就好。

Redis 的持久化你了解吗

持久化是将程序数据在持久状态和瞬时状态间转换的机制。通俗的讲,就是瞬时数据(比如内存中的数据,是不能永久保存的)持久化为持久数据(比如持久化至数据库中,能够长久保存)。另外我们使用的 Redis 之所以快就是因为数据都存储在内存当中,为了保证在服务器出现异常过后还能恢复数据,所以就有了 Redis 的持久化,Redis 的持久化有两种方式,一种是快照形式 RDB,另一种是增量文件 AOF。

RDB

RDB 持久化方式是会在一个特定的时间间隔里面保存某个时间点的数据快照,我们拿到这个数据快照过后就可以根据这个快照完整的复制出数据。这种方式我们可以用来备份数据,把快照文件备份起来,传送到其他服务器就可以直接恢复数据。但是这只是某个时间点的全部数据,如果我们想要最新的数据,就只能定期的去生成快照文件。

RDB 的实现主要是通过创建一个子进程来实现 RDB 文件的快照生成,通过子进程来实现备份功能,不会影响主进程的性能。同时上面也提到 RDB 的快照文件是保存一定时间间隔的数据的,这就会导致如果时间间隔过长,服务器出现异常还没来得及生成快照的时候就会丢失这个间隔时间的所有数据;那有同学就会说,我们可以把时间间隔设置的短一点,适当的缩短是可以的,但是如果间隔时间段设置短一点频繁的生成快照对系统还是会有影响的,特别是在数据量大的情况下,高性能的环境下是不允许这种情况出现的。

我们可以在 redis.conf 进行 RDB 的相关配置,配置生成快照的策略,以及日志文件的路径和名称。还有定时备份规则,如下图所示,里面的注释写的很清楚,简单说就是在多少时间以内多少个 key 变化了就会触发快照。如save 300 10 表示在 5 分钟内如果有 10 个 key 发生了变化就会触发生产快照,其他的同理。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
3月前
|
存储 缓存 NoSQL
聊聊 Redis 的高级特性之一: 发布订阅
Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。 图中,消费者1和消费者2 订阅了 Redis 服务的频道 channel ,当生产者通过 PUBLISH 命令发送给频道 channel 时, 这个消息就会被发送给订阅它的两个客户端。
聊聊 Redis 的高级特性之一: 发布订阅
|
4月前
|
NoSQL 网络协议 安全
Redis的高级特性与应用场景(二)
Redis的高级特性与应用场景(二)
25 0
|
4月前
|
存储 缓存 分布式计算
Redis的高级特性与应用场景(一)
Redis的高级特性与应用场景(一)
36 0
|
10月前
|
存储 缓存 NoSQL
Redis极简入门-Redis高级特性
1.1.什么是Redis持久化 因为Redis数据基于内存读写,为了防止Redis服务器关闭或者宕机造成数据丢失,我们通常需要对Redis最磁极化,即:把内从中的数据(命令)保存一份到磁盘做一个备份,当Redis服务关闭或者宕机, 在Redis服务器重启的时候会从磁盘重新加载备份的数据,不至于数据丢失。 Redis 提供了两种不同级别的持久化方式:RDB和AOF,可以通过修改redis.conf来进行配置. 开启持久配置后,对Redis进行写操作,在Redis安装目录将会看到持久文件:“appendonly.aof”和“ dump.rdb”。
246 0
|
10月前
|
NoSQL 网络协议 安全
Redis的高级特性与应用场景(二)
Redis的高级特性与应用场景(二)
92 0
|
10月前
|
存储 缓存 分布式计算
Redis的高级特性与应用场景(一)
Redis的高级特性与应用场景(一)
99 0
|
缓存 NoSQL 数据库
一篇文章搞清楚Redis 缓存预热,缓存击穿,缓存雪崩,缓存穿透。
一篇文章搞清楚Redis 缓存预热,缓存击穿,缓存雪崩,缓存穿透。
170 0
|
存储 NoSQL 算法
【SpringBoot DB 系列】Redis 高级特性之 HyperLoglog
hyperloglog 算法,利用非常少的空间,实现比较大的数据量级统计;比如我们前面在介绍 bitmap 的过程中,说到了日活的统计,当数据量达到百万时,最佳的存储方式是 hyperloglog,本文将介绍一下 hyperloglog 的基本原理,以及 redis 中的使用姿势
258 0
|
消息中间件 缓存 NoSQL
【SpringBoot DB 系列】Redis 高级特性之发布订阅
通常来讲,当我们业务存在消息的业务逻辑时更多的是直接使用成熟的 rabbitmq,rocketmq,但是一些简单的业务场景中,真的有必要额外的引入一个 mq 么?本文将介绍一下 redis 的发布订阅方式,来实现简易的消息系统逻辑
197 0
|
存储 数据采集 缓存
【SpringBoot DB 系列】Redis 高级特性之 Bitmap 使用姿势及应用场景介绍
前面介绍过 redis 的五种基本数据结构,如 String,List, Set, ZSet, Hash,这些属于相对常见了;在这些基本结果之上,redis 还提供了一些更高级的功能,如 geo, bitmap, hyperloglog,pub/sub,本文将主要介绍 Bitmap 的使用姿势以及其适用场景,主要知识点包括
518 0