Redis支持的数据类型
Redis 提供了多种数据类型来支持不同的业务场景,比如 String(字符串)、Hash(哈希)、 List (列表)、Set(集合)、Zset(有序集合)、Bitmaps(位图)、HyperLogLog(基数统计)、GEO(地理信息)、Stream(流)。
-
String 类型的应用场景:缓存对象、常规计数、分布式锁、共享 session 信息等。
-
List 类型的应用场景:消息队列(但是有两个问题:1. 生产者需要自行实现全局唯一 ID;2. 不能以消费组形式消费数据)等。
-
Hash 类型:缓存对象、购物车等。
-
Set 类型:聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。
-
Zset 类型:排序场景,比如排行榜、电话和姓名排序等。
-
BitMap(2.2 版新增):二值状态统计的场景,比如签到、判断用户登陆状态、连续签到用户总数等;
-
HyperLogLog(2.8 版新增):海量数据基数统计的场景,比如百万级网页 UV 计数等;
-
GEO(3.2 版新增):存储地理位置信息的场景,比如滴滴叫车;
-
Stream(5.0 版新增):消息队列,相比于基于 List 类型实现的消息队列,有这两个特有的特性:自动生成全局唯一消息ID,支持以消费组形式消费数据。
详细使用,todo
Redis 是单线程吗?
命令执行是单线程的,但会启动后台线程执行关闭文件、AOF 刷盘、释放内存这三个耗时长的任务,分别有不同的任务队列。
Redis 采用单线程为什么还这么快?
单线程的 Redis 吞吐量可以达到 10W/每秒
原因为:Redis 的大部分操作都在内存中完成、Redis 采用单线程模型可以避免了多线程之间的竞争、Redis 采用了 I/O 多路复用机制处理大量的客户端 Socket 请求(6.0版本这个部分也用多线程处理)
Redis 如何实现数据不丢失?
Redis 共有三种数据持久化的方式:
-
AOF 日志:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;
-
RDB 快照:将某一时刻的内存数据,以二进制的方式写入磁盘;
-
混合持久化方式:Redis 4.0 新增的方式,集成了 AOF 和 RBD 的优点;
Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave,他们的区别就在于是否在「主线程」里执行。
AOF 优点是丢失数据少,但是数据恢复不快。RDB 优点是数据恢复速度快,但是快照的频率不好把握
Redis 和 Memcached 有什么区别?
-
Redis 支持的数据类型更丰富(String、Hash、List、Set、ZSet),而 Memcached 只支持最简单的 key-value 数据类型;
-
Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memcached 没有持久化功能,数据全部存在内存之中,Memcached 重启或者挂掉后,数据就没了;
-
Redis 原生支持集群模式,Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;
-
Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持;
Redis 如何实现服务高可用?
主从复制(读写分离)、哨兵模式(哨兵节点选择可用的,防止主服务器挂掉问题)、切片集群(数据量大到一个服务器无法缓存时,切片存储)。
集群脑裂导致数据丢失怎么办?
这时,哨兵也发现主节点失联了,它就认为主节点挂了(但实际上主节点正常运行,只是网络出问题了),于是哨兵就会在「从节点」中选举出一个 leader 作为主节点,这时集群就有两个主节点了 —— 脑裂出现了。
解决方案:当主节点发现从节点下线或者通信超时的总数量小于阈值时,那么禁止主节点进行写数据,直接把错误返回给客户端。
Redis 使用的过期删除策略是什么?
Redis 使用的过期删除策略是「惰性删除+定期删除」这两种策略配和使用。
惰性删除策略的做法是,不主动删除过期键,每次从数据库访问 key 时,都检测 key 是否过期,如果过期则删除该 key。
定期删除策略的做法是,每隔一段时间「随机」从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。
Redis 内存满了,会发生什么?
在 Redis 的运行内存达到了某个阀值,就会触发内存淘汰机制,这个阀值就是我们设置的最大运行内存,此值在 Redis 的配置文件中可以找到,配置项为 maxmemory。
Redis 内存淘汰策略共有八种,这八种策略大体分为「不进行数据淘汰」和「进行数据淘汰」两类策略。
什么是缓存雪崩、击穿、穿透?
1、缓存雪崩:大量缓存数据在同一时间过期(失效)
方案:将缓存失效时间随机打散、设置缓存不过期
2、缓存击穿
缓存中的某个热点数据过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮,这就是缓存击穿的问题
方案:互斥锁方案、不给热点数据设置过期时间
3、缓存穿透
当用户访问的数据,既不在缓存中,也不在数据库中,那么当有大量这样的请求到来时,数据库的压力骤增,这就是缓存穿透的问题。
方案:非法请求的限制、设置空值或者默认值、使用布隆过滤器快速判断数据是否存在
问题?
在 Redis 中可以用 zadd 方法和 zrange 方法来完成排序队列和获取 200 个商品的操作。
说说常见的缓存更新策略?
实际开发中,Redis 和 MySQL 的更新策略用的是 Cache Aside (旁路缓存)
写策略的步骤:
-
先更新数据库中的数据,再删除缓存中的数据。
读策略的步骤:
-
如果读取的数据命中了缓存,则直接返回数据;
-
如果读取的数据没有命中缓存,则从数据库中读取数据,然后将数据写入到缓存,并且返回给用户。
Redis 实战
Redis 如何实现延迟队列?
在 Redis 可以使用有序集合(ZSet)的方式来实现延迟消息队列的,ZSet 有一个 Score 属性可以用来存储延迟执行的时间。
使用 zadd score1 value1 命令就可以一直往内存中生产消息。再利用 zrangebysocre 查询符合条件的所有待处理的任务, 通过循环执行队列任务即可。
Redis 的大 key 如何处理?
一般而言,下面这两种情况被称为大 key:
-
String 类型的值大于 10 KB;
-
Hash、List、Set、ZSet 类型的元素的个数超过 5000个;
存在问题?
客户端超时阻塞、引发网络阻塞、阻塞工作线程(del阻塞工作线程)、内存分布不均
如何找到大 key ?
1、可以通过 redis-cli --bigkeys 命令查找大 key
最好选择在从节点上执行该命令或者业务压力的低峰阶段进行扫描查询
2、使用 SCAN 命令查找大 key
3、使用 RdbTools 工具查找大 key
使用 RdbTools 第三方开源工具,可以用来解析 Redis 快照(RDB)文件,找到其中的大 key。
如何删除大 key?
-
分批次删除
-
异步删除(Redis 4.0版本以上)
从 Redis 4.0 版本开始,可以采用异步删除法,用 unlink 命令代替 del 来删除。
Redis 管道有什么用?
使用管道技术可以解决多个命令执行时的网络等待,它是把多个命令整合到一起发送给服务器端处理之后统一返回给客户端。要注意的是,管道技术本质上是客户端提供的功能,而非 Redis 服务器端的功能。
Redis 事务支持回滚吗?
Redis 中并没有提供回滚机制,虽然 Redis 提供了 DISCARD 命令,但是这个命令只能用来主动放弃事务执行,把暂存的命令队列清空,起不到回滚的效果。从作者的角度来看:不支持事务回滚是因为这种复杂的功能和 Redis 追求的简单高效的设计主旨不符合
如何用 Redis 实现分布式锁的?
Redis 的 SET 命令有个 NX 参数可以实现「key不存在才插入」,所以可以用它来实现分布式锁:
-
如果 key 不存在,则显示插入成功,可以用来表示加锁成功;
-
如果 key 存在,则会显示插入失败,可以用来表示加锁失败。
基于 Redis 节点实现分布式锁时,对于加锁操作,我们需要满足三个条件。
-
加锁包括了读取锁变量、检查锁变量值和设置锁变量值三个操作,但需要以原子操作的方式完成,所以,我们使用 SET 命令带上 NX 选项来实现加锁;
-
锁变量需要设置过期时间,以免客户端拿到锁后发生异常,导致锁一直无法释放,所以,我们在 SET 命令执行时加上 EX/PX 选项,设置其过期时间;
-
锁变量的值需要能区分来自不同客户端的加锁操作,以免在释放锁时,出现误释放操作,所以,我们使用 SET 命令设置锁变量值时,每个客户端设置的值是一个唯一值,用于标识客户端;
满足这三个条件的分布式命令如下:
SET lock_key unique_value NX PX 10000
-
lock_key 就是 key 键;
-
unique_value 是客户端生成的唯一的标识,区分来自不同客户端的锁操作;
-
NX 代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;
-
PX 10000 表示设置 lock_key 的过期时间为 10s,这是为了避免客户端发生异常而无法释放锁。
而解锁的过程就是将 lock_key 键删除(del lock_key),但不能乱删,要保证执行操作的客户端就是加锁的客户端。所以,解锁的时候,我们要先判断锁的 unique_value 是否为加锁客户端,是的话,才将 lock_key 键删除。
// 释放锁时,先比较 unique_value 是否相等,避免锁的误释放
// 释放锁时,先比较 unique_value 是否相等,避免锁的误释放
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
基于 Redis 实现分布式锁有什么优缺点?
-
性能高效(这是选择缓存实现分布式锁最核心的出发点)。
-
实现方便。很多研发工程师选择使用 Redis 来实现分布式锁,很大成分上是因为 Redis 提供了 setnx 方法,实现分布式锁很方便。
-
避免单点故障(因为 Redis 是跨集群部署的,自然就避免了单点故障)。
基于 Redis 实现分布式锁的缺点:
- 超时时间不好设置。如果锁的超时时间设置过长,会影响性能,如果设置的超时时间过短会保护不到共享资源
- Redis 主从复制模式中的数据是异步复制的,这样导致分布式锁的不可靠性
Redis 如何解决集群情况下分布式锁的可靠性?
为了保证集群环境下分布式锁的可靠性,Redis 官方已经设计了一个分布式锁算法 Redlock(红锁)。
Redlock 算法的基本思路,是让客户端和多个独立的 Redis 节点依次请求申请加锁,如果客户端能够和半数以上的节点成功地完成加锁操作,那么我们就认为,客户端成功地获得分布式锁,否则加锁失败。