Redis实战

Redis 实战

第一章 初识 Redis

1.1 Redis 简介

  • Redis 是内存数据库,但可以通过 RDB 和 AOF 两种方式来支持数据持久化。
  • Redis 通过主从复制特性来扩展读性能(主服务器写,从服务器读)
  • Redis 通过客户端分片来扩展写性能

1.1.1 持久化

Redis 拥有两种不同形式的持久化方法,这两种方式都可以用小而紧凑的格式将内存中的数据写入到磁盘中:

  • 方式一:时间点转储(point-in-time dump)

    转储操作既可以在 ”指定时间段内有指定数量的写操作执行“ 时触发,也可以通过调用两条特定的转储命令来触发

  • 方式二:AOF(append-only)文件

    可以根据数据的重要程度,对AOF文件进行设置,从不重要到重要依次为:不同步、一秒同步一次、一次命令同步一次

1.1.2 Redis 的主从复制特性

  1. 执行复制的从服务器会连接上主服务器,接收主服务器发送的整个数据库的初始副本(copy)
  2. 主服务器执行的写命令,都会被发送给从服务器,从而更新从服务器中的数据集
  3. 因为从服务器中的数据不断被更新,因此客户端可以从任意一台从服务器中读取数据,来避免对主服务器进行集中式的访问,减轻主服务器的压力

1.2 Redis 数据结构简介

结构类型 结构存储的值 结构的读写能力
String 字符串、整数、浮点数 字符串操作;
整数和浮点数执行自增、自减操作
List 双向链表结构,链表的每个节点都包含一个String
Set 无序集合 集合的增删查;
随机获取一个集合中的元素;
计算集合与集合之间的交集、差集、并集
Hash
ZSet value值限制为score的一个Hash结构,
自动根据value值进行排序(升序)
获取[stop, end]之间的元素;
获取value在给定范围内的元素

1.3 Redis 应用场景

最近越来越多的网站提供对网页链接、文章或问题进行投票的功能,以 StackOverflow 为例,网站会根据文章的发布时间和获得的投票数来计算一个分数,默认按照该分数来进行文章的排序。

在StackOverflow中使用Redis

1.3.1 对文章进行投票

为了产生一个能够随着时间流逝而不断减少的评分,程序需要根据文章的发布时间和当前时间来计算文章的评分,具体方法为:文章评分 = 文章得到的票数 $\times$ 某个常数 + 文章的发布时间的时间戳(秒数)。其中,常数表示的含义为一票能够等价对应的秒数。

根据评分进行排序只是展示的一种方式,还可以根据发布时间进行排序,因此需要提供多个 ZSet 集合,还是以 StackOverflow 为例

一种排序方式对应一个ZSet

为防止一个用户对同一篇文章进行多次投票,因此需要为每一篇文章记录一个已投票的用户名单。因此,每篇文章都会创建一个集合。为了节省内存,可以规定在一段时间后禁止投票,将文章的评分几乎确定下来,此时也可以自动删除这个已投票的用户名单集合(设置该集合的过期时间)。

1.3.2 发布并获取文章

1.3.3 对文章进行分组

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ZSetOperations;

@SpringBootTest
public class RedisInActionMainTest {
    // 一周对应的毫秒数
    private static final long ONE_WEEK_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
    // 一张投票对应的毫秒数, 计算得出
    private static final int VOTE_SCORE = 432 * 1000;

    // 记录文章是否过期
    private static final String TIMEOUT_PREFIX = "timeout:";
    // 记录文章详细信息
    private static final String POST_PREFIX = "post:";
    // 记录文章的投票人
    private static final String VOTED_PREFIX = "voted:";

    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 发布文章
     */
    @Test
    public void publishPostTest() {

    }


    /**
     * 对文章进行投票
     */
    @Test
    public void votePostTest() {
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        SetOperations setOperations = redisTemplate.opsForSet();
        HashOperations hashOperations = redisTemplate.opsForHash();

        String userId = "root";
        String postId = "123456";

        // 检查是否已经过了能够投票的时间
        if (zSetOperations.score(TIMEOUT_PREFIX, postId) + ONE_WEEK_IN_MILLISECONDS > System.currentTimeMillis()) {
            System.out.println("This Post isn't voted!");
            return;
        }

        // TODO: 使用Lua脚本
        // 为该文章进行投票
        // 1. 记录投票人, 如果不是重复投票的话
        if (setOperations.add(VOTED_PREFIX + postId, userId) > 0) {
            // 2. 更新文章的得分
            hashOperations.increment(POST_PREFIX + postId, "score", VOTE_SCORE);
            // 3. 更新文章的得票数
            hashOperations.increment(POST_PREFIX + postId, "votes", 1);
        }

    }
}

   转载规则


《Redis实战》 熊水斌 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
HTTP2 HTTP2
HTTP2sequenceDiagram participant Client participant Server Client->>Server: ClientHello Server-->>Client
2023-04-03
下一篇 
BeanFactory容器的实现 BeanFactory容器的实现
BeanFactory 容器的实现BeanFactory 不会做的事情: 不会主动调用 BeanFactoryPostProcessor 不会主动调用(添加)BeanPostProcessor 不会主动实例化单例对象(懒加载) 不会解析B
2023-04-02
  目录