为什么Redis执行速度快
- 纯内存操作。Redis将数据存储在内存中,并且数据的读写操作也是在内存上进行的,这种读写方式远远快于在对磁盘的数据操作。
- I/O多路复用。同时监听多个客户端连接,只有当网络事件发生时才会进行实际的I/O。这样有效的利用了CPU,减少了无谓的等待。
- 高效的数据结构。Redis采用了哈希表、有序集合等数据结构,有效的提高了数据的读写效率。
Redis如何保证线程安全?
Redis在6.0版本前,所有客户端的请求处理、命令执行和数据读写都是在一个主线程中完成的,这种设计目的是为了防止多线程环境下锁竞争和上下文切换带来的性能开销,从而在保证线程安全性的同时提高性能。Redis在6.0版本后,采用多路复用监听多个客户端的连接,并且使用多个工作线程处理连接请求,但是指令执行仍然由单个主线程完成,所以不会存在线程安全问题。
在实际工作中,Redis实现了哪些业务场景?
- 缓存。Redis提供了键过期功能和键淘汰策略。
- 排行榜。Redis提供的有序集合(zset)能方便的实现排行榜功能。
- 分布式会话。在集群模式下,一般会搭建以Redis等内存数据库为中心的session服务,session不再由容器管理,而是由session服务及内存数据库管理。
- 分布式锁。在高并发场景下,可以利用Redis的setnx功能来编写分布式锁。
Redis常用数据类型
- 字符串。String 是一种二进制安全的数据类型,可以用来存储任何类型的数据比如字符串、整数、浮点数、图片(图片的 base64 编码或者解码或者图片的路径)、序列化后的对象。
- 应用场景。
- 需要存储常规数据的场景。
- 举例:缓存 Session、Token、图片地址、序列化后的对象(相比较于 Hash 存储更节省内存)。
- 相关命令:
SET
、GET
。
- 需要计数的场景。
- 举例:用户单位时间的请求数(简单限流可以用到)、页面单位时间的访问数。
- 相关命令:
SET
、GET
、INCR
、DECR
。
- 需要存储常规数据的场景。
- 应用场景。
- 列表。Redis 中的 List 其实就是链表数据结构的实现。
- 应用场景
- 信息流展示。
- 举例:最新文章、最新动态。
- 相关命令:
LPUSH
、LRANGE
。
- 信息流展示。
- 应用场景
- 哈希。Redis 中的 Hash 是一个 String 类型的 field-value(键值对) 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接修改这个对象中的某些字段的值。
- 应用场景。
- 对象数据存储场景。
- 举例:用户信息、商品信息、文章信息、购物车信息。
- 相关命令:
HSET
(设置单个字段的值)、HMSET
(设置多个字段的值)、HGET
(获取单个字段的值)、HMGET
(获取多个字段的值)。
- 对象数据存储场景。
- 应用场景。
- 集合。Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺序但都唯一,有点类似于 Java 中的
HashSet
。当你需要存储一个列表数据,又不希望出现重复数据时,Set 是一个很好的选择,并且 Set 提供了判断某个元素是否在一个 Set 集合内的重要接口,这个也是 List 所不能提供的。你可以基于 Set 轻易实现交集、并集、差集的操作,比如你可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。这样的话,Set 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程。- 应用场景。
- 需要存放的数据不能重复的场景。
- 举例:网站 UV 统计(数据量巨大的场景还是
HyperLogLog
更适合一些)、文章点赞、动态点赞等场景。 - 相关命令:
SCARD
(获取集合数量) 。
- 举例:网站 UV 统计(数据量巨大的场景还是
- 需要获取多个数据源交集、并集和差集的场景。
- 举例:共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集)、订阅号推荐(差集+交集) 等场景。
- 相关命令:
SINTER
(交集)、SINTERSTORE
(交集)、SUNION
(并集)、SUNIONSTORE
(并集)、SDIFF
(差集)、SDIFFSTORE
(差集)。
- 需要存放的数据不能重复的场景。
- 应用场景。
- 有序集合。Sorted Set 类似于 Set,但和 Set 相比,Sorted Set 增加了一个权重参数
score
,使得集合中的元素能够按score
进行有序排列,还可以通过score
的范围来获取元素的列表。有点像是 Java 中HashMap
和TreeSet
的结合体。- 应用场景。
- 需要随机获取数据源中的元素根据某个权重进行排序的场景。
- 举例:各种排行榜比如直播间送礼物的排行榜、朋友圈的微信步数排行榜、王者荣耀中的段位排行榜、话题热度排行榜等等。
- 相关命令:
ZRANGE
(从小到大排序)、ZREVRANGE
(从大到小排序)、ZREVRANK
(指定元素排名)。
- 需要存储的数据有优先级或者重要程度的场景 比如优先级任务队列。
- 举例:优先级任务队列。
- 相关命令:
ZRANGE
(从小到大排序)、ZREVRANGE
(从大到小排序)、ZREVRANK
(指定元素排名)。
- 需要随机获取数据源中的元素根据某个权重进行排序的场景。
- 应用场景。
- BitMap(位图)。Bitmap 存储的是连续的二进制数字(0 和 1),通过 Bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 Bitmap 本身会极大的节省储存空间。你可以将 Bitmap 看作是一个存储二进制数字(0 和 1)的数组,数组中每个元素的下标叫做 offset(偏移量)。
- 应用场景。
- 需要保存状态信息(0/1 即可表示)的场景。
- 举例:用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。
- 相关命令:
SETBIT
、GETBIT
、BITCOUNT
、BITOP
。
- 需要保存状态信息(0/1 即可表示)的场景。
- 应用场景。
- HyperLogLog(基数统计)。HyperLogLog 是一种有名的基数计数概率算法 ,基于 LogLog Counting(LLC)优化改进得来,并不是 Redis 特有的,Redis 只是实现了这个算法并提供了一些开箱即用的 API。
- 应用场景。
- 数量量巨大(百万、千万级别以上)的计数场景。
- 举例:热门网站每日/每周/每月访问 ip 数统计、热门帖子 uv 统计。
- 相关命令:
PFADD
、PFCOUNT
。
- 数量量巨大(百万、千万级别以上)的计数场景。
- 应用场景。
- Geospatial (地理位置)。Geospatial index(地理空间索引,简称 GEO) 主要用于存储地理位置信息,基于 Sorted Set 实现。通过 GEO 我们可以轻松实现两个位置距离的计算、获取指定位置附近的元素等功能。
- 应用场景。
- 需要管理使用地理空间数据的场景。
- 举例:附近的人。
- 相关命令:
GEOADD
、GEORADIUS
、GEORADIUSBYMEMBER
。
- 需要管理使用地理空间数据的场景。
- 应用场景。
Redis分布式锁实现
分布式锁的作用:分布式锁可以管理 多个系统对资源的同步控制。
我们首先希望在同一时间只能有一个客户端持有锁:
- setnx(set if not exists)指令。
- 使用。setnx key value。
- 问题。在代码执行一半时,特殊原因导致程序崩溃而没有执行解锁操作,导致死锁。
- setnx ex/px指令。
- set key value nx px 3000。
- 问题。不能解决锁续期和锁重入。
- Lua脚本。可以解决锁重入,但是不能解决锁续期问题。
- Redisson框架。可以同时解决锁重入和锁续期问题,且实现简单。
缓存击穿
高并发时,一个key是热点数据,需要应对大量并发请求,这时key如果失效,在失效的瞬间会有大量请求跨国redis直接请求数据库,并将查询到的值回写到缓存,这会导致性能的大幅下降,这种现象就叫缓存击穿。
解决方案:
- 将过期时间设置为永不过期。
- 方案一。将可能的热门商品的key值设置成永不过期。
- 方案二。日均访问量达到某个阈值时将key设置成永不过期。
- 加锁排队。使用分布式锁或者双重检查锁对数据库请求代码进行加锁,使同时只会有一个线程对数据库放松请求。
缓存雪崩
缓存集中过期或缓存服务器宕机,导致大量请求访问数据库,造成数据库瞬间压力过大而造成宕机,这种现象称为缓存雪崩。
解决方案:
- (加锁排队)随机失效时间(针对缓存集中过期)。
- redis集群(针对缓存服务器宕机)。
缓存穿透
当key值在数据库和缓存中都不存在,导致每次请求都回去查询数据库。这种场景可能涉及到攻击者对不真实数据的大量攻击请求,可能会导致数据库压力过大从而宕机。
解决方案:
- 参数校验。
- 缓存空对象。
- 布隆过滤器。
布隆过滤器
布隆过滤器是一种能降低查询的时间复杂度又能减小存储的空间复杂度的一种数据结构。
- 优点
- 数据检索性能快。
- 空间占用小。
- 缺点
- 存在误判性。
- 适用场景。
- 大数据量。
- 能容忍一定误判性。
- 过滤流程。
- 后端应用到redis(布隆过滤器集成于redis中)中查找,判断是否存在key。
- 如果存在,则读取redis中的缓存数据。如果不存在,则拦截无效请求。
- 实现原理。布隆过滤器的底层实现是bit位数组。
- 除了非负整形数据之外的存储逻辑。计算hash值,通过hash值取模来获取存储的下标地址,将对应下标地址的位值置为1,因为存在hash冲突,所以存在误判现象。
- 降低误判的概率。可以通过这几种方式来降低误判发生的概率。
- 增大bitmap的存储长度。
- 多次hash取交集及再hash。
发表回复