Redis是什么?
Redis(Remote Dictionary Server
)是一个使用 C 语言编写的,高性能非关系型的键值对数据库。与传统数据库不同的是,Redis 的数据是存在内存中的,所以读写速度非常快,被广泛应用于缓存方向。Redis可以将数据写入磁盘中,保证了数据的安全不丢失,而且Redis的操作是原子性的。
Redis优缺点?
优点:
-
基于内存操作,内存读写速度快。
- 支持多种数据类型,包括String、Hash、List、Set、ZSet等。
- 支持持久化。Redis支持RDB和AOF两种持久化机制,持久化功能可以有效地避免数据丢失问题。
- 支持事务。Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
- 支持主从复制。主节点会自动将数据同步到从节点,可以进行读写分离。
- Redis命令的处理是单线程的。Redis6.0引入了多线程,需要注意的是,多线程用于处理网络数据的读写和协议解析,Redis命令执行还是单线程的。
缺点:
- 对结构化查询的支持比较差。
- 数据库容量受到物理内存的限制,不适合用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的操作。
- Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
Redis为什么这么快?
-
基于内存:Redis是使用内存存储,没有磁盘IO上的开销。数据存在内存中,读写速度快。
- IO多路复用模型:Redis 采用 IO 多路复用技术。Redis 使用单线程来轮询描述符,将数据库的操作都转换成了事件,不在网络I/O上浪费过多的时间。
- 高效的数据结构:Redis 每种数据类型底层都做了优化,目的就是为了追求更快的速度。
计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~
如果访问不了Github,可以访问gitee地址。
既然Redis那么快,为什么不用它做主数据库,只用它做缓存?
虽然Redis非常快,但它也有一些局限性,不能完全替代主数据库。有以下原因:
事务处理:Redis只支持简单的事务处理,对于复杂的事务无能为力,比如跨多个键的事务处理。
数据持久化:Redis是内存数据库,数据存储在内存中,如果服务器崩溃或断电,数据可能丢失。虽然Redis提供了数据持久化机制,但有一些限制。
数据处理:Redis只支持一些简单的数据结构,比如字符串、列表、哈希表等。如果需要处理复杂的数据结构,比如关系型数据库中的表,那么Redis可能不是一个好的选择。
数据安全:Redis没有提供像主数据库那样的安全机制,比如用户认证、访问控制等等。
讲讲Redis的线程模型?
Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型。
- 文件事件处理器使用I/O多路复用(multiplexing)程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。
- 当被监听的套接字准备好执行连接accept、read、write、close等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
Redis应用场景有哪些?
-
缓存热点数据,缓解数据库的压力。
- 利用 Redis 原子性的自增操作,可以实现计数器的功能,比如统计用户点赞数、用户访问数等。
- 分布式锁。在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。
- 简单的消息队列,可以使用Redis自身的发布/订阅模式或者List来实现简单的消息队列,实现异步操作。
- 限速器,可用于限制某个用户访问某个接口的频率,比如秒杀场景用于防止用户快速点击带来不必要的压力。
- 好友关系,利用集合的一些命令,比如交集、并集、差集等,实现共同好友、共同爱好之类的功能。
最全面的Java面试网站
Memcached和Redis的区别?
- MemCached 数据结构单一,仅用来缓存数据,而 Redis 支持多种数据类型。
- MemCached 不支持数据持久化,重启后数据会消失。Redis 支持数据持久化。
- Redis 提供主从同步机制和 cluster 集群部署能力,能够提供高可用服务。Memcached 没有提供原生的集群模式,需要依靠客户端实现往集群中分片写入数据。
- Redis 的速度比 Memcached 快很多。
- Redis 使用单线程的多路 IO 复用模型,Memcached使用多线程的非阻塞 IO 模型。(Redis6.0引入了多线程IO,用来处理网络数据的读写和协议解析,但是命令的执行仍然是单线程)
- value 值大小不同:Redis 最大可以达到 512M;memcache 只有 1mb。
为什么要用 Redis 而不用 map/guava 做缓存?
本地缓存,最主要的特点是轻量以及快速,生命周期随着 jvm 的销毁而结束,并且在多实例的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性。
分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。
Redis 数据类型有哪些?
基本数据类型:
String:最常用的一种数据类型,String类型的值可以是字符串、数字或者二进制,但值最大不能超过512MB。
Hash:Hash 是一个键值对集合。
Set:无序去重的集合。Set 提供了交集、并集等方法,对于实现共同好友、共同关注等功能特别方便。
List:有序可重复的集合,底层是依赖双向链表实现的。
SortedSet:有序Set。内部维护了一个score
的参数来实现。适用于排行榜和带权重的消息队列等场景。
特殊的数据类型:
Bitmap:位图,可以认为是一个以位为单位数组,数组中的每个单元只能存0或者1,数组的下标在 Bitmap 中叫做偏移量。Bitmap的长度与集合中元素个数无关,而是与基数的上限有关。
Hyperloglog。HyperLogLog 是用来做基数统计的算法,其优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。典型的使用场景是统计独立访客。
Geospatial :主要用于存储地理位置信息,并对存储的信息进行操作,适用场景如定位、附近的人等。
SortedSet和List异同点?
相同点:
- 都是有序的;
- 都可以获得某个范围内的元素。
不同点:
- 列表基于链表实现,获取两端元素速度快,访问中间元素速度慢;
- 有序集合基于散列表和跳跃表实现,访问中间元素时间复杂度是OlogN;
- 列表不能简单的调整某个元素的位置,有序列表可以(更改元素的分数);
- 有序集合更耗内存。
Redis的内存用完了会怎样?
也可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。
Redis如何做内存优化?
keys命令存在的问题?
redis的单线程的。keys指令会导致线程阻塞一段时间,直到执行完毕,服务才能恢复。scan采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题,每次scan命令的时间复杂度是O(1
,但是要真正实现keys的功能,需要执行多次scan。
Redis事务
事务的原理是将一个事务范围内的若干命令发送给Redis,然后再让Redis依次执行这些命令。
-
EXEC命令进行提交事务
使用MULTI开启一个事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a 1
QUEUED
127.0.0.1:6379> set b 1 2
QUEUED
127.0.0.1:6379> set c 3
QUEUED
127.0.0.1:6379> exec
1 OK
2 (error ERR syntax error
3 OK
WATCH命令
WATCH命令可以监控一个或多个键,一旦其中有一个键被修改,之后的事务就不会执行(类似于乐观锁)。执行EXEC
命令之后,就会自动取消监控。
127.0.0.1:6379> watch name
OK
127.0.0.1:6379> set name 1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name 2
QUEUED
127.0.0.1:6379> set gender 1
QUEUED
127.0.0.1:6379> exec
(nil
127.0.0.1:6379> get gender
(nil
比如上面的代码中:
- 修改
name
的值 - 开启事务a
- 在事务a中设置了
name
和gender
的值 - 使用
EXEC
命令进提交事务 - 使用命令
get gender
发现不存在,即事务a没有执行
watch name
开启了对name
这个key
的监控
UNWATCH可以取消WATCH
命令对key
的监控,所有监控锁将会被取消。
Redis事务支持隔离性吗?
Redis事务保证原子性吗,支持回滚吗?
Redis单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
持久化机制
内存的数据写到磁盘中,防止服务宕机导致内存数据丢失。
RDB的方式,一种是AOF
的方式。前者会根据指定的规则定时将内存中的数据存储在硬盘上,而后者在每次执行完命令后将命令记录下来。一般将两者结合使用。
RDB方式
RDB是 Redis 默认的持久化方案。RDB持久化时会将内存中的数据写入到磁盘中,在指定目录下生成一个dump.rdb
文件。Redis 重启会加载dump.rdb
文件恢复数据。
bgsave是主流的触发 RDB 持久化的方式,执行过程如下:
- 执行
- Redis 父进程判断当前是否存在正在执行的子进程,如果存在,
BGSAVE
命令直接返回。 - 父进程执行
fork
操作创建子进程,fork操作过程中父进程会阻塞。 - 父进程
fork
完成后,父进程继续接收并处理客户端的请求,而子进程开始将内存中的数据写进硬盘的临时文件; - 当子进程写完所有数据后会用该临时文件替换旧的 RDB 文件。
BGSAVE
命令
触发 RDB 持久化的方式:
-
被动触发:
- 根据配置规则进行自动快照,如
SAVE 100 10
,100秒内至少有10个键被修改则进行快照。 - 如果从节点执行全量复制操作,主节点会自动执行
BGSAVE
生成 RDB 文件并发送给从节点。 - 默认情况下执行
shutdown
命令时,如果没有开启 AOF 持久化功能则自动执行·BGSAVE·。
手动触发:用户执行SAVE
或BGSAVE
命令。SAVE
命令执行快照的过程会阻塞所有客户端的请求,应避免在生产环境使用此命令。BGSAVE
命令可以在后台异步进行快照操作,快照的同时服务器还可以继续响应客户端的请求,因此需要手动执行快照时推荐使用BGSAVE
命令。
优点:
-
Redis 加载 RDB 恢复数据远远快于 AOF 的方式。
- 使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 Redis 的高性能。
缺点:
-
RDB方式数据无法做到实时持久化。因为
- RDB 文件使用特定二进制格式保存,Redis 版本升级过程中有多个格式的 RDB 版本,存在老版本 Redis 无法兼容新版 RDB 格式的问题。
BGSAVE
每次运行都要执行fork
操作创建子进程,属于重量级操作,频繁执行成本比较高。
AOF方式
解决了数据持久化的实时性,AOF 是Redis持久化的主流方式。
appendonly参数启用:appendonly yes
。开启AOF方式持久化后每执行一条写命令,Redis就会将该命令写进aof_buf
缓冲区,AOF缓冲区根据对应的策略向硬盘做同步操作。
每30秒会执行一次同步操作。为了防止缓冲区数据丢失,可以在Redis写入AOF文件后主动要求系统将缓冲区数据同步到硬盘上。可以通过appendfsync
参数设置同步的时机。
appendfsync always //每次写入aof文件都会执行同步,最安全最慢,不建议配置
appendfsync everysec //既保证性能也保证安全,建议配置
appendfsync no //由操作系统决定何时进行同步操作
接下来看一下 AOF 持久化执行流程:
- 所有的写入命令会追加到 AOP 缓冲区中。
- AOF 缓冲区根据对应的策略向硬盘同步。
- 随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩文件体积的目的。AOF文件重写是把Redis进程内的数据转化为写命令同步到新AOF文件的过程。
- 当 Redis 服务器重启时,可以加载 AOF 文件进行数据恢复。
优点:
- AOF可以更好的保护数据不丢失,可以配置 AOF 每秒执行一次
- AOF以
append-only
的模式写入,所以没有磁盘寻址的开销,写入性能非常高。
fsync
操作,如果Redis进程挂掉,最多丢失1秒的数据。
缺点:
- 对于同一份文件AOF文件比RDB数据快照要大。
- 数据恢复比较慢。
RDB和AOF如何选择?
- 如果数据不敏感,且可以从其他地方重新生成,可以关闭持久化。
- 如果数据比较重要,且能够承受几分钟的数据丢失,比如缓存等,只需要使用RDB即可。
- 如果是用做内存数据,要使用Redis的持久化,建议是RDB和AOF都开启。
- 如果只用AOF,优先使用everysec的配置选择,因为它在可靠性和性能之间取了一个平衡。
当RDB与AOF两种方式都开启时,Redis会优先使用AOF恢复数据,因为AOF保存的文件比RDB文件更完整。
Redis有哪些部署方案?
单机版:单机部署,单机redis能够承载的 QPS 大概就在上万到几万不等。这种部署方式很少使用。存在的问题:1、内存容量有限 2、处理能力有限 3、无法高可用。
主从模式:一主多从,主负责写,并且将数据复制到其它的 slave 节点,从节点负责读。所有的读请求全部走从节点。这样也可以很轻松实现水平扩容,支撑读高并发。master 节点挂掉后,需要手动指定新的 master,可用性不高,基本不用。
哨兵模式:主从复制存在不能自动故障转移、达不到高可用的问题。哨兵模式解决了这些问题。通过哨兵机制可以自动切换主从节点。master 节点挂掉后,哨兵进程会主动选举新的 master,可用性高,但是每个节点存储的数据是一样的,浪费内存空间。数据量不是很多,集群规模不是很大,需要自动容错容灾的时候使用。
Redis cluster:服务端分片技术,3.0版本开始正式提供。Redis Cluster并没有使用一致性hash,而是采用slot(槽的概念,一共分成16384个槽。将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行。主要是针对海量数据+高并发+高可用的场景,如果是海量数据,如果你的数据量很大,那么建议就用Redis cluster,所有主节点的容量总和就是Redis cluster可缓存的数据容量。
主从架构
Redis的复制功能是支持多个数据库之间的数据同步。主数据库可以进行读写操作,当主数据库的数据发生变化时会自动将数据同步到从数据库。从数据库一般是只读的,它会接收主数据库同步过来的数据。一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。
主从复制的原理?
- 当启动一个从节点时,它会发送一个
- 如果是从节点初次连接到主节点,那么会触发一次全量复制。此时主节点会启动一个后台线程,开始生成一份
RDB
快照文件; - 同时还会将从客户端 client 新收到的所有写命令缓存在内存中。
RDB
文件生成完毕后,主节点会将RDB
文件发送给从节点,从节点会先将RDB
文件写入本地磁盘,然后再从本地磁盘加载到内存中; - 接着主节点会将内存中缓存的写命令发送到从节点,从节点同步这些数据;
- 如果从节点跟主节点之间网络出现故障,连接断开了,会自动重连,连接之后主节点仅会将部分缺失的数据同步给从节点。
PSYNC
命令给主节点;
哨兵Sentinel
客户端连