一. 为什么要用缓存?
使用缓存的目的就是提升读写性能。在实际的业务场景下,更多的是为了提升读性能,带来更好的性能和并发量。Redis 的读写性能比 MySQL 好的多,我们就可以把 MySQL 中的热点数据缓存到 Redis ,提升读取性能,同时减轻了 MySQL 的读取压力。
二. 什么是 Redis?
Redis 是一个高性能的内存数据存储系统,也可以称为键值存储系统。它支持多种数据结构,包括字符串、哈希、列表、集合、有序集合等,还提供了一些高级功能,如发布订阅、事务、Lua 脚本等。Redis 的特点是数据存储在内存中,可以快速读写,同时支持数据持久化到磁盘中。Redis 还具有分布式特性,可以通过分片和赋值来实现高可用和高扩展性。
Redis 主要应用于缓存、会话存储、消息队列、排行榜等场景,具有快速、稳定、可靠等优点。由于其出色的性能和易用性,Redis 已经成为最受欢迎的内存数据库之一。
三. 使用 Redis 有哪些好处?
使用 Redis 有以下几个好处
高性能:Redis 将数据存储在内存中,读写速度非常快,可以达到几十万甚至上百万 QPS,特别适合高并发场景。
数据结构丰富:Redis 支持多种数据结构,如字符串、哈希、列表、集合、有序集合等,可以满足不同场景下的需求。
持久化:Redis 支持将数据持久化到磁盘中,以保证数据的安全性和可恢复性。
分布式特性:Redis 支持分片和复制,可以实现高可用和高扩展性,支持数据在多台服务器之间的共享。
丰富的功能:Redis 提供了许多高级功能,如事务、Lua 脚本、发布订阅、过期策略等,可以满足更加复杂的业务需求。
四. 为什么要是用 Redis 而不是用 Memcached 呢?
Redis 和 Memcached 都是流行的内存缓存系统,它们都可以在内存中快速读写数据,但是在一些方面有所不同,下面是 Redis 相较于 Memcached 的一些优点
数据结构更丰富:Redis 支持多种数据结构,例如字符串、哈希、列表、集合、有序集合等,这些数据结构可以直接映射到实际的数据模型中,方便业务开发和数据处理。
多种持久化方式:Redis 支持多种持久化方式,包括哦 RDB(快照)和AOF(日志),这些持久化方式可以保证数据的安全性和可恢复性。
多种复制方式:Redis 支持主从复制和哨兵模式,可以实现高可用和自动故障转移,而 Memcached 则需要通过第三方工具来实现高可用。
更好的性能:Redis 在读写性能和并发能力上相较于 Memcached 更好,尤其是在多核 CPU 环境下,Redis 可以充分利用多核的优势,提高系统的吞吐量。
更丰富的功能:Redis 提供了更丰富的功能,如事务、Lua 脚本、发布订阅、过期策略等,可以满足更加负载的业务需求。
五. 说说 Redis 线程模型
Redis 采用单线程模型,也就是说所有的请求都由同一个线程来处理。这个线程主要负责网络 IO、请求解析、命令执行和数据返回等业务。Redis 内部通过事件驱动机制来实现异步 IO 操作,包括文件事件和时间事件。具体来说,Redis 在启动时会创建一个事件处理器,来监听客户端套接字的读写事件,并在事件发生时触发响应的回调函数来处理事件。
Redis 单线程模型的优点是代码简洁、易于维护和调试,同时可以避免多线程并发带来的同步和锁的问题。此外,Redis 还采用了多路复用机制,可以将多个客户端的请求合并到一起,减少 IO 操作的次数,提高系统的吞吐量和响应速度。
当然,Redis 的单线程模型也存在一些缺点,如无法充分利用多核 CPU 的优势,容易受到单点故障的影响等。为了解决这些问题,Redis 引入了多个进程和多个实例的方案,如主从复制、哨兵模式和集群模式等。这些方案可以提高系统的可用性和扩展性,同时保持了 Redis 简洁、高效的特点。
六. 为什么 Redis 单线程模型效率也能那么高?
C语言实现,效率高
纯内存操作
基于非阻塞的 IO 复用模型机制
单线程的话可以避免多线程的频繁上下文切换问题
丰富的数据结构,全程采用哈希结构,读取速度非常快,对数据存储进行了一些优化,例如压缩表、跳表等。
七. 为什么 Redis 需要把所有数据放到内存中?
Redis 之所以将所有数据都放在内存中,是因为它设计的目标是高性能、高吞吐量和低延迟,而内存访问的速度比磁盘访问的速度快很多。如果数据存储在硬盘中,磁盘 I/O 会严重影响 Redis 的性能。而且 Redis 还提供了数据持久化功能,不用担心服务器重启对内存中数据的影响。
八. Redis 的同步机制了解吗?
Redis 支持主从同步和从从同步,而在进行第一次主从同步时,需要现在主节点上执行 BGSAVE 命令,将当前内存中的数据持久化道磁盘上生成 RDB 文件,并且将主节点需要将后续修改操作记录到内存缓冲区中。在这个过程中,主节点会将生成的 RDB 文件发送给从节点,从节点接收并加载 RDB 文件到自己的内存中。加载完成后,从节点会通知主节点,将主节点在复制期间产生的命令同步到从节点,以此完成主从同步过程。
九. Pipeline 有什么好处,为什么要是用 Pipeline?
使用 Pipeline 的好处在于可以将多次 I/O 往返的时间缩短为一次,从而提高Redis 的吞吐量和性能。Pipeline 允许客户端将多个 Redis 命令打包成一次请求发送给 Redis 服务器,Redis 服务器收到后,将多个命令按顺序执行,并将执行结果按照请求的顺序返回给客户端,这样就避免了每次请求都要进行网络通信的开销。
十. 说一下 Redis 有什么优点和缺点?
优点:
高性能:Redis 使用C语言编写,采用单线程模型,将数据全部存储在内存中,加上异步 I/O 和时间驱动机制等优化,使得 Redis 在读写数据时的性能非常高。
数据结构丰富:Redis 支持多种数据结构,如字符串、列表、哈希表、集合、有序集合等,这些数据结构可以满足不同的业务需求。
持久化机制:Redis 提供了两张持久化机制,即 RDB 和 AOF,可以将内存中的数据持久化到磁盘上,保证了数据的可靠性和安全性。
高可用性:Redis 提供了主从复制和 Sentinel 机制,可以实现数据的高可用性和容错能力。
缺点:
内存受限:Redis 将所有数据存储在内存中,如果数据量很大,会受到内存大小的限制,不适合存储大规模数据。
持久化机制可能带来性能损失:由于 Redis 提供了持久化机制,数据需要同步到磁盘上,真会导致写入性能的下降。
单线程模型可能存在瓶颈:尽管 Redis 采用了单线程模型,但是在极端情况下,可能会出现性能瓶颈,影响系统性能。
不支持多机数据共享:Redis 不支持多机数据共享,需要使用其他技术如主从复制和 Sentinel 机制来实现高可用性和容错能力。
十一. Redis 缓存刷新策略有哪些?
Redis 提供了以下几种缓存刷新策略
基于过期时间:可以设置 key 的过期时间,当过期时间到达后,Redis 会自动删除该 key。
基于 LRU 算法:Redis 使用 LRU 算法来淘汰最近最少使用的 key,以保留热点数据。
基于 LFU 算法:Redis 使用 LFU 算法来淘汰最不经常使用的 key,以保留热点数据。
基于手动刷新:可以手动删除缓存中的 key,或者通过发送通知来通知客户端删除 key。
基于定时刷新:可以定时清空缓存,或者定时刷新缓存中的数据,以保持数据的及时性。
十二. Redis 持久化方式有哪些?有什么区别?
Redis 提供两种持久化机制:RDB 和 AOF。
RDB (Redis DataBase)持久化:会将 Redis 在内存中的数据快照保存到磁盘上,形成一个 RDB 文件,该文件包含了 Redis 在某个时间点上的数据快照
优点:
只有一个 dump.rdb 文件,方便持久化
容灾性好,一个文件可以保存到安全的磁盘。
性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,I/O最大化
数据集较大时,比 AOF 的启动效率更高。
缺点:
数据安全性较低,RDB 是间隔一段时间进行持久化,如果持久化之间Redis 发生故障,会发生数据丢失,因此这种方式更适合数据要求不严谨的时候。
AOF (Append Only File)持久化:是将 Redis 写操作记录到一个文件中,每次Redis 执行一条写命令,就将该命令写入 AOF 文件中,这样可以保证每条命令都能被保存下来。AOF 文件可以进行追加和重写操作,当文件太大时,Redis 会自动进行重写,将多次修改合并成一条,以减少磁盘占用空间。
优点:
数据安全:AOF 持久化可以配置
appendfsync
属性,它可以指定 AOF文件的刷盘策略。默认情况下appendfsync
的值为everysec
。即每秒中将 AOF 缓存中的数据写入磁盘一次。但是,用户也可以将appendfsync
的值设置为always
,这样每次执行写操作都会立即将AOF 缓存中的数据写入磁盘。这样即使 Redis 发生异常情况。只要AOF 文件中已经记录了相应的写操作,就可以通过 AOF 文件来恢复数据。数据一致性:AOF 持久化是通过 append 模式写入文件的,即每次写操作都是追加到 AOF 文件末尾。因此,即使 Redis 在写入 AOF 文件的过程中宕机,AOF 文件也不会损坏,而是只会丢失一部分的数据。当 Redis 重新启动是,会通过 redis-check-aof 工具将 AOF 文件中不一致的数据进行修复,保证数据的一致性。需要注意的是,使用 AOF持久化时,如果 Redis 频繁执行写操作,那么 AOF 文件可能会非常大,可能影响性能。因此,用户可以通过配置 AOF 重写规则,定期对AOF 文件进行压缩,以减小文件大小。
缺点:
AOF 文件比 RDB 文件大,且恢复速度慢。
数据集较大时,比 RDB 启动效率低。
Redis 支持同时使用 RDB 和 AOF 持久化机制。在使用时,Redis 会先尝试使用AOF 文件来恢复数据,如果 AOF 文件不存在或者恢复失败,Redis 会尝试使用RDB 文件来恢复数据。同时使用两种持久化机制可以在保证数据完整性的同时提高恢复速度。
十三. 持久化有两种,那么该怎么选择呢?
不要仅仅使用 RDB,因为那样会导致丢失很多数据。虽然 RDB 持久化机制的忒但是可以生成数据的快照,这样在恢复数据的时候非常快速。但是 RDB 持久化只在发生故障时执行,如果 Redis 崩溃或意外关闭,可能会丢失最近执行的一些命令。因此,建议使用 AOF 持久化来记录 Redis 执行的所有写操作,并将RDB 持久化用于冷备。
也不要仅仅使用 AOF,虽然 AOF 持久化机制可以记录 Redis 执行的所有写操作,因此在数据恢复方面会比 RDB 更加健壮,但是它也存在一些问题。如果仅使用 AOF 进行冷备,那么在恢复数据时,它可能会比 RDB 持久化慢。如果只使用 AOF 持久化,那么可能会因为 AOF 文件过大导致性能下降。
Redis 支持同时使用 AOF 和 RDB 持久化机制。使用 AOF 持久化可以保证数据不丢失,并作为数据恢复的首选,使用 RDB 持久化作为冷备,以提供快速数据恢复选项。这种方式可以利用 AOF 和 RDB 持久化机制的优点来提高数据安全性和恢复速度。
如果同时使用 RDB 和 AOF 持久化机制,在 Redis 重启时,会使用 AOF 来重构数据,因为 AOF 中的数据更加完整。
十四. 怎么使用 Redis 实现消息队列?
Redis 可以使用 list 结构作为队列来实现消息队列,使用 rpush 生产消息,使用lpop 消费消息。当 lpop 没有消息的时候,需要适当的 sleep 一会儿再重试。但是也可以使用 blpop 命令来阻塞住,直到消息到来,避免了 sleep 操作。
如果需要实现生产一次消费多次的场景,可以使用 pub/sub 主题订阅者模式,实现 1:N 的消息队列。
但是 pub/sub 的缺点是在消费者下线的情况下,生产的消息会丢失。因此,如果需要更可靠的消息队列,需要使用专业的消息队列,例如 RabbitMQ。
此外,Redis 还可以使用 sortedset 结构来实现延时队列。使用时间戳作为score,消息内容作为 key,调用 zadd 来生产消息。消费者可以使用zrangebyscore 指令获取 N 秒之前的数据,然后轮询进行处理。
十五. 说说你对 Redis 事务的理解
什么是 Redis 事务?
Redis 中的事务是一组命令的集合,是Redis 的最小执行单位。它可以保证一次执行多个命令,每个事务是一个单独的隔离操作,事务中的所有命令都会被序列化、按顺序地执行,服务端在执行事务的过程中,不会被其他客户端发送来的命令请求打断。Redis 事务通过 MULTI、EXEC、DISCARD、WATCH等命令来实现的。
Redis 事务的注意点有哪些?
Redis 事务是不支持回滚的。
Redis 服务端在执行事务的过程中,不会被其他客户端发送来的命令请求打断,直到事务命令全部执行完毕才会执行其他客户端的命令。
Redis 事务为什么不支持回滚?
Redis 的事务不支持回滚,但是执行的命令如果有语法错误,Redis 会执行失败,这些问题可以从程序层面捕获并解决。但是如果出现其他问题,则依然会继续执行剩下的命令。这样做的原因是因为回滚需要增加很多工作,而不支持回滚可以保持简单、快速的特性。
十六. Redis 为什么设计成单线程的?
Redis 的单线程设计是其高性能的重要原因之一。Redis 单线程的设计思想主要是为了避免多线程带来的上下文切换、锁竞争等开销。从而提高 Redis 的效率和性能。
具体来说,Redis 单线程的设计主要有以下几个方面的考虑:
避免上下文切换:在多线程环境下,线程的切换会涉及到上下文的切换,这个切换本身就就会消耗 CPU 资源和时间。而 Redis 单线程的设计可以避免这种上下文切换的开销,从而提高 Redis 的性能。
避免锁竞争:在多线程环境下,线程之间共享数据时需要使用锁来保证数据的一致性和可靠性。而所本身也会带来开销和竞争,降低 Redis 的效率和性能。而 Redis 单线程的设计可以避免这种锁竞争的开销,从而提高Redis 的性能。
减少内存分配:在多线程环境下,线程之间需要共享内存,而内存共享会涉及到内存分配和管理的开销。而 Redis 单线程的设计可以避免这种内存分配的开销,从而提高 Redis 的效率和性能。
十七. 什么是 Bigkey ?会存在什么影响?
Bigkey 指的是 Redis 中的大键,即占用内存较多的键值对。造成的影响如下:
内存占用:Bigkey 会占用大量的内存资源,导致 Redis 内存不足,从而影响 Redis 的性能和可用性。
网络传输:Bigkey 会增加网络传输的负担,因为在进行数据备份和复制的时候,需要将 Bigkey 的数据全部传输,从而增加了网络带宽的使用。
超时阻塞:由于 Bigkey 占用的空间较大,所以 Redis 在对其操作时,可能会消耗过长的时间,导致客户端超时阻塞。因为 Redis 采用单线程模型,当处理一个大 key 时,其他请求必须等待该操作完成后才能执行,而这个操作可能会需要较长的时间,从而导致阻塞。为了避免这种情况的发生,可以对 bigkey 进行拆分或优化。
内存碎片:Bigkey 会导致 Redis 中出现内存碎片,从而影响 Redis 的内存使用效率,导致 Redis 内存占用率上升。
十八. 说说 Redis 哈希槽的概念
Redis 哈希槽是 Redis 集群中用来实现数据分片的一种机制,可以将所有的键均匀地分布到多个节点上,以实现高可用和高性能分布式数据存储。
具体来说,Redis 集群将整个数据集分为16384个哈希槽,每个节点负责其中的一部分哈希槽,节点之间通过 Gossip 协议进行通信,维护整个集群的状态。当一个客户端想要访问一个键时,Redis 会根据键名计算出该键对应的哈希值,然后找到哈希槽的编号,再根据哈希槽的映射关系,将请求路由到对应节点上。
十九. Redis 常见性能问题和解决方案有哪些?
网络延迟:Redis 的性能很大程度上受限于网络延迟,因此需要尽可能减少网络传输次数和数据量,避免过多的网络IO操作
解决方案:可以使用 Redis 的 Pipline 特性,将多个请求打包发送,减少网络传输的次数;也可以使用 Redis 的批量操作命令,将多个数据一次性提交,减少网络传输的数据量。
大量的数据读写:Redis 的单线程模型会在高并发读写的情况下出现性能瓶颈,导致响应时间变长。
解决方案:可以使用 Redis 的主从复制和集群特性,将数据分布在多个节点上,增加系统的读写并发能力。
慢查询:当 Redis 中存在大量慢查询操作时,会影响 Redis 的整体性能。
解决方案:可以使用 Redis 的 slowlog 功能,记录 Redis 的慢查询操作,并使用 Redis 的监控工具进行监控,及时发现慢查询问题。
内存使用过多:Redis 需要将所有的数据存储在内存中,当数据量过大时,会占用大量的内存资源,导致 Redis 的性能下降。
解决方案:可以使用 Redis 的持久化功能,将数据写入磁盘中,以释放内存空间;也可以使用 Redis 的内存优化技巧,如删除不必要的数据、合理使用 Redis 的数据结构等。
阻塞操作:当 Redis 执行某些操作时,会阻塞其他操作的执行,从而影响 Redis 的整体性能。
解决方案:可以使用 Redis 的异步操作特性,将阻塞操作转化为异步操作,以提高 Redis 的性能和吞吐量。
二十. 如果 Redis 中有 1亿 个 key,其中有 10w 个 key是以某个固定的已知前缀开头的,如何将它们全部找出来?
我们可以使用 keys 命令或 scan 命令,然而在数据量庞大的环境下,不推荐使用 keys 命令。
keys 命令是遍历查询的,时间复杂度为O(n),数据量越大查询时间越长,且 Redis 是单线程的,使用 keys 命令会导致线程阻塞一段时间,从而导致Redis 会出现假死问题,直到 keys 命令执行完毕才能恢复,这在生产环境下是不可接受的。此外,keys 命令没有分页功能,会一次性查询出所有符合条件的 key 值,输出的信息非常多。
相对来说,scan 命令比 keys 命令更适合生产环境。scan 命令可以实现和keys 命令相同的匹配功能,但是在执行过程中不会阻塞线程,并且查询的数据可能存在重复,需要客户端去重。因为 scan 命令是通过游标方式查询的,所以不会导致 Redis 出现假死问题。Redis 在查询过程中会把游标返回给客户端,单词返回控制且游标不为0,则说明遍历还没有结束,客户端继续遍历查询。但是,scan 命令在检索的过程中,被删除的元素是不会被查询出来的,如果在迭代过程中有元素被修改,scan 命令也不能保证查询出对应的元素。相对来说,scan 命令查找花费的时间会比 keys 命令长。
补充:假死问题
Redis 假死问题是指当 Redis 实例在进行某些耗时操作时(例如遍历所有key),由于 Redis 是单线程的,所以这个操作会导致 Redis 线程被阻塞,从而导致 Redis 无法处理其他请求,造成 Redis 服务不可用的状态。在这种情况下,Redis 似乎已经死了,但其实 Redis 线程仍在执行操作,只是无法处理其他请求而已。因此,这种状态被称为 Redis 假死问题。避免Redis 假死问题的常见方法是使用 Redis 提供的异步命令和管道技术,以避免在生产环境中会使用遍历所有 key 的操作。
二十一. 如果有大量的 key 需要设置同一时间过期,一般需要注意什么?
如果大量缓存同时失效,会导致大量的请求直接访问数据库,容易造成数据库崩溃或者降低数据库的性能,进而影响整个系统的稳定性。
为了预防这种情况的发生,我们最好在设计数据过期时间的时候,都加上一个随机值,让过期时间更加分散,从而尽量避免大量的key在同一时刻失效。
二十二. 什么情况下可能会导致 Redis 阻塞?
Redis 可能出现阻塞的情况包括:
Redis 主线程在执行阻塞命令(如
BRPOP
、BLPOP
、BRPOPLPUSH
、SUBSCRIBE
等)时,会阻塞其他客户端的请求,直到命令执行完毕才能继续处理其他请求。Redis 主线程在执行某些耗时的命令(如
SORT
、KEYS
等)时,也会阻塞其他客户端的请求,同样需要等待命令执行完毕后才能继续处理其他请求。Redis 内存使用达到最大限制时,执行写操作(如
SET
、INCR
等)可能会导致 Redis 阻塞。这是因为 Redis 需要执行内存回收操作以释放内存空间,如果回收操作耗时过长,就会导致 Redis 阻塞。Redis 主从同步过程中,如果主库无法及时响应从库的同步请求,就会导致从库阻塞,无法继续进行数据同步。
对于这些阻塞情况,可以采取一些措施来避免或减少阻塞的影响,例如
尽可能使用非阻塞命令,例如
LPUSH
和RPOP
代替BLPOP
,使用Lua脚本实现多个操作的原子性等。尽量避免使用耗时的命令或对大数据集进行操作,如果必须使用,可以考虑将这些操作放在后台进行。
设置合理的内存使用上限,同时使用内存淘汰策略来控制内存使用情况。
配置合理的主从架构,避免主库过于繁忙,导致从库同步阻塞。
二十三. 怎么提高缓存命中率?
提高缓存命中率可以采取以下措施:
预热缓存:在系统启动的时候,将一些热点数据提前加载到缓存中,可以避免在系统运行时出现缓存穿透和缓存雪崩的情况。
增加缓存容量:增加缓存容量可以缓存更多的数据,从而提高缓存命中率。
优化缓存设计:合理的缓存设计是提高缓存命中率的前提,包括选择合适的数据结构、缓存过期时间、缓存的key命名等。
使用多级缓存:多级缓存可以将热点数据缓存在更快速、容量更小的缓存中,减少从慢速缓存或者数据库中读取数据的次数。
缓存穿透处理:针对一些缓存中不存在,但是经常被查询的数据,可以采取布隆过滤器或设置空值等方式来进行预判,避免缓存穿透的情况。
建立读写分离的架构:将读请求和写请求分别处理,读请求可以直接从缓存中读取数据,写请求更新数据库后再更新缓存,从而避免缓存和数据库的一致性问题。
二十四. Redis 如何解决 key 冲突?
如果两个 key 的名字相同,后一个 key 会覆盖前一个 key。因此,为了避免 key 冲突,最好为每一个 key 取一个独特的、易于辨识的名称。
通常可以使用业务名和参数来区 key,这样可以避免 key 冲突,同时也方便业务逻辑的管理和维护。
二十五. Redis 报内存不足怎么处理?
可以考虑以下几种处理方式:
增加物理内存:增加 Redis 所在服务器的物理内存,可以让 Redis 有更多的空间来存储数据。
减少数据量:可以删除一些已经不再使用的数据,或者将一些数据进行持久化,以释放内存。设置缓存淘汰策略,提高内存的使用效率。
修改 Redis 配置:可以调整 Redis 配置文件中的一些参数,如 maxmemory 等,增加 Redis 可用内存。
使用 Redis 集群:可以将数据分散在多个 Redis 节点中,每个节点存储一部分数据,从而减少单个 Redis 实例的内存使用量。
二十六. 缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级都了解吗?
简述其概念如下
缓存雪崩:指在某个时间段内缓存集体过期失效或缓存服务重启,导致大量请求都落到数据库上,从而导致数据库崩溃的情况。
缓存穿透:指查询一个不存在的数据,由于缓存没有命中,导致所有的请求都会到数据库上,造成数据库压力过大,严重的可能会导致数据库宕机。
缓存预热:指系统上线后,将相关的缓存数据直接加载到缓存系统中,避免在用户请求过程中因没有预先加载而导致缓存穿透的现象。
缓存更新:指对数据库中的数据更新时,同时更新缓存中的数据,保证缓存数据和数据库的一致性。
缓存降级:指在缓存失效或缓存访问异常时,为了保证系统的可用性,通过一些机制,将请求转发到其他服务或者直接返回默认值,从而避免系统崩溃或者因为缓存故障导致业务受损。
常见的 Redis 缓存降级策略包括:
熔断降级:当 Redis 缓存故障或者超时时,系统会进入熔断状态,所有请求奖杯转发到备用服务或者直接返回默认值。
限流降级:当 Redis 缓存无法处理所有请求时,系统会采用限流策略,限制访问流量,保护系统资源,避免系统崩溃。
数据降级:当 Redis 缓存故障时,系统可以返回默认值,避免因缓存故障导致业务受损。
二十七. 热点数据和冷数据是什么?
热点数据和冷数据是根据数据被访问的频率来进行划分的。
热点数据值的是被频繁访问的数据,通常是系统的核心数据,例如热门商品、热门文章、热门活动等,这些数据的访问量非常高,如果没有得到有效的缓存优化,系统将会面临严重的性能问题。
冷数据则相反,指的是不经常被访问的数据,它们的数据访问频率较低,例如旧的文章、过期的活动等。
了解热点数据和冷数据对于缓存设计和优化非常重要,因为不同的数据需要采用不同的缓存策略。
例如对于热点数据需要采用缓存预热、缓存更新等策略来保证缓存的命中率,而对于冷数据则可以采用懒加载等策略来避免不必要的缓存开销。
二十八. Memcached 和 Redis 的区别都有哪些?
Memcached 和 Redis 是两种常用的缓存系统,它们的区别如下:
数据类型:Redis 支持更丰富的数据类型,包括字符串、哈希、列表、集合、有序集合等,而 Memcached 仅支持简单的键值对存储。
持久化:Redis 支持数据的持久化,可以将数据写入磁盘,而 Memcached不支持数据的持久化。
分布式支持:Memcached 天生支持分布式,多个节点可以组成一个集群,而 Redis 的分布式支持需要通过集群、分片等方式实现。
性能:在单机环境下,Redis 的性能通常比 Memcached 更好,但在分布式环境下,由于网络通信开销的增加,两者的性能差距可能会减小。
缓存策略:Redis 支持更多的缓存策略,比如 LRU(最近最少使用)、LFU(最少使用)、随机等,而 Memcached 仅支持 LRU。
应用场景:Redis 更适合需要丰富数据类型、支持持久化、缓存策略较多、单机性能较好的场景;而 Memcached 更适合需要高速读写、分布式支持、缓存策略相对简单的场景。
二十九. Redis 的数据类型,以及每种数据类型的使用场景?
常见的几种数据类型和使用场景如下:
字符串(String):字符串类型是 Redis 最基本的数据结构,一个键最大能存储512MB。
使用场景:适用于计数器、分布式锁、缓存等常见。
列表(List):列表是链表结构,可以在头部和尾部添加元素。
使用场景:可以做简单的消息队列功能。利用 Irange 命令,做基于Redis的分页功能。
集合(Set):集合是通过哈希表实现的无序集合,每个元素都是独一无二的。
使用场景:适用于好友关系、共同好友等去重和计算交集、并集、差集的场景。
哈希(Hash):哈希结构类似于关联数组,由字段和值组成。
使用场景:适用于对象缓存。
有序集合(Sorted Set):有序集合类似于集合,不同的是每个元素都会关联一个权重(score),按照权重进行排序。
使用场景:排行榜、带权重的任务队列等场景。
位图(BitMap):用于存储二进制位的数据结构,可以进行位运算,支持高效的位图计算。
使用场景:用户签到记录。
地理位置(Geo):用于存储地理位置信息的数据结构。
使用场景:附近的酒店、餐厅。
HyperLogLog:用于进行基数计数的数据结构,支持高效的对大量元素进行去重统计。
使用场景:网站的UV统计。
三十. Redis 的过期策略以及内存淘汰机制
Redis 的过期策略和内存淘汰机制如下:
过期策略:Redis 中可以设置 key 的过期时间,过期时间到期后,key 将会自动被删除。Redis 提供了两种不同的过期策略:
定时删除:在设置 key 过期的同时,创建一个定时器,当过期时间到达时,就会立即删除该 key。
惰性删除:再获取某个 key 的值时,先检查该 key 是否过期,如果过期就删除,否则返回该 key 的值。
Redis 默认使用惰性删除策略。
内存淘汰机制:当 Redis 内存达到了最大限制时,需要从内存中删除一些数据。Redis 提供了多种内存淘汰机制:
noeviction:当内存空间不足以容纳新写入数据时,新写入操作会报错,这种方式不会删除任何数据,应该只用于特殊场景。
allkeys-lru:当内存空间不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(LRU算法)。这是 Redis 默认的淘汰策略。
allkeys-random:从所有 key 中随机选择一些进行删除。
volatile-lru:当内存空间不足以容纳新写入数据时,再设置了过期时间的键空间中,移除最近最少使用的 key(LRU算法)。
volatile-ramdom:从设置了过期时间的 key 中随机选择一些进行删除。
volatile-ttl:从设置了过期时间的 key 中,根据过期时间的先后顺序进行删除,越早过期的越优先删除。
三十一. 为什么 Redis 的操作是原子性的,怎么保证原子性?
Redis 的操作是原子性的,是因为 Redis 是单线程的,Redis 中的所有操作都是在一个单线程中执行,这样就可以避免并发的环境下多个线程同时修改同一个键值对的问题。在 Redis 中,任何一个操作都是原子性的,要么执行成功,要么执行不成功。如果一个操作包含多个步骤,那么这些步骤会被当成一个整体,要么全部执行成功,要么全部不执行。
Redis 保证原子性的方式主要有两种:事务和 Lua 脚本。在事务中,Redis 会将多个命令打包成一个事务进行执行,事务中的所有命令都会在一次操作中被执行,要么全部执行成功,要么全部不执行。而 Lua 脚本则可以将多个操作打包成一个原子性的操作进行执行,这个操作要么全部执行成功,要么全部不执行。另外,Redis 还提供了一些原子性操作,例如 INCR、DECR 等,这些操作都是原子性的。
在并发环境下,如果多个线程同时执行 get 和 set 命令,可能会出现竞争条件,从而导致数据不一致的问题。但是如果使用 Redis 提供的原子性操作INCR,则不会存在这种问题,因为 INCR 命令是原子性的。
因此可以使用 Redis 事务或者 Redis+Lua 的方式保证多个命令在并发中的原子性,或者使用 Redis 提供的原子性操作。