关于java:Redis-高频面试题含答案

2次阅读

共计 19586 个字符,预计需要花费 49 分钟才能阅读完成。

什么是 Redis

Redis(Remote Dictionary Server) 是一个应用 C 语言编写的,开源的(BSD 许可)高性能非关系型(NoSQL)的键值对数据库。

Redis 能够存储键和五种不同类型的值之间的映射。键的类型只能为字符串,值反对五种数据类型:字符串、列表、汇合、散列表、有序汇合。

与传统数据库不同的是 Redis 的数据是存在内存中的,所以读写速度十分快,因而 redis 被广泛应用于缓存方向,每秒能够解决超过 10 万次读写操作,是已知性能最快的 Key-Value DB。另外,Redis 也常常用来做分布式锁。除此之外,Redis 反对事务、长久化、LUA 脚本、LRU 驱动事件、多种集群计划。

Redis 有哪些优缺点

长处

读写性能优异,Redis 能读的速度是 110000 次 /s,写的速度是 81000 次 /s。

反对数据长久化,反对 AOF 和 RDB 两种长久化形式。

反对事务,Redis 的所有操作都是原子性的,同时 Redis 还反对对几个操作合并后的原子性执行。

数据结构丰盛,除了反对 string 类型的 value 外还反对 hash、set、zset、list 等数据结构。

反对主从复制,主机会主动将数据同步到从机,能够进行读写拆散。

毛病

数据库容量受到物理内存的限度,不能用作海量数据的高性能读写,因而 Redis 适宜的场景次要局限在较小数据量的高性能操作和运算上。

Redis 不具备主动容错和复原性能,主机从机的宕机都会导致前端局部读写申请失败,须要期待机器重启或者手动切换前端的 IP 能力复原。

主机宕机,宕机前有局部数据未能及时同步到从机,切换 IP 后还会引入数据不统一的问题,升高了零碎的可用性。

Redis 较难反对在线扩容,在集群容量达到下限时在线扩容会变得很简单。为防止这一问题,运维人员在零碎上线时必须确保有足够的空间,这对资源造成了很大的节约。

为什么要用缓存,为什么要用 Redis

次要从“高性能”和“高并发”这两点来对待这个问题。

高性能:

如果用户第一次拜访数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户拜访的数据存在数缓存中,这样下一次再拜访这些数据的时候就能够间接从缓存中获取了。操作缓存就是间接操作内存,所以速度相当快。如果数据库中的对应数据扭转的之后,同步扭转缓存中相应的数据即可!

高并发:

间接操作缓存可能接受的申请是远远大于间接拜访数据库的,所以咱们能够思考把数据库中的局部数据转移到缓存中去,这样用户的一部分申请会间接到缓存这里而不必通过数据库。

为什么要用 Redis 而不必 map/guava 做缓存?

缓存分为本地缓存和分布式缓存。以 Java 为例,应用自带的 map 或者 guava 实现的是本地缓存,最次要的特点是轻量以及疾速,生命周期随着 jvm 的销毁而完结,并且在多实例的状况下,每个实例都须要各自保留一份缓存,缓存不具备一致性。

应用 redis 或 memcached 之类的称为分布式缓存,在多实例的状况下,各实例共用一份缓存数据,缓存具备一致性。毛病是须要放弃 redis 或 memcached 服务的高可用,整个程序架构上较为简单。

Redis 为什么这么快

1、齐全基于内存,绝大部分申请是纯正的内存操作,十分疾速。数据存在内存中,相似于 HashMap,HashMap 的劣势就是查找和操作的工夫复杂度都是 O(1);

2、数据结构简略,对数据操作也简略,Redis 中的数据结构是专门进行设计的;

3、采纳单线程,防止了不必要的上下文切换和竞争条件,也不存在多过程或者多线程导致的切换而耗费 CPU,不必去思考各种锁的问题,不存在加锁开释锁操作,没有因为可能呈现死锁而导致的性能耗费;

4、应用多路 I/O 复用模型,非阻塞 IO;

5、应用底层模型不同,它们之间底层实现形式以及与客户端之间通信的利用协定不一样,Redis 间接本人构建了 VM 机制,因为个别的零碎调用零碎函数的话,会节约肯定的工夫去挪动和申请;

数据类型

Redis 有哪些数据类型

Redis 次要有 5 种数据类型,包含 String,List,Set,Zset,Hash,满足大部分的应用要求

Redis 的利用场景

总结一

1、计数器

能够对 String 进行自增自减运算,从而实现计数器性能。Redis 这种内存型数据库的读写性能十分高,很适宜存储频繁读写的计数量。

2、缓存

将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保障缓存的命中率。

3、会话缓存

能够应用 Redis 来对立存储多台应用服务器的会话信息。当应用服务器不再存储用户的会话信息,也就不再具备状态,一个用户能够申请任意一个应用服务器,从而更容易实现高可用性以及可伸缩性。

4、全页缓存(FPC)

除根本的会话 token 之外,Redis 还提供很简便的 FPC 平台。以 Magento 为例,Magento 提供一个插件来应用 Redis 作为全页缓存后端。此外,对 WordPress 的用户来说,Pantheon 有一个十分好的插件 wp-redis,这个插件能帮忙你以最快速度加载你曾浏览过的页面。

5、查找表

例如 DNS 记录就很适宜应用 Redis 进行存储。查找表和缓存相似,也是利用了 Redis 疾速的查找个性。然而查找表的内容不能生效,而缓存的内容能够生效,因为缓存不作为牢靠的数据起源。

6、音讯队列(公布 / 订阅性能)

List 是一个双向链表,能够通过 lpush 和 rpop 写入和读取音讯。不过最好应用 Kafka、RabbitMQ 等消息中间件。

7、分布式锁实现

在分布式场景下,无奈应用单机环境下的锁来对多个节点上的过程进行同步。能够应用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还能够应用官网提供的 RedLock 分布式锁实现。

8、其它

Set 能够实现交加、并集等操作,从而实现独特好友等性能。ZSet 能够实现有序性操作,从而实现排行榜等性能。

总结二

Redis 相比其余缓存,有一个十分大的劣势,就是反对多种数据类型。

数据类型阐明 string 字符串,最简略的 k - v 存储 hashhash 格局,value 为 field 和 value,适宜 ID-Detail 这样的场景。list 简略的 list,程序列表,反对首位或者开端插入数据 set 无序 list,查找速度快,适宜交加、并集、差集解决 sorted set 有序的 set

其实,通过下面的数据类型的个性,根本就能想到适合的利用场景了。

string——适宜最简略的 k - v 存储,相似于 memcached 的存储构造,短信验证码,配置信息等,就用这种类型来存储。

hash——个别 key 为 ID 或者惟一标示,value 对应的就是详情了。如商品详情,个人信息详情,新闻详情等。

list——因为 list 是有序的,比拟适宜存储一些有序且数据绝对固定的数据。如省市区表、字典表等。因为 list 是有序的,适宜依据写入的工夫来排序,如:最新的热点新闻,音讯队列等。

set——能够简略的了解为 ID-List 的模式,如微博中一个人有哪些好友,set 最牛的中央在于,能够对两个 set 提供交加、并集、差集操作。例如:查找两个人独特的好友等。

Sorted Set——是 set 的加强版本,减少了一个 score 参数,主动会依据 score 的值进行排序。比拟适宜相似于 top 10 等不依据插入的工夫来排序的数据。

如上所述,尽管 Redis 不像关系数据库那么简单的数据结构,然而,也能适宜很多场景,比个别的缓存数据构造要多。理解每种数据结构适宜的业务场景,不仅有利于晋升开发效率,也能无效利用 Redis 的性能。

长久化

什么是 Redis 长久化?

长久化就是把内存的数据写到磁盘中去,避免服务宕机了内存数据失落。

Redis 的长久化机制是什么?各自的优缺点?

Redis 提供两种长久化机制 RDB(默认)和 AOF 机制;

RDB:是 Redis DataBase 缩写快照

RDB 是 Redis 默认的长久化形式。依照肯定的工夫将内存的数据以快照的模式保留到硬盘中,对应产生的数据文件为 dump.rdb。通过配置文件中的 save 参数来定义快照的周期。

长处:

1、只有一个文件 dump.rdb,不便长久化。

2、容灾性好,一个文件能够保留到平安的磁盘。

3、性能最大化,fork 子过程来实现写操作,让主过程持续解决命令,所以是 IO 最大化。应用独自子过程来进行长久化,主过程不会进行任何 IO 操作,保障了 redis 的高性能。

4、绝对于数据集大时,比 AOF 的启动效率更高。

毛病:

1、数据安全性低。RDB 是距离一段时间进行长久化,如果长久化之间 redis 产生故障,会产生数据失落。所以这种形式更适宜数据要求不谨严的时候)

2、AOF(Append-only file)长久化形式:是指所有的命令行记录以 redis 命令请 求协定的格局齐全长久化存储)保留为 aof 文件。

AOF:长久化

AOF 长久化(即 Append Only File 长久化),则是将 Redis 执行的每次写命令记录到独自的日志文件中,当重启 Redis 会从新将长久化的日志中文件复原数据。

当两种形式同时开启时,数据恢复 Redis 会优先选择 AOF 复原。

长处:

1、数据安全,aof 长久化能够配置 appendfsync 属性,有 always,每进行一次 命令操作就记录到 aof 文件中一次。

2、通过 append 模式写文件,即便中途服务器宕机,能够通过 redis-check-aof 工具解决数据一致性问题。

3、AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令 进行合并重写),能够删除其中的某些命令(比方误操作的 flushall)

毛病:

1、AOF 文件比 RDB 文件大,且复原速度慢。

2、数据集大的时候,比 rdb 启动效率低。

优缺点是什么?

AOF 文件比 RDB 更新频率高,优先应用 AOF 还原数据

AOF 比 RDB 更平安也更大

RDB 性能比 AOF 好

如果两个都配了优先加载 AOF

如何抉择适合的长久化形式?

一般来说,如果想达到足以媲美 PostgreSQL 的数据安全性,你应该同时应用两种长久化性能。在这种状况下,当 Redis 重启的时候会优先载入 AOF 文件来复原原始的数据,因为在通常状况下 AOF 文件保留的数据集要比 RDB 文件保留的数据集要残缺。

如果你十分关怀你的数据,但依然能够接受数分钟以内的数据失落,那么你能够只应用 RDB 长久化。

有很多用户都只应用 AOF 长久化,但并不举荐这种形式,因为定时生成 RDB 快照(snapshot)十分便于进行数据库备份,并且 RDB 复原数据集的速度也要比 AOF 复原的速度要快,除此之外,应用 RDB 还能够防止 AOF 程序的 bug。

如果你只心愿你的数据在服务器运行的时候存在,你也能够不应用任何长久化形式。

Redis 长久化数据和缓存怎么做扩容?

如果 Redis 被当做缓存应用,应用一致性哈希实现动静扩容缩容。

如果 Redis 被当做一个长久化存储应用,必须应用固定的 keys-to-nodes 映射关系,节点的数量一旦确定不能变动。否则的话(即 Redis 节点须要动态变化的状况),必须应用能够在运行时进行数据再均衡的一套零碎,而以后只有 Redis 集群能够做到这样。

过期键的删除策略

Redis 的过期键的删除策略

咱们都晓得,Redis 是 key-value 数据库,咱们能够设置 Redis 中缓存的 key 的过期工夫。Redis 的过期策略就是指当 Redis 中缓存的 key 过期了,Redis 如何解决。

过期策略通常有以下三种:

定时过期:每个设置过期工夫的 key 都须要创立一个定时器,到过期工夫就会立刻革除。该策略能够立刻革除过期的数据,对内存很敌对;然而会占用大量的 CPU 资源去解决过期的数据,从而影响缓存的响应工夫和吞吐量。

惰性过期:只有当拜访一个 key 时,才会判断该 key 是否已过期,过期则革除。该策略能够最大化地节俭 CPU 资源,却对内存十分不敌对。极其状况可能呈现大量的过期 key 没有再次被拜访,从而不会被革除,占用大量内存。

定期过期:每隔肯定的工夫,会扫描肯定数量的数据库的 expires 字典中肯定数量的 key,并革除其中已过期的 key。该策略是前两者的一个折中计划。通过调整定时扫描的工夫距离和每次扫描的限定耗时,能够在不同状况下使得 CPU 和内存资源达到最优的均衡成果。

(expires 字典会保留所有设置了过期工夫的 key 的过期工夫数据,其中,key 是指向键空间中的某个键的指针,value 是该键的毫秒精度的 UNIX 工夫戳示意的过期工夫。键空间是指该 Redis 集群中保留的所有键。)

Redis 中同时应用了惰性过期和定期过期两种过期策略。

Redis key 的过期工夫和永恒无效别离怎么设置?

EXPIRE 和 PERSIST 命令。

咱们晓得通过 expire 来设置 key 的过期工夫,那么对过期的数据怎么解决呢?

除了缓存服务器自带的缓存生效策略之外(Redis 默认的有 6 中策略可供选择),咱们还能够依据具体的业务需要进行自定义的缓存淘汰,常见的策略有两种:

1、定时去清理过期的缓存;

2、当有用户申请过去时,再判断这个申请所用到的缓存是否过期,过期的话就去底层零碎失去新数据并更新缓存。

两者各有优劣,第一种的毛病是保护大量缓存的 key 是比拟麻烦的,第二种的毛病就是每次用户申请过去都要判断缓存生效,逻辑绝对比较复杂!具体用哪种计划,大家能够依据本人的利用场景来衡量。

内存相干

MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保障 redis 中的数据都是热点数据?

redis 内存数据集大小回升到肯定大小的时候,就会实施数据淘汰策略。

Redis 的内存淘汰策略有哪些

Redis 的内存淘汰策略是指在 Redis 的用于缓存的内存不足时,怎么解决须要新写入且须要申请额定空间的数据。

1、全局的键空间选择性移除

noeviction:当内存不足以包容新写入数据时,新写入操作会报错。

allkeys-lru:当内存不足以包容新写入数据时,在键空间中,移除最近起码应用的 key。(这个是最罕用的)

allkeys-random:当内存不足以包容新写入数据时,在键空间中,随机移除某个 key。

2、设置过期工夫的键空间选择性移除

volatile-lru:当内存不足以包容新写入数据时,在设置了过期工夫的键空间中,移除最近起码应用的 key。

volatile-random:当内存不足以包容新写入数据时,在设置了过期工夫的键空间中,随机移除某个 key。

volatile-ttl:当内存不足以包容新写入数据时,在设置了过期工夫的键空间中,有更早过期工夫的 key 优先移除。

总结

Redis 的内存淘汰策略的选取并不会影响过期的 key 的解决。内存淘汰策略用于解决内存不足时的须要申请额定空间的数据;过期策略用于解决过期的缓存数据。

Redis 次要耗费什么物理资源?

内存。

Redis 的内存用完了会产生什么?

如果达到设置的下限,Redis 的写命令会返回错误信息(然而读命令还能够失常返回。)或者你能够配置内存淘汰机制,当 Redis 达到内存下限时会冲刷掉旧的内容。

Redis 如何做内存优化?

能够好好利用 Hash,list,sorted set,set 等汇合类型数据,因为通常状况下很多小的 Key-Value 能够用更紧凑的形式寄存到一起。尽可能应用散列表(hashes),散列表(是说散列表外面存储的数少)应用的内存十分小,所以你应该尽可能的将你的数据模型形象到一个散列表外面。比方你的 web 零碎中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,明码设置独自的 key,而是应该把这个用户的所有信息存储到一张散列表外面。

线程模型

Redis 线程模型

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器(file event handler)。它的组成构造为 4 局部:多个套接字、IO 多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的生产是单线程的,所以 Redis 才叫单线程模型。

1、文件事件处理器应用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并依据套接字目前执行的工作来为套接字关联不同的事件处理器。

2、当被监听的套接字筹备好执行连贯应答(accept)、读取(read)、写入(write)、敞开(close)等操作时,与操作绝对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来解决这些事件。

尽管文件事件处理器以单线程形式运行,但通过应用 I/O 多路复用程序来监听多个套接字,文件事件处理器既实现了高性能的网络通信模型,又能够很好地与 redis 服务器中其余同样以单线程形式运行的模块进行对接,这放弃了 Redis 外部单线程设计的简略性。

事务

什么是事务?

事务是一个独自的隔离操作:事务中的所有命令都会序列化、按程序地执行。事务在执行的过程中,不会被其余客户端发送来的命令申请所打断。

事务是一个原子操作:事务中的命令要么全副被执行,要么全副都不执行。

Redis 事务的概念

Redis 事务的实质是通过 MULTI、EXEC、WATCH 等一组命令的汇合。事务反对一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会依照程序串行化执行队列中的命令,其余客户端提交的命令申请不会插入到事务执行命令序列中。

总结说:redis 事务就是一次性、程序性、排他性的执行一个队列中的一系列命令。

Redis 事务的三个阶段

1、事务开始 MULTI

2、命令入队

3、事务执行 EXEC

事务执行过程中,如果服务端收到有 EXEC、DISCARD、WATCH、MULTI 之外的申请,将会把申请放入队列中排队

Redis 事务相干命令

Redis 事务性能是通过 MULTI、EXEC、DISCARD 和 WATCH 四个原语实现的

Redis 会将一个事务中的所有命令序列化,而后按程序执行。

1、redis 不反对回滚,“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”,所以 Redis 的外部能够放弃简略且疾速。

2、如果在一个事务中的命令呈现谬误,那么所有的命令都不会执行;

3、如果在一个事务中呈现运行谬误,那么正确的命令会被执行。

WATCH 命令是一个乐观锁,能够为 Redis 事务提供 check-and-set(CAS)行为。能够监控一个或多个键,一旦其中有一个键被批改(或删除),之后的事务就不会执行,监控始终继续到 EXEC 命令。

MULTI 命令用于开启一个事务,它总是返回 OK。MULTI 执行之后,客户端能够持续向服务器发送任意多条命令,这些命令不会立刻被执行,而是被放到一个队列中,当 EXEC 命令被调用时,所有队列中的命令才会被执行。

EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值 nil。

通过调用 DISCARD,客户端能够清空事务队列,并放弃执行事务,并且客户端会从事务状态中退出。

UNWATCH 命令能够勾销 watch 对所有 key 的监控。

事务管理(ACID)概述

原子性(Atomicity)

原子性是指事务是一个不可分割的工作单位,事务中的操作要么都产生,要么都不产生。

一致性(Consistency)

事务前后数据的完整性必须保持一致。

隔离性(Isolation)

多个事务并发执行时,一个事务的执行不应影响其余事务的执行。

持久性(Durability)

持久性是指一个事务一旦被提交,它对数据库中数据的扭转就是永久性的,接下来即便数据库产生故障也不应该对其有任何影响。

Redis 的事务总是具备 ACID 中的一致性和隔离性,其余个性是不反对的。当服务器运行在 AOF 长久化模式下,并且 appendfsync 选项的值为 always 时,事务也具备耐久性。

Redis 事务反对隔离性吗

Redis 是单过程程序,并且它保障在执行事务时,不会对事务进行中断,事务能够运行直到执行完所有事务队列中的命令为止。因而,Redis 的事务是总是带有隔离性的。

Redis 事务保障原子性吗,反对回滚吗

Redis 中,单条命令是原子性执行的,但事务不保障原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。

Redis 事务其余实现

基于 Lua 脚本,Redis 能够保障脚本内的命令一次性、按程序地执行,其同时也不提供事务运行谬误的回滚,执行过程中如果局部命令运行谬误,剩下的命令还是会持续运。

基于两头标记变量,通过另外的标记变量来标识事务是否执行实现,读取数据时先读取该标记变量判断是否事务执行实现。但这样会须要额定写代码实现,比拟繁琐。

集群计划

哨兵模式

哨兵的介绍

sentinel,中文名是哨兵。哨兵是 redis 集群机构中十分重要的一个组件,次要有以下性能:

集群监控:负责监控 redis master 和 slave 过程是否失常工作。

音讯告诉:如果某个 redis 实例有故障,那么哨兵负责发送音讯作为报警告诉给管理员。

故障转移:如果 master node 挂掉了,会主动转移到 slave node 上。

配置核心:如果故障转移产生了,告诉 client 客户端新的 master 地址。

哨兵用于实现 redis 集群的高可用,自身也是分布式的,作为一个哨兵集群去运行,相互协同工作。

1、故障转移时,判断一个 master node 是否宕机了,须要大部分的哨兵都批准才行,波及到了分布式选举的问题。

2、即便局部哨兵节点挂掉了,哨兵集群还是能失常工作的,因为如果一个作为高可用机制重要组成部分的故障转移零碎自身是单点的,那就很坑爹了。

哨兵的外围常识

1、哨兵至多须要 3 个实例,来保障本人的健壮性。

2、哨兵 + redis 主从的部署架构,是不保证数据零失落的,只能保障 redis 集群的高可用性。

3、对于哨兵 + redis 主从这种简单的部署架构,尽量在测试环境和生产环境,都进行短缺的测试和演练。

官网 Redis Cluster 计划(服务端路由查问)

redis 集群模式的工作原理能说一下么?在集群模式下,redis 的 key 是如何寻址的?分布式寻址都有哪些算法?理解一致性 hash 算法吗?

简介

Redis Cluster 是一种服务端 Sharding 技术,3.0 版本开始正式提供。Redis Cluster 并没有应用一致性 hash,而是采纳 slot(槽)的概念,一共分成 16384 个槽。将申请发送到任意节点,接管到申请的节点会将查问申请发送到正确的节点上执行

计划阐明

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 协定进行通信。

分布式寻址算法

hash 算法(大量缓存重建)

一致性 hash 算法(主动缓存迁徙)+ 虚构节点(主动负载平衡)

redis cluster 的 hash slot 算法

长处

1、无核心架构,反对动静扩容,对业务通明

2、具备 Sentinel 的监控和主动 Failover(故障转移)能力

3、客户端不须要连贯集群所有节点,连贯集群中任何一个可用节点即可

4、高性能,客户端直连 redis 服务,免去了 proxy 代理的损耗

毛病

1、运维也很简单,数据迁徙须要人工干预

2、只能应用 0 号数据库

3、不反对批量操作(pipeline 管道操作)

4、分布式逻辑和存储模块耦合等

基于客户端调配

简介

Redis Sharding 是 Redis Cluster 进去之前,业界广泛应用的多 Redis 实例集群办法。其次要思维是采纳哈希算法将 Redis 数据的 key 进行散列,通过 hash 函数,特定的 key 会映射到特定的 Redis 节点上。Java redis 客户端驱动 jedis,反对 Redis Sharding 性能,即 ShardedJedis 以及联合缓存池的 ShardedJedisPool

长处

劣势在于非常简单,服务端的 Redis 实例彼此独立,互相无关联,每个 Redis 实例像单服务器一样运行,非常容易线性扩大,零碎的灵活性很强

毛病

1、因为 sharding 解决放到客户端,规模进一步扩充时给运维带来挑战。

2、客户端 sharding 不反对动静增删节点。服务端 Redis 实例群拓扑构造有变动时,每个客户端都须要更新调整。连贯不能共享,当利用规模增大时,资源节约制约优化

基于代理服务器分片

简介

客户端发送申请到一个代理组件,代理解析客户端的数据,并将申请转发至正确的节点,最初将后果回复给客户端

特色

1、通明接入,业务程序不必关怀后端 Redis 实例,切换成本低

2、Proxy 的逻辑和存储的逻辑是隔离的

3、代理层多了一次转发,性能有所损耗

业界开源计划

1、Twtter 开源的 Twemproxy

2、豌豆荚开源的 Codis

Redis 主从架构

单机的 redis,可能承载的 QPS 大略就在上万到几万不等。对于缓存来说,个别都是用来撑持读高并发的。因而架构做成主从 (master-slave) 架构,一主多从,主负责写,并且将数据复制到其它的 slave 节点,从节点负责读。所有的读申请全副走从节点。这样也能够很轻松实现程度扩容,撑持读高并发。

redis replication -> 主从架构 -> 读写拆散 -> 程度扩容撑持读高并发

redis replication 的外围机制

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 能够进步读的吞吐量。

留神,如果采纳了主从架构,那么倡议必须开启 master node 的长久化,不倡议用 slave node 作为 master node 的数据热备,因为那样的话,如果你关掉 master 的长久化,可能在 master 宕机重启的时候数据是空的,而后可能一通过复制,slave node 的数据也丢了。

另外,master 的各种备份计划,也须要做。万一本地的所有文件失落了,从备份中筛选一份 rdb 去复原 master,这样能力确保启动的时候,是有数据的,即便采纳了后续解说的高可用机制,slave node 能够主动接管 master node,但也可能 sentinel 还没检测到 master failure,master node 就主动重启了,还是可能导致下面所有的 slave node 数据被清空。

redis 主从复制的外围原理

当启动一个 slave node 的时候,它会发送一个 PSYNC 命令给 master node。

如果这是 slave node 首次连贯到 master node,那么会触发一次 full resynchronization 全量复制。此时 master 会启动一个后盾线程,开始生成一份 RDB 快照文件,

同时还会将从客户端 client 新收到的所有写命令缓存在内存中。RDB 文件生成结束后,master 会将这个 RDB 发送给 slave,slave 会先写入本地磁盘,而后再从本地磁盘加载到内存中,

接着 master 会将内存中缓存的写命令发送到 slave,slave 也会同步这些数据。

slave node 如果跟 master node 有网络故障,断开了连贯,会主动重连,连贯之后 master node 仅会复制给 slave 局部短少的数据。

过程原理

1、当从库和主库建设 MS 关系后,会向主数据库发送 SYNC 命令

2、主库接管到 SYNC 命令后会开始在后盾保留快照(RDB 长久化过程),并将期间接管到的写命令缓存起来

3、当快照实现后,主 Redis 会将快照文件和所有缓存的写命令发送给从 Redis

4、从 Redis 接管到后,会载入快照文件并且执行收到的缓存的命令

5、之后,主 Redis 每当接管到写命令时就会将命令发送从 Redis,从而保证数据的统一

毛病

所有的 slave 节点数据的复制和同步都由 master 节点来解决,会照成 master 节点压力太大,应用主从从构造来解决

Redis 集群的主从复制模型是怎么的?

为了使在局部节点失败或者大部分节点无奈通信的状况下集群依然可用,所以集群应用了主从复制模型,每个节点都会有 N - 1 个复制品

生产环境中的 redis 是怎么部署的?

redis cluster,10 台机器,5 台机器部署了 redis 主实例,另外 5 台机器部署了 redis 的从实例,每个主实例挂了一个从实例,5 个节点对外提供读写服务,每个节点的读写顶峰 qps 可能能够达到每秒 5 万,5 台机器最多是 25 万读写申请 /s。

机器是什么配置?32G 内存 + 8 核 CPU + 1T 磁盘,然而调配给 redis 过程的是 10g 内存,个别线上生产环境,redis 的内存尽量不要超过 10g,超过 10g 可能会有问题。

5 台机器对外提供读写,一共有 50g 内存。

因为每个主实例都挂了一个从实例,所以是高可用的,任何一个主实例宕机,都会主动故障迁徙,redis 从实例会主动变成主实例持续提供读写服务。

你往内存里写的是什么数据?每条数据的大小是多少?商品数据,每条数据是 10kb。100 条数据是 1mb,10 万条数据是 1g。常驻内存的是 200 万条商品数据,占用内存是 20g,仅仅不到总内存的 50%。目前高峰期每秒就是 3500 左右的申请量。

其实大型的公司,会有基础架构的 team 负责缓存集群的运维。

说说 Redis 哈希槽的概念?

Redis 集群没有应用一致性 hash, 而是引入了哈希槽的概念,Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定搁置哪个槽,集群的每个节点负责一部分 hash 槽。

Redis 集群会有写操作失落吗?为什么?

Redis 并不能保证数据的强一致性,这象征这在理论中集群在特定的条件下可能会失落写操作。

Redis 集群之间是如何复制的?

异步复制

Redis 集群最大节点个数是多少?

16384 个

Redis 集群如何抉择数据库?

Redis 集群目前无奈做数据库抉择,默认在 0 数据库。

分区

Redis 是单线程的,如何进步多核 CPU 的利用率?

能够在同一个服务器部署多个 Redis 的实例,并把他们当作不同的服务器来应用,在某些时候,无论如何一个服务器是不够的,所以,如果你想应用多个 CPU,你能够考虑一下分片(shard)。

为什么要做 Redis 分区?

分区能够让 Redis 治理更大的内存,Redis 将能够应用所有机器的内存。如果没有分区,你最多只能应用一台机器的内存。分区使 Redis 的计算能力通过简略地减少计算机失去成倍晋升,Redis 的网络带宽也会随着计算机和网卡的减少而成倍增长。

你晓得有哪些 Redis 分区实现计划?

1、客户端分区就是在客户端就曾经决定数据会被存储到哪个 redis 节点或者从哪个 redis 节点读取。大多数客户端曾经实现了客户端分区。

2、代理分区 意味着客户端将申请发送给代理,而后代理决定去哪个节点写数据或者读数据。代理依据分区规定决定申请哪些 Redis 实例,而后依据 Redis 的响应后果返回给客户端。redis 和 memcached 的一种代理实现就是 Twemproxy。

3、查问路由(Query routing) 的意思是客户端随机地申请任意一个 redis 实例,而后由 Redis 将申请转发给正确的 Redis 节点。Redis Cluster 实现了一种混合模式的查问路由,但并不是间接将申请从一个 redis 节点转发到另一个 redis 节点,而是在客户端的帮忙下间接 redirected 到正确的 redis 节点。

Redis 分区有什么毛病?

1、波及多个 key 的操作通常不会被反对。例如你不能对两个汇合求交加,因为他们可能被存储到不同的 Redis 实例(实际上这种状况也有方法,然而不能间接应用交加指令)。

2、同时操作多个 key,则不能应用 Redis 事务。

3、分区应用的粒度是 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)

4、当应用分区的时候,数据处理会非常复杂,例如为了备份你必须从不同的 Redis 实例和主机同时收集 RDB / AOF 文件。

5、分区时动静扩容或缩容可能非常复杂。Redis 集群在运行时减少或者删除 Redis 节点,能做到最大水平对用户通明地数据再均衡,但其余一些客户端分区或者代理分区办法则不反对这种个性。然而,有一种预分片的技术也能够较好的解决这个问题。

分布式问题

Redis 实现分布式锁

Redis 为单过程单线程模式,采纳队列模式将并发拜访变成串行拜访,且多客户端对 Redis 的连贯并不存在竞争关系 Redis 中能够应用 SETNX 命令实现分布式锁。

当且仅当 key 不存在,将 key 的值设为 value。若给定的 key 曾经存在,则 SETNX 不做任何动作

SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。

返回值:设置胜利,返回 1。设置失败,返回 0。

应用 SETNX 实现同步锁的流程及事项如下:

应用 SETNX 命令获取锁,若返回 0(key 已存在,锁已存在)则获取失败,反之获取胜利

为了避免获取锁后程序出现异常,导致其余线程 / 过程调用 SETNX 命令总是返回 0 而进入死锁状态,须要为该 key 设置一个“正当”的过期工夫

开释锁,应用 DEL 命令将锁数据删除

如何解决 Redis 的并发竞争 Key 问题

所谓 Redis 的并发竞争 Key 的问题也就是多个零碎同时对一个 key 进行操作,然而最初执行的程序和咱们冀望的程序不同,这样也就导致了后果的不同!

举荐一种计划:分布式锁(zookeeper 和 redis 都能够实现分布式锁)。(如果不存在 Redis 的并发竞争 Key 问题,不要应用分布式锁,这样会影响性能)

基于 zookeeper 长期有序节点能够实现的分布式锁。大抵思维为:每个客户端对某个办法加锁时,在 zookeeper 上的与该办法对应的指定节点的目录下,生成一个惟一的刹时有序节点。判断是否获取锁的形式很简略,只须要判断有序节点中序号最小的一个。当开释锁的时候,只需将这个刹时节点删除即可。同时,其能够防止服务宕机导致的锁无奈开释,而产生的死锁问题。实现业务流程后,删除对应的子节点开释锁。

在实践中,当然是从以可靠性为主。所以首推 Zookeeper。

分布式 Redis 是后期做还是前期规模上来了再做好?为什么?

既然 Redis 是如此的轻量(单实例只应用 1M 内存),为避免当前的扩容,最好的方法就是一开始就启动较多实例。即使你只有一台服务器,你也能够一开始就让 Redis 以分布式的形式运行,应用分区,在同一台服务器上启动多个实例。

一开始就多设置几个 Redis 实例,例如 32 或者 64 个实例,对大多数用户来说这操作起来可能比拟麻烦,然而从短暂来看做这点就义是值得的。

这样的话,当你的数据一直增长,须要更多的 Redis 服务器时,你须要做的就是仅仅将 Redis 实例从一台服务迁徙到另外一台服务器而已(而不必思考从新分区的问题)。一旦你增加了另一台服务器,你须要将你一半的 Redis 实例从第一台机器迁徙到第二台机器。

什么是 RedLock

Redis 官网站提出了一种权威的基于 Redis 实现分布式锁的形式名叫 Redlock,此种形式比原先的单节点的办法更平安。它能够保障以下个性:

1、平安个性:互斥拜访,即永远只有一个 client 能拿到锁

2、防止死锁:最终 client 都可能拿到锁,不会呈现死锁的状况,即便本来锁住某资源的 client crash 了或者呈现了网络分区

3、容错性:只有大部分 Redis 节点存活就能够失常提供服务

缓存异样

缓存雪崩

缓存雪崩是指缓存同一时间大面积的生效,所以,前面的申请都会落到数据库上,造成数据库短时间内接受大量申请而崩掉。

解决方案

1、缓存数据的过期工夫设置随机,避免同一时间大量数据过期景象产生。

2、个别并发量不是特地多的时候,应用最多的解决方案是加锁排队。

3、给每一个缓存数据减少相应的缓存标记,记录缓存的是否生效,如果缓存标记生效,则更新数据缓存。

缓存穿透

缓存穿透是指缓存和数据库中都没有的数据,导致所有的申请都落到数据库上,造成数据库短时间内接受大量申请而崩掉。

解决方案

1、接口层减少校验,如用户鉴权校验,id 做根底校验,id<= 0 的间接拦挡;

2、从缓存取不到的数据,在数据库中也没有取到,这时也能够将 key-value 对写为 key-null,缓存无效工夫能够设置短点,如 30 秒(设置太长会导致失常状况也没法应用)。这样能够避免攻打用户重复用同一个 id 暴力攻打;

3、采纳布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个肯定不存在的数据会被这个 bitmap 拦挡掉,从而防止了对底层存储系统的查问压力。

附加

对于空间的利用达到了一种极致,那就是 Bitmap 和布隆过滤器(Bloom Filter)。

Bitmap:典型的就是哈希表

毛病是,Bitmap 对于每个元素只能记录 1bit 信息,如果还想实现额定的性能,恐怕只能靠就义更多的空间、工夫来实现了。

布隆过滤器(举荐)

就是引入了 k(k>1)k(k>1)个互相独立的哈希函数,保障在给定的空间、误判率下,实现元素判重的过程。

它的长处是空间效率和查问工夫都远远超过个别的算法,毛病是有肯定的误识别率和删除艰难。

Bloom-Filter 算法的核心思想就是利用多个不同的 Hash 函数来解决“抵触”。

Hash 存在一个抵触(碰撞)的问题,用同一个 Hash 失去的两个 URL 的值有可能雷同。为了缩小抵触,咱们能够多引入几个 Hash,如果通过其中的一个 Hash 值咱们得出某元素不在汇合中,那么该元素必定不在汇合中。只有在所有的 Hash 函数通知咱们该元素在汇合中时,能力确定该元素存在于汇合中。这便是 Bloom-Filter 的根本思维。

Bloom-Filter 个别用于在大数据量的汇合中断定某元素是否存在。

缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(个别是缓存工夫到期),这时因为并发用户特地多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力霎时增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

解决方案

1、设置热点数据永远不过期

2、加互斥锁,互斥锁

缓存预热

缓存预热就是零碎上线后,将相干的缓存数据间接加载到缓存零碎。这样就能够防止在用户申请的时候,先查询数据库,而后再将数据缓存的问题!用户间接查问当时被预热的缓存数据!

解决方案

1、间接写个缓存刷新页面,上线时手工操作一下;

2、数据量不大,能够在我的项目启动的时候主动进行加载;

3、定时刷新缓存;

缓存降级

当访问量剧增、服务呈现问题(如响应工夫慢或不响应)或非核心服务影响到外围流程的性能时,依然须要保障服务还是可用的,即便是有损服务。零碎能够依据一些要害数据进行主动降级,也能够配置开关实现人工降级。

缓存降级的最终目标是保障外围服务可用,即便是有损的。而且有些服务是无奈降级的(如退出购物车、结算)。

在进行降级之前要对系统进行梳理,看看零碎是不是能够丢卒保帅;从而梳理出哪些必须誓死爱护,哪些可降级;比方能够参考日志级别设置预案:

1、个别:比方有些服务偶然因为网络抖动或者服务正在上线而超时,能够主动降级;

2、正告:有些服务在一段时间内成功率有稳定(如在 95~100% 之间),能够主动降级或人工降级,并发送告警;

3、谬误:比方可用率低于 90%,或者数据库连接池被打爆了,或者访问量忽然猛增到零碎能接受的最大阀值,此时能够依据状况主动降级或者人工降级;

4、严重错误:比方因为非凡起因数据谬误了,此时须要紧急人工降级。

服务降级的目标,是为了避免 Redis 服务故障,导致数据库跟着一起产生雪崩问题。因而,对于不重要的缓存数据,能够采取服务降级策略,例如一个比拟常见的做法就是,Redis 呈现问题,不去数据库查问,而是间接返回默认值给用户。

热点数据和冷数据

热点数据,缓存才有价值

对于冷数据而言,大部分数据可能还没有再次拜访到就曾经被挤出内存,不仅占用内存,而且价值不大。频繁批改的数据,看状况思考应用缓存

对于热点数据,比方咱们的某 IM 产品,生日祝愿模块,当天的寿星列表,缓存当前可能读取数十万次。再举个例子,某导航产品,咱们将导航信息,缓存当前可能读取数百万次。

数据更新前至多读取两次,缓存才有意义。java 培训这个是最根本的策略,如果缓存还没有起作用就生效了,那就没有太大价值了。

那存不存在,批改频率很高,然而又不得不思考缓存的场景呢?有!比方,这个读取接口对数据库的压力很大,然而又是热点数据,这个时候就须要思考通过缓存伎俩,缩小数据库的压力,比方咱们的某助手产品的,点赞数,珍藏数,分享数等是十分典型的热点数据,然而又一直变动,此时就须要将数据同步保留到 Redis 缓存,缩小数据库压力。

缓存热点 key

缓存中的一个 Key(比方一个促销商品),在某个工夫点过期的时候,恰好在这个工夫点对这个 Key 有大量的并发申请过去,这些申请发现缓存过期个别都会从后端 DB 加载数据并回设到缓存,这个时候大并发的申请可能会霎时把后端 DB 压垮。

解决方案

对缓存查问加锁,如果 KEY 不存在,就加锁,而后查 DB 入缓存,而后解锁;其余过程如果发现有锁就期待,而后等解锁后返回数据或者进入 DB 查问

常用工具

Redis 反对的 Java 客户端都有哪些?官网举荐用哪个?

Redisson、Jedis、lettuce 等等,官网举荐应用 Redisson。

Redis 和 Redisson 有什么关系?

Redisson 是一个高级的分布式协调 Redis 客服端,能帮忙用户在分布式环境中轻松实现一些 Java 的对象 (Bloom filter, BitSet, Set, SetMultimap, ScoredSortedSet, SortedSet, Map, ConcurrentMap, List, ListMultimap, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, ReadWriteLock, AtomicLong, CountDownLatch, Publish / Subscribe, HyperLogLog)。

Jedis 与 Redisson 比照有什么优缺点?

Jedis 是 Redis 的 Java 实现的客户端,其 API 提供了比拟全面的 Redis 命令的反对;Redisson 实现了分布式和可扩大的 Java 数据结构,和 Jedis 相比,性能较为简单,不反对字符串操作,不反对排序、事务、管道、分区等 Redis 个性。Redisson 的主旨是促成使用者对 Redis 的关注拆散,从而让使用者可能将精力更集中地放在解决业务逻辑上。

其余问题

Redis 与 Memcached 的区别

两者都是非关系型内存键值数据库,当初公司个别都是用 Redis 来实现缓存,而且 Redis 本身也越来越弱小了!Redis 与 Memcached 次要有以下不同:

(1) memcached 所有的值均是简略的字符串,redis 作为其替代者,反对更为丰盛的数据类型

(2) redis 的速度比 memcached 快很多

(3) redis 能够长久化其数据

如何保障缓存与数据库双写时的数据一致性?

你只有用缓存,就可能会波及到缓存与数据库双存储双写,你只有是双写,就肯定会有数据一致性的问题,那么你如何解决一致性问题?

一般来说,就是如果你的零碎不是严格要求缓存 + 数据库必须一致性的话,缓存能够略微的跟数据库偶然有不统一的状况,最好不要做这个计划,读申请和写申请串行化,串到一个内存队列里去,这样就能够保障肯定不会呈现不统一的状况

串行化之后,就会导致系统的吞吐量会大幅度的升高,用比失常状况下多几倍的机器去撑持线上的一个申请。

还有一种形式就是可能会临时产生不统一的状况,然而产生的几率特地小,就是先更新数据库,而后再删除缓存。

Redis 常见性能问题和解决方案?

1、Master 最好不要做任何长久化工作,包含内存快照和 AOF 日志文件,特地是不要启用内存快照做长久化。

2、如果数据比拟要害,某个 Slave 开启 AOF 备份数据,策略为每秒同步一次。

3、为了主从复制的速度和连贯的稳定性,Slave 和 Master 最好在同一个局域网内。

4、尽量避免在压力较大的主库上减少从库

5、Master 调用 BGREWRITEAOF 重写 AOF 文件,AOF 在重写的时候会占大量的 CPU 和内存资源,导致服务 load 过高,呈现短暂服务暂停景象。

6、为了 Master 的稳定性,主从复制不要用图状构造,用单向链表构造更稳固,即主从关系为:Master<–Slave1<–Slave2<–Slave3…,这样的构造也不便解决单点故障问题,实现 Slave 对 Master 的替换,也即,如果 Master 挂了,能够立马启用 Slave1 做 Master,其余不变。

Redis 官网为什么不提供 Windows 版本?

因为目前 Linux 版本曾经相当稳固,而且用户量很大,无需开发 windows 版本,反而会带来兼容性等问题。

一个字符串类型的值能存储最大容量是多少?

512M

Redis 如何做大量数据插入?

Redis2.6 开始 redis-cli 反对一种新的被称之为 pipe mode 的新模式用于执行大量数据插入工作。

如果 Redis 外面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀结尾的,如果将它们全副找进去?

应用 keys 指令能够扫出指定模式的 key 列表。

如果这个 redis 正在给线上的业务提供服务,那应用 keys 指令会有什么问题?

这个时候你要答复 redis 要害的一个个性:redis 的单线程的。keys 指令会导致线程阻塞一段时间,线上服务会进展,直到指令执行结束,服务能力复原。这个时候能够应用 scan 指令,scan 指令能够无阻塞的提取出指定模式的 key 列表,然而会有肯定的反复概率,在客户端做一次去重就能够了,然而整体所破费的工夫会比间接用 keys 指令长。

应用 Redis 做过异步队列吗,是如何实现的?

应用 list 类型保留数据信息,rpush 生产音讯,lpop 生产音讯,当 lpop 没有音讯时,能够 sleep 一段时间,而后再查看有没有信息,如果不想 sleep 的话,能够应用 blpop, 在没有信息的时候,会始终阻塞,直到信息的到来。redis 能够通过 pub/sub 主题订阅模式实现一个生产者,多个消费者,当然也存在肯定的毛病,当消费者下线时,生产的音讯会失落。

Redis 如何实现延时队列?

应用 sortedset,应用工夫戳做 score, 音讯内容作为 key, 调用 zadd 来生产音讯,消费者应用 zrangbyscore 获取 n 秒之前的数据做轮询解决。

Redis 回收过程如何工作的?

1、一个客户端运行了新的命令,增加了新的数据。

2、Redis 查看内存应用状况,如果大于 maxmemory 的限度,则依据设定好的策略进行回收。

3、一个新的命令被执行,等等。

4、所以咱们一直地穿梭内存限度的边界,通过一直达到边界而后一直地回收回到边界以下。

如果一个命令的后果导致大量内存被应用(例如很大的汇合的交加保留到一个新的键),不必多久内存限度就会被这个内存使用量超过。

Redis 回收应用的是什么算法?

LRU 算法

正文完
 0