在分布式盛行的今天,本地缓存明显无法满足分布式场景的缓存诉求.作为应对之法,集中式缓存被广泛的使用在各中分布式系统中,而使用最广泛的莫过于大家耳熟能详的Redis了,本篇开始聊一聊Redis相关的内容.
大家好,又见面了.
提到Redis,大家应该都不会陌生,至少应该是有听过这个名字.在中大型分布式系统中,Redis似乎成了一种标配,而说到集中缓存,很多人脑海中第一闪过的也是Redis.Redis是一个基于内存的非关系型数据库(NoSQL),主要是存储key-value类型的键值对数据,而value则支持多种不同的类型.由于其强悍的性能表现以及完善的可靠性与集群扩展机制,使其俘获了众多开发人员的青睐,成为了高并发系统的制胜法宝.此时此刻呢的几篇文章中呢,我们就一起聊一聊与Redis有关的内容,探讨下Redis在集中式缓存领域一枝独秀的秘诀.
对几种类型的结构特点与使用注意点梳理汇总如下:
实际的使用中,也会根据各自类型不同的特点,用来实现不同的业务诉求.
举个例子:
一个系统内的通知公告查看功能,可以将公告ID作为key,然后这边通知公告的阅读量作为score,在redis中存储为zset类型,然后每次读取详情操作的都累加更新下对应的score值,这样的话,就可以根据score进行降序排列,拉取到热门新闻公告的排行榜.
基于Redis提供的基础能力,在项目中不同场景都有被广泛的使用,下面列举几个常见的使用场景.
分布式锁
在分布式系统里面经常会需要用到分布式锁,实现分布式锁的方式有很多种,其中使用的比较广泛的一种策略,就是基于Redis来实现的.之所以采用Redis来作为分布式锁,可以有几方面理由:
redis足够的快
redis提供了setnx + expire的机制,完全契合分布式锁的实现要点
数据库扛压层
借助redis超高的处理性能,经常会被放置在数据库的前面,用于数据扛压场景使用.比如各种秒杀场景,可以将数据库中的库存信息缓存到redis中,然后利用redis来抗住秒杀期间洪水般的大并发量请求.
登录验证码存储
全局ID生成全局限流
在分布式系统中,Redis作为一个可以被所有节点访问的集中节点,加上其具备的incrby原子命令,使得在多个场景下发挥价值:
将其用作全局唯一ID的生成,以保证各个节点之间生成的唯一ID不会冲突.
incrby可以实现全局请求量的统计计数,结合expire一起可以实现定时重置计数器,进而实现限流能力.
bitmap方式存储每日签到数据
其实,Redis还支持位图(Bitmap)格式进行数据存储.前面我们说Redis支持五种数据结构里面并没有看到Bitmap类型的身影,其实Redis的bitmap数据最终存储的是string类型,但是Redis为Bitmap操作提供了配套的操作接口,比如setbit命令.
热门榜单生成
基于Redis的zset数据结构,可以将热门值作为score进行存储,这样可以根据需要,按照score进行排序并拉取榜单数据.
这篇文章中,我们改变下以往的文章行文叙事风格.我们先不直接切入到Redis的具体特性或功能点的实现原理与使用层面,而是先从面试场景作为切入口,通过几个面试问题,来感受下Redis整体的"魅力"、引出Redis所具备的核心特性与常见使用注意事项.
因为Redis在项目中的广泛使用,也让其成为了后端面试中的热门嘉宾.很多小伙伴应该在面试中都被问过与Redis有关的问题吧?当然有很多的八股文背诵一下就可以应付很多简单的面试场景,但笔者作为面试官一般不太会直接去问八股文问题,经常会将问题稍作包装之后再去问.
下面举几个例子.
Q1. 很多人都说Redis处理快是因为它是单线程的,Redis进程中真的只有一个线程吗?为什么常规项目中为了提升并发量都会采用线程池等方式来多线程处理,而Redis却反其道而行之呢?
Redis整体基于一种多路复用的机制来实现请求的接收与分配处理.整体简化后的处理逻辑如下图所示.
所以说,其实Redis仅仅是采用单线程来负责执行命令请求处理,而非整个Redis就是一个单线程的.回到最初的问题,为什么Redis选择采用单线程的方式来执行命令.在多线程编程的时候面临问题主要有:
并发线程安全问题, 需要保证操作的先后顺序,需要保证同一时刻只能有1个线程对某个对象进行写操作 —— 需要构建完备的同步保护机制,会对整体性能造成影响.
多线程维护的系统额外开销 —— CPU需要不停的在多个线程之间进行切换,由此会带来一系列的额外开销.
而由于Redis是一种key-value模型的数据结构模式,比如很多查询操作都是O(1)的时间复杂度,其操作执行速度非常快,所以这种情况下,结合I/O多路复用模型一起,使用单线程的方式执行命令,反而可以达到比多线程更加优异的表现.
问题可以进一步引申,可以继续聊一些其他问题.比如:
既然Redis是单线程的,那使用的时候有什么需要注意的事项吗?不能执行耗时操作,会阻塞其余请求命令的执行.
当前计算机一般都是多核CPU,用单线程去执行的话,相当于其它几个核就浪费了,那有什么方式可以将其余的几个核也利用起来么?答案其实也不难,在一台机器上同时去部署多个Redis进程,组成个集群,就可以啦.
这个问题其实是有一点小陷阱的.查找以指定前缀开头的记录,首先很多同学想到的就是keys命令,但问题中有个约束是在生产环境中执行.所以这个问题看似简单,其实需要结合如下几点来综合考虑:
通常情况下,生产环境中的数据量是非常大的、且请求并发量会比较高;
Redis的keys命令是一个耗时操作,复杂度O(n),数据量越大执行速度越慢;
Redis的命令执行是单线程执行的.
基于上述几点因素,如果在数据量较大的生产环境去执行keys命令将会导致执行耗时特别长,而由于Redis是单线程执行命令,就会导致其余请求命令被阻塞无法执行,这样在一个高并发集群内,很容易造成集群内请求的大面积阻塞,影响系统的整体稳定性.
那么keys命令不可以用,有什么替代方案呢?可以使用scan命令.
这个问题就比较开放,而且答案也不唯一,考核的点也比较综合.
首先来分析下题目,从题干描述中可以捕捉到几个信息,以及对应的关联知识点:
单机内存小于整体数据量,所以想要将所有数据全量加载到单机内存里面是不可行的;
使用Redis的用途是扛压来降低数据库访问压力的,也就是允许部分请求穿透Redis打到数据库上的,所以可以考虑将有限内存用来存放热点数据,扛住大部分的流量;
题目说有一批机器,就是说机器的数量不止一台,所以可以考虑构建集群的方式,扩展Redis集群总内存大小,这样以集群的力量来缓存全部的数据量.
所以说这个题目里面其实涉及到了两个考点:
热点数据的概念、也即Redis的数据淘汰策略.
Redis集群扩展的相关概念.
更进一步,又可以引申出很多其它细节问题,比如:
Redis中的数据淘汰策略有哪些?no-enviction、volatile-lru、volatile-ttl、volatile-random、allkeys-lru、allkeys-random
Redis的数据淘汰策略与数据过期有啥区别?数据过期是达到了设定的过期时间之后使数据不可用,而数据淘汰策略主要是在容量满之后采取的被动应对策略.
Redis集群中是如何决定一个记录应该保存在哪个节点上的?关于一致性Hash相关的内容,以及如何解决数据倾斜问题、节点扩容对缓存命中情况的影响等等.
回头看下,是不是其中蕴含的内容还是蛮多的?
这里我们以面试场景中会被问及的几个问题作为切入点,大概聊了下与Redis有关的一系列内容.当然这里介绍的都比较浅显,甚至只是列了下相关的知识点,主要是先让大家先感受下Redis所包含与涉及的相关知识点.在后续的文章中,我们将逐步逐个地去剖析与介绍.
补充说明1 :
关于本文中涉及的演示代码的完整示例,我已经整理并提交到github中,如果您有需要,可以自取:https://github.com/veezean/JavaBasicSkills
我是悟道,聊技术、又不仅仅聊技术~
如果觉得有用,请点赞 + 关注让我感受到您的支持.也可以关注下我的公众号【架构悟道】,获取更及时的更新.
期待与你一起探讨,一起成长为更好的自己.
以上就是土嘎嘎小编为大家整理的Redis缓存何以一枝独秀?——从百变应用场景与热门面试题中感受下Redis的核心特性与使用注意点相关主题介绍,如果您觉得小编更新的文章只要能对粉丝们有用,就是我们最大的鼓励和动力,不要忘记讲本站分享给您身边的朋友哦!!