Redis 基础教程

Redis 命令

Redis 高级教程

Redis 笔记

original icon
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.knowledgedict.com/tutorial/redis-scenarios.html

Redis 应用场景


Redis丰富的数据存储结构及基于内存的高性能操作使得其在很多应用场景中大显身手。以下列举了一些常涉及Redis的应用场景。

针对用户登录进行频控


该场景主要是利用Redis对用户登录进行既定时间粒度的访问次数上限限制,也可以将其扩展到某种业务(或接口)的频次限制。

如何用Redis设计一套逻辑,限制1个小时内每个用户ID最多只能登录5次?

解决如上问题思路如下:

  • 判断一个用户当前登录时间与向前推最近第5次登录时间做比较,如果不存在可以登录,若存在,则比较是否超过1个小时;在1小时内则拒绝登录,若大于等于1小时则可以登录。
  • 如上思路借助于Redis的列表数据结构,列表的每个元素即用户登录的时间(可登录的情况)。

此外,设计上需要注意的是,为了让该风控逻辑更加灵活,存储每个用户的列表长度设置可以比阈值稍大一些(或可动态配置),比如,根据调控粒度需要,我把登录上限次数从5调到10的话,如果列表入队只入5条(若写死),还需代码层面上调整 。时间粒度也可以设计成动态配置,便于后期灵活调整频控。

Java实现代码

import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import redis.clients.jedis.Jedis;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@WebFilter(urlPatterns = "/*")
public class FrequencyControlFilter implements Filter {

    private Jedis jedis;    //redis采用单例

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        if (jedis == null) {
            AnnotationConfigServletWebServerApplicationContext acswac = (AnnotationConfigServletWebServerApplicationContext) WebApplicationContextUtils
                    .getWebApplicationContext(servletRequest.getServletContext());
            jedis = (Jedis) acswac.getBean("jedis");
        }

        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String uid = httpServletRequest.getHeader("uid");   // uid获取粗略地mock成从消息头获取,实际操作中,uid获取因公司而异

        boolean thresholdLogInFlag = false; // 阈值登录标记
        int now = (int) (System.currentTimeMillis() / 1000);
        String loginTimeStampStr = jedis.lindex(uid, 4);    // 获取最近第5次登录的时间点,登录上限阈值可用动态配置
        if (loginTimeStampStr != null) {
            thresholdLogInFlag = true;
            int loginTimeStamp = Integer.parseInt(loginTimeStampStr);
            if (now - loginTimeStamp < 3600) {  // 登录频控的时间粒度1个小时,该值也可用动态配置,便于后续扩展
                HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
                httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden");
                return;
            }
        }

        jedis.rpush(uid, String.valueOf(now));  // 向表尾加入登录时间戳
        if (thresholdLogInFlag) {
            jedis.ltrim(uid, 0, 4); // 只保留上限阈值内最近登录的时间点
        }

        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

实现用户上线次数的统计


该场景主要是利用Redis的SETBIT命令和BITCOUNT命令来实现。

如何用Redis设计一套逻辑,统计用户上线的频次(时间粒度为1天)?

设计思路如下:

  • 通过SETBIT命令来记录用户的登录设置,比如以用户ID作为key,以那天所代表的网站的上线日作为offset参数,并将这个offset上的位设置为1。
  • 当要计算某个用户总共以来的上线次数时,就以该用户ID作为key,执行BITCOUNT命令,得出的结果就是该用户上线的总天数。

性能表现

如上统计例子,即使运行10年,占用的空间也只是每个用户10*365比特位(bit),即每个用户占用456字节。对于这种大小的数据来说,BITCOUNT的处理速度就像GET和INCR这种时间复杂度位O(1)的操作一样快。

Elasticsearch是一个开源的分布式搜索和分析引擎,旨在处理大规模数据的搜索、分析和可视化。地理空间数据分析:Elasticsear ...
要将自定义或其他库的函数应用于Pandas对象,有三个重要的方法,下面来讨论如何使用这些方法。使用适当的方法取决于函数是否期望在整个Data ...
创建一个Django项目的过程主要包括安装python环境、安装对应Django版本以及命令创建Django项目等后续操作。2版本:创建Dj ...
余弦相似度(CosineSimilarity)是用来度量两个向量之间角度余弦值的大小,来判断两个向量是否相似的一种方法。余弦相似度公式余弦相 ...
本文以作者的实际经验列出了基于点击、转化等目标的选取使用的特征分享,主要是针对搜索、推荐、广告等典型场景的实践,读者可以结合实际业务特点参考 ...