文章目录
- 1 什么是 Redis
- 2 常见的分布式缓存技术
- 3 Memcached 与 Redis 的异同
- 4 缓存数据的处理流程
- 5 Redis 的数据类型
- 6 一个字符串能存储的最大容量
- 7 为什么要使用缓存
- 8 具体说说 Redis 可以做什么
- 9 Redis 为什么不用多线程以及为什么 Redis6.0 之后又引入了多线程
- 10 Redis 过期删除策略
- 11 设置缓存过期的作用
- 12 缓存问题
- 13 如何保证 Redis 中的20W数据都是数据库中200W数据的热点数据
- 14 如果有大量的 key 需要设置同一时间过期,一般需要注意什么
- 15 使用过 Redis 分布式锁么,它是什么回事
- 16 如何使用Redis实现一个分布式锁
- 17 Memcache与Redis的区别都有哪些?
1 什么是 Redis
Redis 全称Remote Dictionary Server 远程字典服务器,是一个使用C语言开发的 用于存储k-v键值对的高性能NoSQL数据库,由于是基于内存的数据库,因此读写速度非常快,广泛应用于缓存和分布式锁,此外Redis还支持事务、持久化、Lua脚本,可以使用众多复杂的业务场景。
2 常见的分布式缓存技术
缓存使用的比较多的主要是 Memcached 和 Redis。不过现在使用 Memcached 做缓存的比较少
Memcached 是分布式缓存最开始兴起的时候比较常用的方案,后来随着 Redis 的发展,Redis 逐渐成为了人们的首选
分布式缓存主要解决的是单机缓存的容量受服务器限制并且无法保存通用信息的问题。因为本地缓存只在当前服务里有效,比如如果你部署了两个相同的服务,他们两者之间的缓存数据是无法共享的
3 Memcached 与 Redis 的异同
共同点
- 都是基于内存的数据库,一般都用来当做缓存使用
- 都有过期策略
- 两者的性能都非常高
异同点
- Redis 支持更丰富的数据类型,Memcached 只支持最简单的 k/v 数据类型,所有的值均是简单的字符串
- Redis 支持数据的持久化,而 Memecache 把数据全部存在内存之中,重启之后数据丢失
- Redis 有灾难恢复机制,因为可以把缓存中的数据持久化到磁盘上
- Redis 在服务器内存使用完之后,可以将不用的数据放到磁盘上。但 Memcached 在服务器内存使用完之后,就会直接报异常
- Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但 Redis目前是原生支持 cluster 模式的
- Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 使用单线程的多路 IO 复用模型
- Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持。并且,Redis 支持更多的编程语言
- Memcached 过期数据的删除策略只用了惰性删除,而 Redis 同时使用了惰性删除与定期删除
4 缓存数据的处理流程
缓存的简单处理流程如下:
- 如果用户请求的数据命中缓存,就直接返回
- 缓存中不存在的话,查看数据库中是否存在
- 如果数据库中存在,则更新缓存中的数据
- 如果数据库中不存在,则返回空数据
5 Redis 的数据类型
Redis 支持五种基本数据类型:string(字符串),hash(哈希),list(列表),set(集合)及 zsetsorted set:有序集合)
此外还有三种特殊数据类型: GEO , BitMap, HyperLog
6 一个字符串能存储的最大容量
512 M
7 为什么要使用缓存
高性能
将高频访问的数据放进缓存,保证高频操作可以快速响应,提高系统响应速度和用户体验
高并发
一般像 MySQL 这类数据库的 QPS 大概都在 1w 左右(4 核 8g) ,但是使用 Redis 缓存之后很容易达到 10w+,甚至最高能达到 30w+(redis 集群的话会更高)
QPS(Query Per Second):服务器每秒可以执行的查询次数
所以增加缓存可以大大提高系统的并发能力
8 具体说说 Redis 可以做什么
缓存
缓存机制几乎在所有的大型网站都有使用,合理地使用缓存不仅可以加 快数据的访问速度,而且能够有效地降低后端数据源的压力。Redis提供了键值过期时间设置,并且也提供了灵活控制最大内存和内存溢出后的淘汰策略。可以这么说,一个合理的缓存设计能够很好的为一个网站的稳定保驾护航
排行榜系统
排行榜系统几乎存在于所有的网站,例如按照热度排名的排行榜,按照 发布时间的排行榜,按照各种复杂维度计算出的排行榜,Redis提供了列表和有序集合数据结构,合理地使用这些数据结构可以很方便地构建各种排行榜系统
计数器应用
计数器在网站中的作用至关重要,例如视频网站有播放数、电商网站有 浏览数,为了保证数据的实时性,每一次播放和浏览都要做加1的操作,如果并发量很大对于传统关系型数据的性能是一种挑战。Redis天然支持计数功能而且计数的性能也非常好,可以说是计数器系统的重要选择
社交网络
赞/踩、粉丝、共同好友/喜好、推送、下拉刷新等是社交网站的必备功能,由于社交网站访问量通常比较大,而且传统的关系型数据不太适合保存这种类型的数据,Redis 提供的数据结构可以相对比较容易地实现这些功能
消息队列系统
消息队列系统可以说是一个大型网站的必备基础组件,因为其具有业务 解耦、非实时业务削峰等特性。Redis 提供了发布订阅功能和阻塞队列的功能,虽然和专业的消息队列比还不够足够强大,但是对于一般的消息队列功能基本可以满足
9 Redis 为什么不用多线程以及为什么 Redis6.0 之后又引入了多线程
使用单线程的原因
- 单线程编程更容易并且更容易维护
- Redis 的性能瓶颈不在 CPU ,主要在内存和网络
- 多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能
Redis6.0 引入多线程主要是为了提高网络 IO 读写性能
,因为这个是 Redis 中的一个性能瓶颈
虽然 Redis6.0 引入了多线程,但是 Redis 的多线程只是在网络数据的读写这类耗时操作上使用了,执行命令仍然是单线程顺序执行
10 Redis 过期删除策略
-
定时删除:在设置键的过期时间的同时,创建一个定时器 time,让定时器在键的过期时间来临时,立即执行对键的删除操作
-
惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键
-
定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定
Redis采用了定期删除和惰性删除两种删除策略
11 设置缓存过期的作用
因为内存是有限的,如果缓存中的所有数据都是一直保存的话,很快就会 OOM了
Redis 中除了字符串类型有自己独有设置过期时间的命令 setex 外,其他方法都需要依靠 expire 命令来设置过期时间 。另外 persist 命令可以移除一个键的过期时间
还有一种情况需要设置过期时间,当我们的业务场景就是需要某个数据只在某一时间段内存在,比如我们的短信验证码可能只在 1 分钟内有效,用户登录的 token 可能只在 1 天内有效,此时设置过期时间就能比较好的处理,如果使用传统的数据库来处理的话,一般都是自己判断过期,这样更麻烦并且性能要差很多
12 缓存问题
1 缓存穿透
缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。比如某个攻击者故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库上,使得数据库由于性能的原因崩溃,导致系统异常
解决方案
-
接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
-
将key缓存起来,从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
-
使用布隆过滤器
2 缓存击穿
缓存击穿是指缓存中没有但数据库中有的单条数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
解决方案
-
设置热点数据永远不过期。
-
加斥锁。读数据库时需要获取锁,一条请求拿到锁之后读取数据并更新缓存,为了防止那些抢锁失败线程重新获取到锁后又进行读数据库操作。
3 缓存雪崩
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至 down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案
-
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
-
设置热点数据永远不过期。
13 如何保证 Redis 中的20W数据都是数据库中200W数据的热点数据
正确设置内存淘汰策略,当 Redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略,从而剩下的数据就是热点数据
14 如果有大量的 key 需要设置同一时间过期,一般需要注意什么
如果大量的 key 过期时间设置的过于集中,到过期的那个时间点,Redis 可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些
15 使用过 Redis 分布式锁么,它是什么回事
先拿 setnx 来争抢锁,抢到之后,再用 expire 给锁加一个过期时间防止锁忘记了释放。
追问:
如果在 setnx 之后执行 expire之前进程意外 crash 或者要重启维护了,那会怎么样?锁无法释放
set 指令有非常复杂的参数,可以同时把 setnx 和expire 合成一条指令来用的!或者使用Lua脚本
16 如何使用Redis实现一个分布式锁
最简单redis分布式锁的实现方式:加锁:setnx(key,1),解锁:del(key),问题:如果客户忘记解锁,将会出现死锁。第二种分布式锁的实现方式:setnx(key,1)+expire(key,30),解锁:del(key).问题:,由于setnx和expire的非原子性,当第二步挂掉,仍然会出现死锁。第三种方式:加锁:将setnx和expire变成原子性操作,set(key,1,30,NX),解锁:del(key)。同时考虑到线程A还在执行,但是锁已经到期,当线程A执行结束时去释放锁时,可能就会释放别的线程锁,所以在解锁时要先判断一下value值,看是不是该锁,如果是,再进行删除。但是判断和删除锁也不是原子性的,也会存在并发问题,可以使用lua脚本解决。
17 Memcache与Redis的区别都有哪些?
1)、存储方式 Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis有部份存在硬盘上,redis可以持久化其数据
2)、数据支持类型 memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型 ,提供list,set,zset,hash等数据结构的存储
3)、使用底层模型不同 它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。 Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
4). value 值大小不同:Redis 最大可以达到 512M;memcache 只有 1M。
5)redis的速度比memcached快很多
6)Redis支持数据的备份,即master-slave模式的数据备份。