荐 redis从小白到大白--2020迎战redis面试
目录:
1. 什么是redis?
- redis属于NoSQL数据库,它是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
2. 什么是可基于内存的?
- 像mysql数据库是存放在磁盘上的,需要读取数据时去磁盘上读取,我们知道电脑有磁盘-内存-cpu,磁盘-内存-cpu速度越来越快,可基于内存的就是指redis的数据可以存放在内存中。与mysql不同的是,只有当用户访问mysql中的数据时,mysql中的数据才会被读取到内存中,供用户访问。而redis可以直接将热数据存放在内存中。这就是redis的可基于内存的。
3. 什么是可持久化的?
- 持久化指的是能够被保存到硬盘上的过程。redis将热数据存储在内存中,那么万一服务器宕机,内存中的数据不就消失了吗?所以redis引入了可持久化的机制,就是redis会将内存中的热数据及时的以很小代价的存储到磁盘上,这样服务器宕机,内存中的数据消失,我们依然可以在磁盘上将数据读取到内存中。
4. 什么是热数据?
- 拿淘宝当例子,淘宝上的秒杀产品就是热数据,即被用户经常访问的数据就是热数据,redis中的热数据是加载到内存的。与热数据对应的就是冷数据,冷数据是并不经常被访问的数据。冷数据存放在磁盘中,只有当用户访问冷数据时,redis才会去磁盘中读取数据返回给用户。
5. 为什么redis要将热数据存储到内存中?
- 如果redis将用户经常访问的热数据存储在磁盘中,那么秒杀商品在开抢的一瞬间,大量的并发访问请求会连接到服务器上,此时有许多线程都去磁盘上进行读取查询操作,而且磁盘速度慢,秒杀就变成了时杀(一个小时才刷新出商品界面)。但是内存快啊,在内存越来越便宜的今天,一个内存条不够那就来十个。假如一个内存条能支撑10000的并发,那么十个可就不是只能支撑10万的并发了,因为资源调度的原因能支撑比10万多的多的并发量。总之,将热数据存放在内存,就是为了提高并发量和服务器的性能。
6. 什么是并发?并发与并行的关系?
- 并发:多个任务在同一个 CPU 核上,按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行。也就是说并发指服务器有处理多个任务的能力,拿秒杀系统来说,在1秒内有1万的用户提交了秒杀请求,服务器必须能把请求全部处理完。你抢不到的原因是你提交的请求(点击秒杀按钮就提交了一个请求),并不是服务器第一个处理的。服务器第一个处理的请求就是成功秒杀的用户。
- 并行:单位时间内,多个处理器或多核处理器同时处理多个任务,是真正意义上的“同时进行”。现在的服务器大多是多核处理器多个CPU,在处理秒杀请求时,又有并发又有并行。
7. redis 的持久化机制是什么?
- redis 提供两种持久化机制 RDB(默认) 和 AOF 机制:
- RDB:Redis DataBase,RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照保存的时间。
save 900 1 #刷新快照到硬盘中,必须满足两者要求才会触发,即900秒之后且900秒时间内至少1个关键词发生变化
save 300 10 # 必须是300秒之后且300秒时间内至少10个关键词发生变化
save 60 10000 # 必须是60秒之后且60秒时间内至少10000个关键词发生变化
- RDB持久化机制的优点:
1、只有一个文件 dump.rdb,方便持久化。
2、容灾性好,一个文件可以保存到安全的磁盘。
3、性能最大化,使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能。
4、数据集比较大的时候,比 AOF 的启动效率更高。 - RDB持久化机制的缺点:
1、数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。
- AOF(Append-only file)持久化:指将所有的命令行记录以 redis 命令请求协议的格式完全持久化存储为 aof 文件。也就是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis时会重新从持久化的日志中文件恢复数据。
- 当两种方式同时开启时,redis恢复数据会优先选择AOF恢复。
- AOF持久化机制的优点:
1、数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次写命令操作就记录到 aof 文件中一次。
2、通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。
3、AOF 机制的 rewrite 模式。当aof文件过大时rewrite模式会对命令进行合并重写。
- AOF持久化机制的缺点:
1、AOF 文件比 RDB 文件大,且恢复速度慢。
2、数据集较大的时候,比 rdb 启动效率低。
- RDB与AOF机制对比:
1、AOF文件比RDB更新频率高,优先使用AOF还原数据。
2、AOF比RDB更安全也更大。
3、RDB性能比AOF好。
4、如果两个都配置了则优先使用AOF做数据恢复。
8. 什么是缓存?
- 存放在内存中的数据就是缓存。缓存分为本地缓存和分布式缓存。
9. 什么是本地缓存?
- 本地缓存是指将客户机本地的物理内存划分出一部分空间,用来缓冲客户机回写到服务器的数据。wps的网盘机制就是这样的。我在wps网盘上编辑一个文档,我在文档中增加一个数字“1”,这时wps网盘会自动保存吗?不会。只有当我更改了足够多的内容时,wps网盘才会自动保存。wps网盘利用的就是这个机制。我增加数字“1”的操作是在我的电脑上的内存中操作的,如果这时候我关机了,增加的数字“1”不会保存到wps网盘上。只有当达到一定的数据量时,才会保存到wps网盘上。csdn写博客的这个界面用的也是本地缓存的机制哟。
10. 本地缓存的优点?
- 本地缓存的技术将客户机(你的电脑)回写的数据不再先写入服务器硬盘(wps网盘),而是将回写数据先写入本地回写缓存(你电脑上的内存),当缓存空间达到一定的阈值时(你写入的数据超过阈值时),再将数据回写到服务器。有了本地回写缓存功能之后,可大大降低服务器读写压力和网络负载。
11. 什么是分布式缓存?
- 缓存这种能够提升指令和数据读取速度的特性,随着本地计算机系统向分布式系统的扩展,在分布式计算领域中得到了广泛的应用,称为分布式缓存。
12. 分布式缓存的优点?
- 能够实现负载均衡,从而支撑超高并发。
13. 为什么用redis做缓存?而不用Java自带的map/guava做缓存?
- redis 可以用大量内存空间来做缓存;redis 的缓存可以持久化;redis 可以实现分布式的缓存;redis 可以处理每秒百万级的并发,是专业的缓存服务;redis 缓存有过期机制;redis 有丰富的 API;redis有专门的管理工具可以查看缓存数据等,总而言之:高性能、高并发。而使用Java自带的map或者guava实现的是本地缓存,本地缓存最主要的特点是轻量以及快速,生命周期随着JVM的销毁而结束,并且在多实例(实例即服务器)的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性。
14. 什么是分区?
- 分区是将你的数据分发到不同redis实例上的一个过程,每个redis实例只是你所有key的一个子集。(每一个数据文件对象:如一张图片、一个MP3文件都叫数据文件对象,都有自己特定的key)
15. 分区可以在程序的不同层次实现,有哪些?
- 客户端分区:就是在客户端就已经决定数据会被存储到哪个redis节点或者从哪个redis节点读取。大多数客户端已经实现了客户端分区。
- 代理分区:意味着客户端将请求发送给代理,然后代理决定去哪个节点写数据或者读数据。代理根据分区规则决定请求哪些Redis实例,然后根据Redis的响应结果返回给客户端。redis和memcached的一种代理实现就是Twemproxy
- 查询路由(Query routing) :的意思是客户端随机地请求任意一个redis实例,然后由Redis将请求转发给正确的Redis节点。Redis Cluster实现了一种混合形式的查询路由,但并不是直接将请求从一个redis节点转发到另一个redis节点,而是在客户端的帮助下直接redirected到正确的redis节点。
16. redis分区的目的或优点是什么?
- 分区可以让Redis管理更大的内存,Redis将可以使用所有机器的内存。如果没有分区,你最多只能使用一台机器的内存。
- 分区使Redis的计算能力通过简单地增加计算机得到成倍提升,Redis的网络带宽也会随着计算机和网卡的增加而成倍增长。
17. 分区的缺点是什么?
- 有些特性在分区的情况下将受到限制:
- 涉及多个key的操作通常不会被支持。例如你不能对两个集合求交集,因为他们可能被存储到不同的Redis实例(实际上这种情况也有办法,但是不能直接使用交集指令)。
- 同时操作多个key,则不能使用Redis事务。
- 分区使用的粒度是key,不能使用一个非常长的排序key存储一个数据集(The partitioning granularity is the key, so it is not possible to shard a dataset with a single huge key like a very big sorted set)。
- 当使用分区的时候,数据处理会非常复杂,例如为了备份你必须从不同的Redis实例和主机同时收集RDB / AOF文件。
- 分区时动态扩容或缩容可能非常复杂。Redis集群在运行时增加或者删除Redis节点,能做到最大程度对用户透明地数据再平衡,但其他一些客户端分区或者代理分区方法则不支持这种特性。然而,有一种预分片的技术也可以较好的解决这个问题。
18. 什么是分区规则?
- 有许多分区标准(规则)。假如我们有4个Redis实例R0, R1, R2, R3,有一批用户数据user:1, user:2, … ,那么有很多存储方案可以选择。从另一方面说,有很多different systems to map方案可以决定用户映射到哪个Redis实例。
- 一种最简单的方法就是范围分区,就是将不同范围的对象映射到不同Redis实例。比如说,用户ID从0到10000的都被存储到R0,用户ID从10001到20000被存储到R1,依此类推。
- 这是一种可行方案并且很多人已经在使用。但是这种方案也有缺点,你需要建一张表存储数据到redis实例的映射关系。这张表需要非常谨慎地维护并且需要为每一类对象建立映射关系,所以redis范围分区通常并不像你想象的那样运行,比另外一种分区方案效率要低很多。
- 另一种可选的范围分区方案是散列分区,这种方案要求更低,不需要key必须是object_name:的形式,如此简单:
-
- 使用散列函数 (如 crc32 )将键名称转换为一个数字。例:键foobar, 使用crc32(foobar)函数将产生散列值93024922。
-
- 对转换后的散列值进行取模,以产生一个0到3的数字,以便可以使这个key映射到4个Redis实例当中的一个。93024922 % 4 等于 2, 所以 foobar 会被存储到第2个Redis实例。 R2 注意: 对一个数字进行取模,在大多数编程语言中是使用运算符%
- 还有很多分区方法,上面只是给出了两个简单示例。有一种比较高级的散列分区方法叫一致性哈希,并且有一些客户端和代理(proxies)已经实现。
19. redis集群有几种?
- redis有三种集群模式:
- 主从模式(单master节点的叫主从模式,多matser节点的主从模式分区也叫客户端分区)
- Sentinel模式(单master节点的叫Sentinel哨兵模式,多master节点的Sentinel模式分区也叫代理分区)
- Cluster模式(官方的集群模式,至少三个master节点,也叫查询路由分区)。
19.1. 主从模式
- 主从模式:一主多从,主要用来进行读写分离,如下图。多主多从主要用来实现主节点负责所有的写请求,从节点负责所有的读请求,主节点将数据复制给从节点,主节点与从节点上的数据保持同步。主从结构一般用来支撑“读高并发”。当slave1宕机重启后,只需要再次从主节点同步数据就可再次恢复服务。当master宕机了,不影响slave的“读服务”,但此时不能提供“写服务”,master重启后需要使用RDB或者AOF机制进行数据恢复。master挂了以后,不会在slave节点中重新选一个master。因此主从结构必须配置redis的持久化。
- 主从模式工作机制:当slave启动后,主动向master发送SYNC命令。master接收到SYNC命令后在后台保存快照(RDB持久化)和缓存保存快照这段时间的命令,然后将保存的快照文件和缓存的命令发送给slave。slave接收到快照文件和命令后加载快照文件和缓存的执行命令。复制初始化后,master每次接收到的写命令都会同步发送给slave,保证主从数据一致性。
19.2. sentinel模式
- sentinel模式: sentinel模式是建立在主从模式的基础上,当master挂了以后,sentinel会在slave中选择一个做为master,当挂掉的master重新启动后,它将不再是master而是变成slave节点,并接收新的master的同步数据。sentinel设计为一个sentinel集群是为了保证sentinel的高可用,否则sentinel就没有意义了。而多个sentinel之间也会自动监控。
- sentinel模式工作机制:
1、每个sentinel以每秒钟一次的频率向它所知的master,slave以及其他sentinel实例发送一个 PING 命令 。
2、如果一个实例距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被sentinel标记为主观下线。
3、如果一个master被标记为主观下线,则正在监视这个master的所有sentinel要以每秒一次的频率确认master的确进入了主观下线状态。
4、当有足够数量的sentinel(大于等于配置文件指定的值)在指定的时间范围内确认master的确进入了主观下线状态, 则master会被标记为客观下线 。
5、在一般情况下, 每个sentinel会以每 10 秒一次的频率向它已知的所有master,slave发送 INFO 命令 。
6、当master被sentinel标记为客观下线时,sentinel向下线的master的所有slave发送 INFO 命令的频率会从 10 秒一次改为 1 秒一次 。
7、若没有足够数量的sentinel同意master已经下线,master的客观下线状态就会被移除。
8、若master重新向sentinel的 PING 命令返回有效回复,master的主观下线状态就会被移除。
19.3. cluster模式
- cluster模式:用于数据量大到一台redis服务器无法承受的时候,比如超高并发条件下。cluster模式的出现就是为了解决单机redis容量有限的问题,将redis的读写请求根据==hash slot(哈希槽)==的规则分配到多台redis节点,如下图中的master1,master2,master3。注意,此时三台master节点上存储的数据是不一样的,三台master节点的数据合起来才是所有的数据。使用cluster集群,只需要将redis配置文件中的cluster-enable配置打开即可。每个集群中至少需要三个主节点才能正常运行,新增节点非常方便。
- cluster模式采用无中心结构
- cluster模式实现的就是redis的分布式缓存。即数据是分散开存储的。
- 主节点宕机后,相应的从节点会成为新的主节点来保证服务。宕机的主节点重启后就成为了从节点。
- 所有的节点都是一主一从(也可以是一主多从),其中从不提供服务,仅作为备用。
- 不支持同时处理多个key(如MSET/MGET),因为redis需要把key(每个文件都有一个key)均匀分布在各个节点上,并发量很高的情况下同时创建key-value会降低性能并导致不可预测的行为。
- redis cluster使用数据分片(也叫hash slot哈希槽)而非一致性哈希(consistency hashing)来实现。
- 什么是hash slot(哈希槽)?
- redis cluster 采用虚拟哈希槽分区,假如客户端要存储一张图片a.png,所有的keys根据哈希函数映射到 0 ~ 16383 整数槽内,计算公式:slot = CRC16(key) & 16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。
- redis cluster过程详解:
1、通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽(哈希值)区间的数据,默认分配了16384 个槽位
2、每份数据分片会存储在多个互为主从的多节点上
3、数据写入先写主节点,再同步到从节点(支持配置为阻塞同步)
4、同一分片多个节点间的数据不保持一致性
5、读取数据时,当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点
6、扩容时时需要需要把旧节点的数据迁移一部分到新节点 - 在 redis cluster 架构下,每个 redis 要放开两个端口号,比如一个是 6379,另外一个就是 加1w 的端口号,比如 16379,16379 端口号是用来进行节点间通信的,也就是 cluster bus 的东西,cluster bus 的通信,用来进行故障检测、配置更新、故障转移授权。cluster bus 用了另外一种二进制的协议,gossip 协议,用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间。
- 节点间的内部通信机制
- 集群元数据的维护有两种方式:集中式、Gossip 协议。redis cluster 节点间采用 gossip 协议进行通信。
20. 什么是一致性hash算法?
- 一致性hash算法也是使用取模的方法,但是是对2的32次方取模。简单说,一致性hash算法将整个hash值空间组织成一个虚拟的闭环:
- 对master1、2、3进行hash得到hash,对2的32次方取模,标记在闭环的相应位置上,假设如上图所示的位置,此时对数据a进行hash,然后对2的32次方取模,标记在闭环上,如图所示,一致性hash要求从数据所在位置按顺时针走,第一个遇到的服务器就是要存储到的服务器,如图,数据a存储到master2上,数据b存储到master3上。
21. 一致性hash自动缓存迁移:
- 容错性:假如master3宕机了,则只有master2和master3之间的数据需要转存到master1上,对其他区域的数据没有影响。
- 可扩展性:如果在master1和master3之间增加一个master4,
- 则master3和master4之间的数据存储到master4上,其他的数据不变。以上是一致性hash算法的自动缓存迁移。
22. 一致性hash算法虚拟节点实现自动负载均衡:
- 当服务器过少时,如只有两台服务器,且位置分布不均匀,会导致绝大部分数据存储到master1节点上,导致数据倾斜。
- 这时有虚拟节点机制,将master1和master2节点虚拟成几个节点,如master虚拟成master1-1、1-2、1-3。
- 这就避免了数据倾斜,在实际应用中,通常把虚拟节点设置为32或更大。
- 一致性hash架构图:
23. redis有几种数据类型?以及应用场景?
- redis主要有五种数据类型。包括:
- String:可以存储字符串、整数、或者浮点数,String主要用来做简单的键值对缓存。
- List:主要用来存储一些列表型的数据结构,类似粉丝列表、文章的评论类型的数据。
- Set:可以存储无序集合,主要用来进行交集、并集、差集的操作。
- Zset:可以存储有序集合,主要用来去重和排序,如获取排名前几名的用户。
- Hash:存储包含键值对的无需散列表,主要应用于存储结构化的数据,如存储一个对象。
- redis的应用场景有哪些?
1、计数器:对String进行自增自减运算,String适合最简单的k-v存储。
2、缓存:将热点数据放入缓存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率。
3、会话缓存:用户与应用服务器的会话信息。
4、全页缓存
5、查找表
6、消息队列
7、分布式锁实现
8、共同好友功能、排行榜功能等
24. redis的内存淘汰策略是什么?有哪些?
- 淘汰策略:作为一个内存数据库,redis在内存空间不足的时候,为了保证命中率,就会选择一定的数据淘汰策略;淘汰策略有以下几种:
1、volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰;
2、volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰;
3、volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰;
4、allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰;
5、allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰;
6、noeviction:禁止驱逐数据,当内存使用达到阈值的时候,所有引起申请内存的命令会报错。
25. 什么是分布式锁?分布式锁的要求?redis如何实现分布式锁?
- 分布式锁:在传统单体应用单机部署的情况下,可以使用Java并发处理相关的API(如ReentrantLcok或synchronized)进行互斥控制。但是在分布式系统中,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,分布式锁就是用来解决这一问题的。
- 分布式锁的要求:
1、互斥性:任意时刻,只能有一个客户端获取锁,不能同时有两个客户端获取到锁。
2、安全性:锁只能被持有该锁的客户端删除,不能由其它客户端删除。
3、死锁:获取锁的客户端因为某些原因(如down机等)而未能释放锁,其它客户端再也无法获取到该锁。
4、容错:当部分节点(redis节点等)down机时,客户端仍然能够获取锁和释放锁。 - redis如何实现分布式锁(注意这是单redis实例实现分布式锁的方式):基于Redis命令:
SET key value NX EX max-lock-time
- 从2.6.12版本后, 就可以使用set来获取锁, Lua 脚本来释放锁。
26. 在多实例redis上怎么实现分布式锁?
- 使用RedLock分布式锁。(有效防止单点故障)
27. RedLock分布式锁的原理?
1、假设有5个完全独立的redis主服务器;
2、获取当前时间戳;
3、client尝试按照顺序使用相同的key,value获取所有redis服务的锁,在获取锁的过程中的获取时间比锁过期时间短很多,这是为了不要过长时间等待已经关闭的redis服务。并且试着获取下一个redis实例。比如:TTL为5s,设置获取锁最多用1s,所以如果一秒内无法获取锁,就放弃获取这个锁,从而尝试获取下个锁;
4、client通过获取所有能获取的锁后的时间减去第一步的时间,这个时间差要小于TTL时间并且至少有3个redis实例成功获取锁,才算真正的获取锁成功;
5、如果成功获取锁,则锁的真正有效时间是 TTL减去第三步的时间差 的时间;比如:TTL 是5s,获取所有锁用了2s,则真正锁有效时间为3s(其实应该再减去时钟漂移);
6、如果客户端由于某些原因获取锁失败,便会开始解锁所有redis实例;因为可能已经获取了小于3个锁,必须释放,否则影响其他client获取锁;
- 原理图:
28. RedLock算法是否是异步算法?
- 可以看成是同步算法;因为即使进程间(多个电脑间)没有同步时钟,但是每个进程时间流速大致相同;并且时钟漂移相对于TTL叫小,可以忽略,所以可以看成同步算法;(不够严谨,算法上要算上时钟漂移,因为如果两个电脑在地球两端,则时钟漂移非常大)
29. RedLock失败重试机制:
- 当client不能获取锁时,应该在随机时间后重试获取锁;并且最好在同一时刻并发的把set命令发送给所有redis实例;而且对于已经获取锁的client在完成任务后要及时释放锁,这是为了节省时间;
30. 为什么要使用RedLock?
- 使用RedLock实现的分布式锁,可以保证以下特性:
1、安全特性:互斥访问,即永远只有一个 client 能拿到锁;
2、避免死锁:最终 client 都可能拿到锁,不会出现死锁的情况,即使原本锁住某资源的 client crash 了或者出现了网络分区;
3、容错性:只要大部分 Redis 节点存活就可以正常提供服务;
31. redis做缓存和持久化存储时如何进行扩容操作?
- 如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容(对于主从模式和sentinel模式来说可以使用一致性哈希实现动态扩容缩容,cluster使用hash槽的方式进行动态扩容缩容)。
- 如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样。(即只有cluster模式可以做到)
32. redis的key的过期时间和永久有效怎么设置?
- 过期时间设置:EXPIRE key_name seconds
- 永久有效设置:PERSIST key_name
33. 如何保证redis中的数据都是热点数据?
- Mysql中有2000万条数据、redis只存20万条热点数据,如何保证redis中的数据都是热点数据?
- 数据淘汰策略(前边有介绍)
34. redis如何做内存优化?
- 1、缩减键值对象:满足业务要求下 key 越短越好;value 值进行适当压缩;
2、共享对象池:即 Redis 内部维护[0-9999]的整数对象池,开发中在满足需求的前提下,尽量使用整数对象以节省内存;
3、尽可能使用散列表(hashes);
4、编码优化,控制编码类型;
5、控制 key 的数量。
35. 什么是redis的事务?
- Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
- 总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
- redis事务没有隔离级别的概念:
- 批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行,也就不存在事务内的查询要看到事务里的更新,事务外查询不能看到。
- redis不保证原子性:
- redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
36. redis事务的三个阶段:
- 1、开始事务:MULTI
2、命令入队
3、执行事务:EXEC
37. redis事务相关命令:
- 1、watch key1 key2 … : 监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 )
2、multi : 标记一个事务块的开始( queued )
3、exec : 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 )
4、discard : 取消事务,放弃事务块中的所有命令
5、unwatch : 取消watch对所有key的监控
- redis不支持回滚:
1、如果在一个事务中的命令出现错误,那么所有的命令都不会执行;
2、如果在一个事务中出现运行错误,那么正确的命令会被执行。
38. 分布式寻址算法有哪些?
- 1、hash算法(给服务器编号,计算出hash值对服务器数量取模,得到的即为要存储的服务器的编号)
2、一致性hash算法+虚拟节点
3、hash slot算法
39. redis主从复制的核心机制是什么?
- 1、redis 采用异步方式复制数据到 slave 节点,不过 redis2.8 开始,slave node 会周期性地确认自己每次复制的数据量;
2、一个 master node 是可以配置多个 slave node 的;
3、slave node 也可以连接其他的 slave node;
4、slave node 做复制的时候,不会 block master node 的正常工作;
5、slave node 在做复制的时候,也不会 block 对自己的查询操作,它会用旧的数据集来提供服务;但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了;
6、slave node 主要用来进行横向扩容,做读写分离,扩容的 slave node 可以提高读的吞吐量。
40. 什么是异步复制?
- 主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理。
41. redis集群的最大节点数?
- 16384个,因为redis cluster的分区机制是hash槽,一个redis主节点拥有一个hash槽,所以redis集群最多有16384个。
42. redis集群如何选择数据库?
- 无法选择数据库,默认在0号数据库。
43. redis是单线程的,如何提高多核CPU的利用率?
- 分区(也叫分片)或一台服务器部署多个redis实例。
44. redis分区方法有哪些?
- 1、客户端分区
2、代理分区
3、查询路由
45. 如何解决redis的并发竞争key的问题?
- 1、分布式锁(redis或者zookeeper都可以实现分布式锁)
分布式锁的实现方式:DB锁;memcached(add命令);redis(新版本set命令,setnx命令是老版本的);zookeeper(临时节点)
2、利用消息队列(或者说利用中间件)
在并发量过大的情况下,可以通过消息中间件进行处理,把并行读写进行串行化。
3、这里说明一下zookeeper使用临时有序节点来实现分布式锁的机制:
每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的临时有序节点。判断是否获取锁的方式很简单,只需判断临时有序节点中序号最小的一个。当释放锁的时候,只需将这个临时有序节点删除即可。举例说明:假如client1请求获取锁,这时zookeeper会创建一个临时有序节点1,并获得锁。client2这时来请求锁,zookeeper会创建临时有序节点2,2进入等待的同时会监听1,当client1释放锁,临时有序节点1就被删除,因为被删除了,2就是所有临时有序节点中最小的,所以client2获得锁。
46. 什么是缓存雪崩?
- 缓存雪崩是指,缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
- 缓存雪崩的解决方案有哪些?
1、缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
2、如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
3、设置热点数据永远不过期。
47. 什么是缓存穿透?
- 缓存穿透的概念很简单,用户想要查询一个数据,发现redis缓存没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。
- 缓存穿透的解决方案有哪些?
1、布隆过滤器,使用多个hash方法来做筛选,将用户请求的redis缓存中不存在的数据过滤掉。
48. 什么是布隆过滤器?
- 布隆过滤器实际上是一个很长的二进制向量和一系列随机映射函数。当我们存储a.png时,使用布隆过滤器就是使用多个hash方法来做筛选,将用户请求的redis缓存中不存在的数据过滤掉。如下图所示,将三个hash方法映射到的位置置位1,此时我们的缓存中只有一个a.png。
- 这时用户请求访问b.png,布隆过滤器给b.png做三次hash映射,假如映射到如下图的位置:
- 如上图中的,当用户请求a.png时,hash方法映射到的位置全部为1,布隆过滤器才会返回a.png有可能存在,注意是有可能存在。当用户请求b.png时,只要有一个映射位置不为1,那么布隆过滤器都会返回b.png不存在。
- 为什么映射的位置全部为1,是有可能存在?
- 如下图所示,此时假设a.png和c.png是存储在redis中的,且a和c的映射位置都置为1,此时用户请求b.png(b.png没有在redis中),布隆过滤器做hash映射发现,b的映射位置也都是1,其实b.png不在redis中。所以说映射位置都是1的数据可能存在于redis。
- 布隆过滤器主要是过滤掉那些一定不存在的数据。
49. 什么是缓存击穿?
- 缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
- 缓存击穿解决方案?
1、设置热点数据永远不过期。
2、加互斥锁,互斥锁
50. 发生缓存失效后的补救办法?
- 限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
51. 什么是数据预热?
- 数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
52. 如何保证redis缓存与数据库双写时的一致性?
- 什么是双写:redis做缓存,数据库做持久化存储。那么用户请求写操作(如修改一个文件),缓存中的数据会先修改,再更新数据库中的数据。这就叫双写。而缓存先更新数据库后更新就会导致缓存和数据库出现数据不一致的问题。
- 解决方案:(以下效率很低,适用于强一致性(强一致性:必须保证缓存和数据库的一致性)的部署)
1、使用中间件使操作串行化;
2、使用redis做持久化并采用aof机制(aof机制博客上边讲了)
53. 使用什么指令可以查找redis中的key?
- 假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?
- 使用keys指令可以扫出指定模式的key列表。
- 如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?
- redis的单线程的,所以keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。
54. 如何使用Redis做异步队列?
- 1、使用List作为队列,RPUSH生产消息,LPOP消费消息
缺点:在生产者/消费者模式里,当LPOP没有消息的时候即证明消息暂时被消费完毕,并且生产者还没有来得及生成数据,LPOP没有等待队列里有值就直接消费。
弥补:可以通过在应用层引入Sleep机制去调用LPOP重试,进而实现简单的异步队列。 - 2、BLPOP在没有消息的时候阻塞直到消息到来或者超时
我们在一个消费者客户端中输入blpop testlist 30,意思是在30秒内会一直等待testlist的消息,如果超过30秒没有数据则返回null。 同时在另一个客户端即生产者执行rpush testlist aaa去放入数据,回车执行后会发现,原来的消费者客户端中blpop指令就能获取并返回testlist aaa数据,也就证明了BLPOP能替代队列做更精准的阻塞控制。
缺点:只能供一个消费者消费 - 3、pub/sub主题订阅者模式
发送者pub发送消息,订阅者sub接收消息,订阅者可以订阅任意数量的频道(Topic即消费者订阅的主题)。
缺点:消息的发布是无状态的,无法保证可到达
55. 生产者消费者模式中,客户机和服务器谁是生产者,谁是消费者?
- 客户机一般是产生数据的,所以是生产者;服务器是处理客户机产生的数据的,所以是消费者。
56. redis内存回收机制是什么?
-
1、删除到达时间的键对象。
-
2、内存使用达到maxmemory上限时触发内存溢出控制策略。
-
1、删除过期键对象
redis所有的键都可以设置过期属性,内部保存在过期字典中。由于进程内保存了大量的键,维护每个键精准的过期删除机制会导致消耗大量的CPU,对于单线程的redis来说成本过高,因此redis采用惰性删除和定时任务删除机制实现过期键的内存回收。 -
惰性删除:惰性删除用于当客户端读取带有超时属性的键时,如果已经超过键设置的过期时间,会执行删除操作并返回空,这种策略是出于节省CPU成本考虑,不需要单独维护TTL链表来处理过期键的删除。但是单独用这种方式存在内存泄露的问题,当过期键一直没有访问将无法得到及时删除,从而导致内存不能及时释放。正因为如此,redis还提供另一种定时任务删除机制作为惰性删除的补充。
-
定时任务删除:redis内部维护一个定时任务,默认每秒运行10次(通过配置hz控制)。定时任务中删除过期键逻辑采用了自适应算法,根据键的过期比例,使用快慢两种速率模式回收键。
-
2、内存溢出控制策略
当redis所用内存达到maxmemory上限时会触发相应的溢出控制策略。具体策略受maxmemory-policy参数控制,Redis支持6种策略,如下所示:
1、volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰;
2、volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰;
3、volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰;
4、allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰;
5、allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰;
6、noeviction:禁止驱逐数据,当内存使用达到阈值的时候,所有引起申请内存的命令会报错。
volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 Key 优先移除。不推荐。如果没有对应的键,则回退到noeviction策略。