Redis缓存何以一枝独秀?——从百变应用场景与热门面试题中感受下Redis的核心特性与使用注意点
大家好,又见面了。
本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容,将会通过系列专题,讲清楚缓存的方方面面。如果感兴趣,欢迎关注以获取后续更新。
作为《深入理解缓存原理与实战设计》系列专栏,在前面的文章中,我们一起领略了Guava Cache、Caffeine、Ehcache等优秀的本地JVM级别本地缓存框架的特性、原理与具体的使用方法。除却本地缓存之外,在当前分布式、微服务等架构盛行的时代,本地缓存明显无法满足大型系统中的各种缓存诉求,比如前面文章中反复提及的缓存漂移问题、以及单机缓存无法逾越的内存容量瓶颈。作为应对之法,集中式缓存被广泛的使用在各中分布式系统中,而使用最广泛的莫过于大家耳熟能详的Redis
了。
提到Redis,大家应该都不会陌生,至少应该是有听过这个名字。在中大型分布式系统中,Redis似乎成了一种标配,而说到集中缓存,很多人脑海中第一闪过的也是Redis。Redis
是一个基于内存的非关系型数据库(NoSQL),主要是存储key-value类型的键值对数据,而value则支持多种不同的类型。由于其强悍的性能表现以及完善的可靠性与集群扩展机制,使其俘获了众多开发人员的青睐,成为了高并发系统的制胜法宝。接下来的几篇文章中呢,我们就一起聊一聊与Redis有关的内容,探讨下Redis在集中式缓存领域一枝独秀的秘诀。
Redis的各种数据类型
作为缓存组件,Redis的数据结构整体而言就是key-value
类型的键值对,但是Redis对于value类型的支持还是比较丰富的,提供了5种不同的数据结构,可以满足大部分场景的使用诉求。
对几种类型的结构特点与使用注意点梳理汇总如下:
类型 | 说明 | 支持功能 |
---|---|---|
string | 普通字符串 | 字符串的基础增删改查能力,如果是整数或者浮点数,还支持自增自减能力。 |
list | 链表内容,每个元素都是一个独立的字符串,内容可以相同 | 基础增删改查能力,从链表两端插入或者弹出元素,按照下标获取指定元素列表等等 |
set | 无序集合,每个元素都是一个独立字符串,元素之间不允许重复 | 基础增删改查能力,判断元素是否存在,随机获取元素等等 |
hash | 无序的key-value键值对集合 | 基础增删改查能力,获取所有的键值对 |
zset | 可以理解为一种比较特殊的hash结构,含有member和score两个概念,对应到hash类型上分别是key与value的关系,其区别点在在于score是固定的double类型的value | 基础增删改查能力,支持根据score排序并获取指定的排序个数的元素列表 |
实际的使用中,也会根据各自类型不同的特点,用来实现不同的业务诉求。
举个例子:
一个系统内的通知公告查看功能,可以将公告ID作为key,然后这边通知公告的阅读量作为score,在redis中存储为zset类型,然后每次读取详情操作的都累加更新下对应的score值,这样的话,就可以根据score进行降序排列,拉取到热门新闻公告的排行榜。
Redis的百变应用场景
基于Redis提供的基础能力,在项目中不同场景都有被广泛的使用,下面列举几个常见的使用场景。
- 分布式锁
在分布式系统里面经常会需要用到分布式锁,实现分布式锁的方式有很多种,其中使用的比较广泛的一种策略,就是基于Redis来实现的。之所以采用Redis来作为分布式锁,可以有几方面理由:
- redis足够的快
- redis提供了
setnx + expire
的机制,完全契合分布式锁的实现要点 Redisson
客户端的流行,使得基于redis的分布式锁更加简单
- 数据库扛压层
借助redis超高的处理性能,经常会被放置在数据库的前面,用于数据扛压场景使用。比如各种秒杀场景,可以将数据库中的库存信息缓存到redis中,然后利用redis来抗住秒杀期间洪水般的大并发量请求。
- 登录验证码存储
这个场景也很常见,比如用户发送的短信验证码,一般都会要求5分钟内有效。这种情况下,可以将验证码信息存储在redis中并设定5分钟后自动过期。这样的话就可以实现超时失效的功能,而无需业务层面去维护过期信息。
- 全局ID生成&全局限流
在分布式系统中,Redis作为一个可以被所有节点访问的集中节点,加上其具备的incrby
原子命令,使得在多个场景下发挥价值:
将其用作全局唯一ID的生成,以保证各个节点之间生成的唯一ID不会冲突。
incrby可以实现全局请求量的统计计数,结合expire一起可以实现定时重置计数器,进而实现限流能力。
- bitmap方式存储每日签到数据
其实,Redis还支持位图(Bitmap
)格式进行数据存储。前面我们说Redis支持五种数据结构里面并没有看到Bitmap类型的身影,其实Redis的bitmap数据最终存储的是string类型,但是Redis为Bitmap操作提供了配套的操作接口,比如setbit
命令。
位图的存在就是为了服务于海量数据的存储场景的,比如系统里面有10亿用户,现在需要记录每个人每天的签到情况,每天10亿数据量,如果用普通String类型存储,每天10亿条数据量,时间一久任何的Redis也扛不住。而基于bitmap的方式存储,则可以极大的降低整体数据量。关于redis的bitmap操作与使用,后面文章会展开阐述。
- 热门榜单生成
基于Redis的zset
数据结构,可以将热门值作为score进行存储,这样可以根据需要,按照score进行排序并拉取榜单数据。
后端面试中的常客
这篇文章中,我们改变下以往的文章行文叙事风格。我们先不直接切入到Redis的具体特性或功能点的实现原理与使用层面,而是先从面试场景作为切入口,通过几个面试问题,来感受下Redis整体的“魅力”、引出Redis所具备的核心特性与常见使用注意事项。
因为Redis在项目中的广泛使用,也让其成为了后端面试中的热门嘉宾。很多小伙伴应该在面试中都被问过与Redis有关的问题吧?当然有很多的八股文背诵一下就可以应付很多简单的面试场景,但笔者作为面试官一般不太会直接去问八股文问题,经常会将问题稍作包装之后再去问。
下面举几个例子。
Q1. 很多人都说Redis处理快是因为它是单线程的,Redis进程中真的只有一个线程吗?为什么常规项目中为了提升并发量都会采用线程池等方式来多线程处理,而Redis却反其道而行之呢?
很多的面试八股文中都会提到说Redis是单线程的,这个说法其实不够严谨,因为Redis中并非是只有一个线程,整个进程中还有一些额外的线程负责做一些辅助的其他事务,比如管理与客户端的连接,比如队列中消息的维护等等。
Redis整体基于一种多路复用的机制来实现请求的接收与分配处理。整体简化后的处理逻辑如下图所示。
所以说,其实Redis仅仅是采用单线程来负责执行命令请求处理,而非整个Redis就是一个单线程的。回到最初的问题,为什么Redis选择采用单线程的方式来执行命令。在多线程编程的时候面临问题主要有:
- 并发线程安全问题, 需要保证操作的先后顺序,需要保证同一时刻只能有1个线程对某个对象进行写操作 —— 需要构建完备的同步保护机制,会对整体性能造成影响。
- 多线程维护的系统额外开销 —— CPU需要不停的在多个线程之间进行切换,由此会带来一系列的额外开销。
而由于Redis是一种key-value模型的数据结构模式,比如很多查询操作都是O(1)
的时间复杂度,其操作执行速度非常快,所以这种情况下,结合I/O多路复用
模型一起,使用单线程的方式执行命令,反而可以达到比多线程更加优异的表现。
问题可以进一步引申,可以继续聊一些其他问题。比如:
既然Redis是单线程的,那使用的时候有什么需要注意的事项吗?
不能执行耗时操作,会阻塞其余请求命令的执行。I/O多路复用是个什么概念?它和BIO、NIO之间有什么异同?
诸如此类的问题,都可以进一步的去展开考察。当前计算机一般都是多核CPU,用单线程去执行的话,相当于其它几个核就浪费了,那有什么方式可以将其余的几个核也利用起来么?
答案其实也不难,在一台机器上同时去部署多个Redis进程,组成个集群,就可以啦。
Q2. 如果我想要查询一下生产环境的Redis中有多少以“User_”开头的记录数量,可以怎么做?
这个问题其实是有一点小陷阱的。查找以指定前缀开头的记录,首先很多同学想到的就是keys
命令,但问题中有个约束是在生产环境中执行。所以这个问题看似简单,其实需要结合如下几点来综合考虑:
- 通常情况下,生产环境中的数据量是非常大的、且请求并发量会比较高;
- Redis的
keys
命令是一个耗时操作,复杂度O(n)
,数据量越大执行速度越慢; - Redis的命令执行是单线程执行的。
基于上述几点因素,如果在数据量较大的生产环境去执行keys
命令将会导致执行耗时特别长,而由于Redis是单线程执行命令,就会导致其余请求命令被阻塞无法执行,这样在一个高并发集群内,很容易造成集群内请求的大面积阻塞,影响系统的整体稳定性。
那么keys命令不可以用,有什么替代方案呢?可以使用scan
命令。
Q3. 假如有一批机器,内存都比较小(单机内存小于整体待缓存数据量),用来搭建个Redis做热点数据缓存扛压以降低数据库的请求压力。如果你来做的话,会有哪些应对思路呢?
这个问题就比较开放,而且答案也不唯一,考核的点也比较综合。
首先来分析下题目,从题干描述中可以捕捉到几个信息,以及对应的关联知识点:
- 单机内存小于整体数据量,所以想要将所有数据全量加载到单机内存里面是不可行的;
- 使用Redis的用途是扛压来降低数据库访问压力的,也就是允许部分请求穿透Redis打到数据库上的,所以可以考虑将有限内存用来存放
热点数据
,扛住大部分的流量; - 题目说有一批机器,就是说机器的数量不止一台,所以可以考虑构建
集群
的方式,扩展Redis集群总内存大小,这样以集群的力量来缓存全部的数据量。
所以说这个题目里面其实涉及到了两个考点:
- 热点数据的概念、也即Redis的数据淘汰策略。
- Redis集群扩展的相关概念。
更进一步,又可以引申出很多其它细节问题,比如:
Redis中的数据淘汰策略有哪些?
no-enviction、volatile-lru、volatile-ttl、volatile-random、allkeys-lru、allkeys-randomRedis的数据淘汰策略与数据过期有啥区别?
数据过期是达到了设定的过期时间之后使数据不可用,而数据淘汰策略主要是在容量满之后采取的被动应对策略。Redis集群中是如何决定一个记录应该保存在哪个节点上的?
关于一致性Hash相关的内容,以及如何解决数据倾斜问题
、节点扩容对缓存命中情况的影响等等。
回头看下,是不是其中蕴含的内容还是蛮多的?
这里我们以面试场景中会被问及的几个问题作为切入点,大概聊了下与Redis有关的一系列内容。当然这里介绍的都比较浅显,甚至只是列了下相关的知识点,主要是先让大家先感受下Redis所包含与涉及的相关知识点。在后续的文章中,我们将逐步逐个地去剖析与介绍。
小结回顾
好啦,作为redis部分的第一篇内容,我们只是简单的聊了下Redis
的基础概念以及主要的特性介绍,同时通过几个实际的面试题演示了下Redis整体内容的“博大精深”。而关于Redis的更多细化方向的展开阐述,我们将会在后续文章中逐步介绍。那么你对Redis如何看呢?欢迎评论区一起交流下,期待和各位小伙伴们一起切磋、共同成长。
补充说明1 :
本文属于《深入理解缓存原理与实战设计》系列专栏的内容之一。该专栏围绕缓存这个宏大命题进行展开阐述,全方位、系统性地深度剖析各种缓存实现策略与原理、以及缓存的各种用法、各种问题应对策略,并一起探讨下缓存设计的哲学。
如果有兴趣,也欢迎关注此专栏。
补充说明2 :
- 关于本文中涉及的演示代码的完整示例,我已经整理并提交到github中,如果您有需要,可以自取:https://github.com/veezean/JavaBasicSkills
我是悟道,聊技术、又不仅仅聊技术~
如果觉得有用,请点赞 + 关注让我感受到您的支持。也可以关注下我的公众号【架构悟道】,获取更及时的更新。
期待与你一起探讨,一起成长为更好的自己。
Redis缓存何以一枝独秀?——从百变应用场景与热门面试题中感受下Redis的核心特性与使用注意点的更多相关文章
- 花式玩转社交App,百变应用场景
活动概况 时间:2016年03月20日13:30-16:30 地点:深圳腾讯大厦2楼多功能厅(南山区科技园科技中一路) 主办:APICloud.腾讯云分析和信鸽.云之讯 报名链接:http://act ...
- 在这个应用中,我使用了 MQ 来处理异步流程、Redis 缓存热点数据、MySQL 持久化数据,还有就是在系统中调用另外一个业务系统的接口,对我的应用来说这些都是属于 RPC 调用,而 MQ、MySQL 持久化的数据也会存在于一个分布式文件系统中,他们之间的调用也是需要用 RPC 来完成数据交互的。
在这个应用中,我使用了 MQ 来处理异步流程.Redis 缓存热点数据.MySQL 持久化数据,还有就是在系统中调用另外一个业务系统的接口,对我的应用来说这些都是属于 RPC 调用,而 MQ.MySQ ...
- Redis缓存的设计、性能、应用与数据集群同步
Redis缓存的设计.性能.应用与数据集群同步 http://youzhixueyuan.com/design-performance-and-application-of-redis-cache.h ...
- Windows Azure Redis 缓存服务
8月20日,Windows Azure (中国版)开始提供Redis缓存服务,比较国际版的Microsoft Azure晚了差不多一年的时间.说实话,微软真不应该将这个重要的功能delay这么长时间, ...
- [Redis]c# redis缓存辅助类
public static class RedisCache { private static IRedisClient RCClient = null; /// <summary> // ...
- Azure Redis 缓存使用注意事项与排查问题文档整理
StackExchange.Redis 使用名为 synctimeout 的配置设置进行同步操作,该设置的默认值为 1000 毫秒. 如果同步调用未在规定时间内完成,StackExchange.Red ...
- Redis缓存Mysql模拟用户登录Java实现实例[www]
Redis缓存Mysql模拟用户登录Java实现实例 https://jingyan.baidu.com/article/09ea3ede1dd0f0c0aede3938.html redis+mys ...
- Redis缓存数据库安全加固指导(二)
背景 在众多开源缓存技术中,Redis无疑是目前功能最为强大,应用最多的缓存技术之一,参考2018年国外数据库技术权威网站DB-Engines关于key-value数据库流行度排名,Redis暂列第一 ...
- Redis缓存穿透、缓存雪崩、redis并发问题 并发竞争key的解决方案 (阿里)
阿里的人问我 缓存雪崩(大量数据在同一时间过期了)了如何处理,缓存击穿了如何处理,回答的很烂,做了总结: 把redis作为缓存使用已经是司空见惯,但是使用redis后也可能会碰到一系列的问题,尤其是数 ...
- redis缓存击穿和缓存雪崩
工作中经常会用到redis来做缓存,以防止后台db挂掉.但是db数据一般都在10T以上,不可能把mysql中的数据全部放入redis中,所以一般是将一些热key放入redis中. 缓存击穿 一个请求先 ...
随机推荐
- 图解 | 聊聊 MyBatis 缓存
首发公众号-悟空聊架构:图解 | 聊聊 MyBatis 缓存 你好,我是悟空. 本文主要内容如下: 一.MyBatis 缓存中的常用概念 MyBatis 缓存:它用来优化 SQL 数据库查询的,但是可 ...
- GitLab CI/CD 自动化部署入门
前言:因为找了B站内推,测试开发,正好知道内部使用GitLab做自动化测试,所以简单学了一下,有错误的地方请指正. 入门 初始化 cp: 无法获取'/root/node-v12.9.0-linux-x ...
- web share api 分享
概述 Navigator.share() 方法通过调用本机的共享机制作为 Web Share API 的一部分.如果不支持 Web Share API,则此方法为 undefined. 此项功能仅在 ...
- 41.SessionAuthenticatio和自定义认证
SessionAuthentication认证介绍 SessionAuthentication使用了Django默认的会话后端 适合AJAX客户端等运行在同样会话上下文环境中的模式 是DRF默认的认证 ...
- 在CentOS7下安装Oracle11教程
前言 安装oracle时,发现网上的文章总是缺少一些信息,导致安装不顺利,因为我对一些文章进行了整合,用以备忘. Oracle安装 首先下载linux版本的oracle安装文件,然后通过XFTP上传到 ...
- 如何在bat中进入虚拟环境
很多情况下我们希望在项目中建立一个build.bat用于项目的自动构建,避免每次构建时都需要手动在控制台中输入命令. 例如对于 pyinstall 的项目,只需要如下的实现: pyinstaller ...
- Python学习之实例2
一.根据输入的各科成绩统计学生成绩是否达标 1 (背景:输入学生数量,以及各个学生语文.数学.英语.历史四科的成绩,如果总成绩小于 240,则成绩不达标,否则成绩合格) 2 3 #coding=utf ...
- Ant Design Pro:Layout 组件——嵌套布局
在 BasicLayout.jsx 文件中修改 <ProLayout layout="topmenu" className="chenshuai2144&q ...
- 基于python的数学建模---Fuzzy C-Means(模糊C均值聚类)
簇数的确定: 要用到k-means里面的轮廓系数 基于python的数学建模---轮廓系数的确定 - 坤丶 - 博客园 (cnblogs.com) 模糊c的代码 import copy import ...
- [排序算法] 树形选择排序 (C++)
树形选择排序解释 树形选择排序 又称为锦标赛排序,其实理解起来很简单. 数组的 n 个元素就好像在进行锦标赛一样,每一轮小比赛每两个一组决出胜负(比较谁更小). 再将每一轮的胜者每两个一组进行小比赛, ...