网站 多个ip 备案,做网站的框架,长春百度网站排名优化,武夷山网站建设目录#xff1a;
Redis是什么#xff1f;Redis优缺点#xff1f;Redis为什么这么快#xff1f;讲讲Redis的线程模型#xff1f;Redis应用场景有哪些#xff1f;Memcached和Redis的区别#xff1f;为什么要用 Redis 而不用 map/guava 做缓存?Redis 数据类型有哪些…目录
Redis是什么Redis优缺点Redis为什么这么快讲讲Redis的线程模型Redis应用场景有哪些Memcached和Redis的区别为什么要用 Redis 而不用 map/guava 做缓存?Redis 数据类型有哪些SortedSet和List异同点Redis的内存用完了会怎样Redis如何做内存优化keys命令存在的问题Redis事务Redis事务支持隔离性吗Redis事务保证原子性吗支持回滚吗持久化机制RDB和AOF如何选择Redis有哪些部署方案主从架构哨兵SentinelRedis cluster过期键的删除策略内存淘汰策略有哪些如何保证缓存与数据库双写时的数据一致性缓存穿透缓存雪崩缓存击穿缓存预热缓存降级Redis 怎么实现消息队列Redis 怎么实现延时队列pipeline的作用LUA脚本什么是RedLockRedis大key怎么处理Redis常见性能问题和解决方案说说为什么Redis过期了为什么内存没释放Redis突然变慢有哪些原因为什么 Redis 集群的最大槽数是 16384 个
Redis连环40问绝对够全
Redis是什么
RedisRemote Dictionary Server是一个使用 C 语言编写的高性能非关系型的键值对数据库。与传统数据库不同的是Redis 的数据是存在内存中的所以读写速度非常快被广泛应用于缓存方向。Redis可以将数据写入磁盘中保证了数据的安全不丢失而且Redis的操作是原子性的。
Redis优缺点
优点
基于内存操作内存读写速度快。支持多种数据类型包括String、Hash、List、Set、ZSet等。支持持久化。Redis支持RDB和AOF两种持久化机制持久化功能可以有效地避免数据丢失问题。支持事务。Redis的所有操作都是原子性的同时Redis还支持对几个操作合并后的原子性执行。支持主从复制。主节点会自动将数据同步到从节点可以进行读写分离。Redis命令的处理是单线程的。Redis6.0引入了多线程需要注意的是多线程用于处理网络数据的读写和协议解析Redis命令执行还是单线程的。
缺点
对结构化查询的支持比较差。数据库容量受到物理内存的限制不适合用作海量数据的高性能读写因此Redis适合的场景主要局限在较小数据量的操作。Redis 较难支持在线扩容在集群容量达到上限时在线扩容会变得很复杂。 内容摘自我的学习网站topjavaer.cn Redis为什么这么快
基于内存Redis是使用内存存储没有磁盘IO上的开销。数据存在内存中读写速度快。IO多路复用模型Redis 采用 IO 多路复用技术。Redis 使用单线程来轮询描述符将数据库的操作都转换成了事件不在网络I/O上浪费过多的时间。高效的数据结构Redis 每种数据类型底层都做了优化目的就是为了追求更快的速度。
既然Redis那么快为什么不用它做主数据库只用它做缓存
虽然Redis非常快但它也有一些局限性不能完全替代主数据库。有以下原因
**事务处理**Redis只支持简单的事务处理对于复杂的事务无能为力比如跨多个键的事务处理。
**数据持久化**Redis是内存数据库数据存储在内存中如果服务器崩溃或断电数据可能丢失。虽然Redis提供了数据持久化机制但有一些限制。
**数据处理**Redis只支持一些简单的数据结构比如字符串、列表、哈希表等。如果需要处理复杂的数据结构比如关系型数据库中的表那么Redis可能不是一个好的选择。
**数据安全**Redis没有提供像主数据库那样的安全机制比如用户认证、访问控制等等。
因此虽然Redis非常快但它还有一些限制不能完全替代主数据库。所以使用Redis作为缓存是一种很好的方式可以提高应用程序的性能并减少数据库的负载。
讲讲Redis的线程模型
Redis基于Reactor模式开发了网络事件处理器这个处理器被称为文件事件处理器。它的组成结构为4部分多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的所以Redis才叫单线程模型。
文件事件处理器使用I/O多路复用multiplexing程序来同时监听多个套接字 并根据套接字目前执行的任务来为套接字关联不同的事件处理器。当被监听的套接字准备好执行连接accept、read、write、close等操作时 与操作相对应的文件事件就会产生 这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
虽然文件事件处理器以单线程方式运行 但通过使用 I/O 多路复用程序来监听多个套接字 文件事件处理器既实现了高性能的网络通信模型 又可以很好地与 redis 服务器中其他同样以单线程方式运行的模块进行对接 这保持了 Redis 内部单线程设计的简单性。
Redis应用场景有哪些
缓存热点数据缓解数据库的压力。利用 Redis 原子性的自增操作可以实现计数器的功能比如统计用户点赞数、用户访问数等。分布式锁。在分布式场景下无法使用单机环境下的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的 SETNX 命令实现分布式锁除此之外还可以使用官方提供的 RedLock 分布式锁实现。简单的消息队列可以使用Redis自身的发布/订阅模式或者List来实现简单的消息队列实现异步操作。限速器可用于限制某个用户访问某个接口的频率比如秒杀场景用于防止用户快速点击带来不必要的压力。好友关系利用集合的一些命令比如交集、并集、差集等实现共同好友、共同爱好之类的功能。
Memcached和Redis的区别
MemCached 数据结构单一仅用来缓存数据而 Redis 支持多种数据类型。MemCached 不支持数据持久化重启后数据会消失。Redis 支持数据持久化。Redis 提供主从同步机制和 cluster 集群部署能力能够提供高可用服务。Memcached 没有提供原生的集群模式需要依靠客户端实现往集群中分片写入数据。Redis 的速度比 Memcached 快很多。Redis 使用单线程的多路 IO 复用模型Memcached使用多线程的非阻塞 IO 模型。Redis6.0引入了多线程IO用来处理网络数据的读写和协议解析但是命令的执行仍然是单线程value 值大小不同Redis 最大可以达到 512Mmemcache 只有 1mb。
为什么要用 Redis 而不用 map/guava 做缓存?
使用自带的 map 或者 guava 实现的是本地缓存最主要的特点是轻量以及快速生命周期随着 jvm 的销毁而结束并且在多实例的情况下每个实例都需要各自保存一份缓存缓存不具有一致性。
使用 redis 或 memcached 之类的称为分布式缓存在多实例的情况下各实例共用一份缓存数据缓存具有一致性。 本文已经收录到大彬精心整理的大厂面试手册包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等高频面试题非常实用有小伙伴靠着这份手册拿过字节offer~ 需要的小伙伴可以自行下载 http://mp.weixin.qq.com/s?__bizMzg2OTY1NzY0MQmid2247485445idx1sn1c6e224b9bb3da457f5ee03894493dbcchksmce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd Redis 数据类型有哪些
基本数据类型
1、String最常用的一种数据类型String类型的值可以是字符串、数字或者二进制但值最大不能超过512MB。
2、HashHash 是一个键值对集合。
3、Set无序去重的集合。Set 提供了交集、并集等方法对于实现共同好友、共同关注等功能特别方便。
4、List有序可重复的集合底层是依赖双向链表实现的。
5、SortedSet有序Set。内部维护了一个score的参数来实现。适用于排行榜和带权重的消息队列等场景。
特殊的数据类型
1、Bitmap位图可以认为是一个以位为单位数组数组中的每个单元只能存0或者1数组的下标在 Bitmap 中叫做偏移量。Bitmap的长度与集合中元素个数无关而是与基数的上限有关。
2、Hyperloglog。HyperLogLog 是用来做基数统计的算法其优点是在输入元素的数量或者体积非常非常大时计算基数所需的空间总是固定的、并且是很小的。典型的使用场景是统计独立访客。
3、Geospatial 主要用于存储地理位置信息并对存储的信息进行操作适用场景如定位、附近的人等。
SortedSet和List异同点
相同点
都是有序的都可以获得某个范围内的元素。
不同点
列表基于链表实现获取两端元素速度快访问中间元素速度慢有序集合基于散列表和跳跃表实现访问中间元素时间复杂度是OlogN列表不能简单的调整某个元素的位置有序列表可以更改元素的分数有序集合更耗内存。
Redis的内存用完了会怎样
如果达到设置的上限Redis的写命令会返回错误信息但是读命令还可以正常返回。
也可以配置内存淘汰机制当Redis达到内存上限时会冲刷掉旧的内容。
Redis如何做内存优化
可以好好利用Hash,list,sorted set,set等集合类型数据因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。尽可能使用散列表hashes散列表是说散列表里面存储的数少使用的内存非常小所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的web系统中有一个用户对象不要为这个用户的名称姓氏邮箱密码设置单独的key而是应该把这个用户的所有信息存储到一张散列表里面。
keys命令存在的问题
redis的单线程的。keys指令会导致线程阻塞一段时间直到执行完毕服务才能恢复。scan采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题每次scan命令的时间复杂度是O(1)但是要真正实现keys的功能需要执行多次scan。
scan的缺点在scan的过程中如果有键的变化增加、删除、修改遍历过程可能会有以下问题新增的键可能没有遍历到遍历出了重复的键等情况也就是说scan并不能保证完整的遍历出来所有的键。
Redis事务
事务的原理是将一个事务范围内的若干命令发送给Redis然后再让Redis依次执行这些命令。
事务的生命周期 使用MULTI开启一个事务 在开启事务的时候每次操作的命令将会被插入到一个队列中同时这个命令并不会被真的执行 EXEC命令进行提交事务 一个事务范围内某个命令出错不会影响其他命令的执行不保证原子性
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) OKWATCH命令
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)比如上面的代码中
watch name开启了对name这个key的监控修改name的值开启事务a在事务a中设置了name和gender的值使用EXEC命令进提交事务使用命令get gender发现不存在即事务a没有执行
使用UNWATCH可以取消WATCH命令对key的监控所有监控锁将会被取消。
Redis事务支持隔离性吗
Redis 是单进程程序并且它保证在执行事务时不会对事务进行中断事务可以运行直到执行完所有事务队列中的命令为止。因此Redis 的事务是总是带有隔离性的。
Redis事务保证原子性吗支持回滚吗
Redis单条命令是原子性执行的但事务不保证原子性且没有回滚。事务中任意命令执行失败其余的命令仍会被执行。
持久化机制
持久化就是把内存的数据写到磁盘中防止服务宕机导致内存数据丢失。
Redis支持两种方式的持久化一种是RDB的方式一种是AOF的方式。前者会根据指定的规则定时将内存中的数据存储在硬盘上而后者在每次执行完命令后将命令记录下来。一般将两者结合使用。 分享一本谷歌大佬撰写的算法手册整整 300 道 LeetCode 题目并且都是最优解非常强这本手册帮助不少朋友加入大厂大家加油 Github疯传谷歌大佬撰写的算法手册开源了 RDB方式
RDB是 Redis 默认的持久化方案。RDB持久化时会将内存中的数据写入到磁盘中在指定目录下生成一个dump.rdb文件。Redis 重启会加载dump.rdb文件恢复数据。
bgsave是主流的触发 RDB 持久化的方式执行过程如下 执行BGSAVE命令Redis 父进程判断当前是否存在正在执行的子进程如果存在BGSAVE命令直接返回。父进程执行fork操作创建子进程fork操作过程中父进程会阻塞。父进程fork完成后父进程继续接收并处理客户端的请求而子进程开始将内存中的数据写进硬盘的临时文件当子进程写完所有数据后会用该临时文件替换旧的 RDB 文件。
Redis启动时会读取RDB快照文件将数据从硬盘载入内存。通过 RDB 方式的持久化一旦Redis异常退出就会丢失最近一次持久化以后更改的数据。
触发 RDB 持久化的方式 手动触发用户执行SAVE或BGSAVE命令。SAVE命令执行快照的过程会阻塞所有客户端的请求应避免在生产环境使用此命令。BGSAVE命令可以在后台异步进行快照操作快照的同时服务器还可以继续响应客户端的请求因此需要手动执行快照时推荐使用BGSAVE命令。 被动触发 根据配置规则进行自动快照如SAVE 100 10100秒内至少有10个键被修改则进行快照。如果从节点执行全量复制操作主节点会自动执行BGSAVE生成 RDB 文件并发送给从节点。默认情况下执行shutdown命令时如果没有开启 AOF 持久化功能则自动执行·BGSAVE·。
优点
Redis 加载 RDB 恢复数据远远快于 AOF 的方式。使用单独子进程来进行持久化主进程不会进行任何 IO 操作保证了 Redis 的高性能。
缺点
RDB方式数据无法做到实时持久化。因为BGSAVE每次运行都要执行fork操作创建子进程属于重量级操作频繁执行成本比较高。RDB 文件使用特定二进制格式保存Redis 版本升级过程中有多个格式的 RDB 版本存在老版本 Redis 无法兼容新版 RDB 格式的问题。
AOF方式
AOFappend only file持久化以独立日志的方式记录每次写命令Redis重启时会重新执行AOF文件中的命令达到恢复数据的目的。AOF的主要作用是解决了数据持久化的实时性AOF 是Redis持久化的主流方式。
默认情况下Redis没有开启AOF方式的持久化可以通过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 每秒执行一次fsync操作如果Redis进程挂掉最多丢失1秒的数据。AOF以append-only的模式写入所以没有磁盘寻址的开销写入性能非常高。
缺点
对于同一份文件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能够承载的 QPS 大概就在上万到几万不等。对于缓存来说一般都是用来支撑读高并发的。因此架构做成主从(master-slave)架构一主多从主负责写并且将数据复制到其它的 slave 节点从节点负责读。所有的读请求全部走从节点。这样也可以很轻松实现水平扩容支撑读高并发。
Redis的复制功能是支持多个数据库之间的数据同步。主数据库可以进行读写操作当主数据库的数据发生变化时会自动将数据同步到从数据库。从数据库一般是只读的它会接收主数据库同步过来的数据。一个主数据库可以有多个从数据库而一个从数据库只能有一个主数据库。
主从复制的原理
当启动一个从节点时它会发送一个 PSYNC 命令给主节点如果是从节点初次连接到主节点那么会触发一次全量复制。此时主节点会启动一个后台线程开始生成一份 RDB 快照文件同时还会将从客户端 client 新收到的所有写命令缓存在内存中。RDB 文件生成完毕后 主节点会将RDB文件发送给从节点从节点会先将RDB文件写入本地磁盘然后再从本地磁盘加载到内存中接着主节点会将内存中缓存的写命令发送到从节点从节点同步这些数据如果从节点跟主节点之间网络出现故障连接断开了会自动重连连接之后主节点仅会将部分缺失的数据同步给从节点。
哨兵Sentinel
主从复制存在不能自动故障转移、达不到高可用的问题。哨兵模式解决了这些问题。通过哨兵机制可以自动切换主从节点。
客户端连接Redis的时候先连接哨兵哨兵会告诉客户端Redis主节点的地址然后客户端连接上Redis并进行后续的操作。当主节点宕机的时候哨兵监测到主节点宕机会重新推选出某个表现良好的从节点成为新的主节点然后通过发布订阅模式通知其他的从服务器让它们切换主机。 工作原理
每个Sentinel以每秒钟一次的频率向它所知道的MasterSlave以及其他 Sentinel 实例发送一个 PING命令。如果一个实例距离最后一次有效回复 PING 命令的时间超过指定值 则这个实例会被 Sentine 标记为主观下线。如果一个Master被标记为主观下线则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master是否真正进入主观下线状态。当有足够数量的 Sentinel大于等于配置文件指定值在指定的时间范围内确认Master的确进入了主观下线状态 则Master会被标记为客观下线 。若没有足够数量的 Sentinel 同意 Master 已经下线 Master 的客观下线状态就会被解除。 若 Master重新向 Sentinel 的 PING 命令返回有效回复 Master 的主观下线状态就会被移除。哨兵节点会选举出哨兵 leader负责故障转移的工作。哨兵 leader 会推选出某个表现良好的从节点成为新的主节点然后通知其他从节点更新主节点信息。
Redis cluster
哨兵模式解决了主从复制不能自动故障转移、达不到高可用的问题但还是存在主节点的写能力、容量受限于单机配置的问题。而cluster模式实现了Redis的分布式存储每个节点存储不同的内容解决主节点的写能力、容量受限于单机配置的问题。
Redis cluster集群节点最小配置6个节点以上3主3从其中主节点提供读写操作从节点作为备用节点不提供请求只作为故障转移使用。
Redis cluster采用虚拟槽分区所有的键根据哈希函数映射到016383个整数槽内每个节点负责维护一部分槽以及槽所映射的键值数据。 工作原理
通过哈希的方式将数据分片每个节点均分存储一定哈希槽(哈希值)区间的数据默认分配了16384 个槽位每份数据分片会存储在多个互为主从的多节点上数据写入先写主节点再同步到从节点(支持配置为阻塞同步)同一分片多个节点间的数据不保持一致性读取数据时当客户端操作的key没有分配在该节点上时redis会返回转向指令指向正确的节点扩容时时需要需要把旧节点的数据迁移一部分到新节点
在 redis cluster 架构下每个 redis 要放开两个端口号比如一个是 6379另外一个就是 加1w 的端口号比如 16379。
16379 端口号是用来进行节点间通信的也就是 cluster bus 的东西cluster bus 的通信用来进行故障检测、配置更新、故障转移授权。cluster bus 用了另外一种二进制的协议gossip 协议用于节点间进行高效的数据交换占用更少的网络带宽和处理时间。
优点
无中心架构支持动态扩容数据按照slot存储分布在多个节点节点间数据共享可动态调整数据分布高可用性。部分节点不可用时集群仍可用。集群模式能够实现自动故障转移failover节点之间通过gossip协议交换状态信息用投票机制完成Slave到Master的角色转换。
缺点
不支持批量操作pipeline。数据通过异步复制不保证数据的强一致性。事务操作支持有限只支持多key在同一节点上的事务操作当多个key分布于不同的节点上时无法使用事务功能。key作为数据分区的最小粒度不能将一个很大的键值对象如hash、list等映射到不同的节点。不支持多数据库空间单机下的Redis可以支持到16个数据库集群模式下只能使用1个数据库空间。只能使用0号数据库。
哈希分区算法有哪些
节点取余分区。使用特定的数据如Redis的键或用户ID对节点数量N取余hashkey%N计算出哈希值用来决定数据映射到哪一个节点上。 优点是简单性。扩容时通常采用翻倍扩容避免数据映射全部被打乱导致全量迁移的情况。
一致性哈希分区。为系统中每个节点分配一个token范围一般在0~232这些token构成一个哈希环。数据读写执行节点查找操作时先根据key计算hash值然后顺时针找到第一个大于等于该哈希值的token节点。 这种方式相比节点取余最大的好处在于加入和删除节点只影响哈希环中相邻的节点对其他节点无影响。
虚拟槽分区所有的键根据哈希函数映射到0~16383整数槽内计算公式slotCRC16key16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。Redis Cluser采用虚拟槽分区算法。
过期键的删除策略
1、被动删除。在访问key时如果发现key已经过期那么会将key删除。
2、主动删除。定时清理key每次清理会依次遍历所有DB从db随机取出20个key如果过期就删除如果其中有5个key过期那么就继续对这个db进行清理否则开始清理下一个db。
3、内存不够时清理。Redis有最大内存的限制通过maxmemory参数可以设置最大内存当使用的内存超过了设置的最大内存就要进行内存释放 在进行内存释放的时候会按照配置的淘汰策略清理内存。
内存淘汰策略有哪些
当Redis的内存超过最大允许的内存之后Redis 会触发内存淘汰策略删除一些不常用的数据以保证Redis服务器正常运行。
Redisv4.0前提供 6 种数据淘汰策略
volatile-lruLRULeast Recently Used最近使用。利用LRU算法移除设置了过期时间的keyallkeys-lru当内存不足以容纳新写入数据时从数据集中移除最近最少使用的keyvolatile-ttl从已设置过期时间的数据集中挑选将要过期的数据淘汰volatile-random从已设置过期时间的数据集中任意选择数据淘汰allkeys-random从数据集中任意选择数据淘汰no-eviction禁止删除数据当内存不足以容纳新写入数据时新写入操作会报错
Redisv4.0后增加以下两种
volatile-lfuLFULeast Frequently Used最少使用从已设置过期时间的数据集中挑选最不经常使用的数据淘汰。allkeys-lfu当内存不足以容纳新写入数据时从数据集中移除最不经常使用的key。
内存淘汰策略可以通过配置文件来修改相应的配置项是maxmemory-policy默认配置是noeviction。
如何保证缓存与数据库双写时的数据一致性
1、先删除缓存再更新数据库
进行更新操作时先删除缓存然后更新数据库后续的请求再次读取时会从数据库读取后再将新数据更新到缓存。
存在的问题删除缓存数据之后更新数据库完成之前这个时间段内如果有新的读请求过来就会从数据库读取旧数据重新写到缓存中再次造成不一致并且后续读的都是旧数据。
2、先更新数据库再删除缓存
进行更新操作时先更新MySQL成功之后删除缓存后续读取请求时再将新数据回写缓存。
存在的问题更新MySQL和删除缓存这段时间内请求读取的还是缓存的旧数据不过等数据库更新完成就会恢复一致影响相对比较小。
3、异步更新缓存
数据库的更新操作完成后不直接操作缓存而是把这个操作命令封装成消息扔到消息队列中然后由Redis自己去消费更新数据消息队列可以保证数据操作顺序一致性确保缓存系统的数据正常。
以上几个方案都不完美需要根据业务需求评估哪种方案影响较小然后选择相应的方案。
缓存常见问题
缓存穿透
缓存穿透是指查询一个不存在的数据由于缓存是不命中时被动写的如果从DB查不到数据则不写入缓存这将导致这个不存在的数据每次请求都要到DB去查询失去了缓存的意义。在流量大时可能DB就挂掉了。
怎么解决
缓存空值不会查数据库。采用布隆过滤器将所有可能存在的数据哈希到一个足够大的bitmap中查询不存在的数据会被这个bitmap拦截掉从而避免了对DB的查询压力。
布隆过滤器的原理当一个元素被加入集合时通过K个哈希函数将这个元素映射成一个位数组中的K个点把它们置为1。查询时将元素通过哈希函数映射之后会得到k个点如果这些点有任何一个0则被检元素一定不在直接返回如果都是1则查询元素很可能存在就会去查询Redis和数据库。
布隆过滤器一般用于在大数据量的集合中判定某元素是否存在。
缓存雪崩
缓存雪崩是指在我们设置缓存时采用了相同的过期时间导致缓存在某一时刻同时失效请求全部转发到DBDB瞬时压力过重挂掉。
解决方法
在原有的失效时间基础上增加一个随机值使得过期时间分散一些。这样每一个缓存的过期时间的重复率就会降低就很难引发集体失效的事件。加锁排队可以起到缓冲的作用防止大量的请求同时操作数据库但它的缺点是增加了系统的响应时间降低了系统的吞吐量牺牲了一部分用户体验。当缓存未查询到时对要请求的 key 进行加锁只允许一个线程去数据库中查其他线程等候排队。设置二级缓存。二级缓存指的是除了 Redis 本身的缓存再设置一层缓存当 Redis 失效之后先去查询二级缓存。例如可以设置一个本地缓存在 Redis 缓存失效的时候先去查询本地缓存而非查询数据库。
缓存击穿
缓存击穿大量的请求同时查询一个 key 时此时这个 key 正好失效了就会导致大量的请求都落到数据库。缓存击穿是查询缓存中失效的 key而缓存穿透是查询不存在的 key。
解决方法
1、加互斥锁。在并发的多个请求中只有第一个请求线程能拿到锁并执行数据库查询操作其他的线程拿不到锁就阻塞等着等到第一个线程将数据写入缓存后直接走缓存。可以使用Redis分布式锁实现代码如下
public String get(String key) {String value redis.get(key);if (value null) { //缓存值过期String unique_key systemId : key;//设置30s的超时if (redis.set(unique_key, 1, NX, PX, 30000) 1) { //设置成功value db.get(key);redis.set(key, value, expire_secs);redis.del(unique_key);} else { //其他线程已经到数据库取值并回写到缓存了可以重试获取缓存值sleep(50);get(key); //重试}} else {return value;}
}2、热点数据不过期。直接将缓存设置为不过期然后由定时任务去异步加载数据更新缓存。这种方式适用于比较极端的场景例如流量特别特别大的场景使用时需要考虑业务能接受数据不一致的时间还有就是异常情况的处理保证缓存可以定时刷新。
缓存预热
缓存预热就是系统上线后将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候先查询数据库然后再将数据缓存的问题用户直接查询事先被预热的缓存数据
解决方案
直接写个缓存刷新页面上线时手工操作一下数据量不大可以在项目启动的时候自动进行加载定时刷新缓存
缓存降级
当访问量剧增、服务出现问题如响应时间慢或不响应或非核心服务影响到核心流程的性能时仍然需要保证服务还是可用的即使是有损服务。系统可以根据一些关键数据进行自动降级也可以配置开关实现人工降级。
缓存降级的最终目的是保证核心服务可用即使是有损的。而且有些服务是无法降级的如加入购物车、结算。
在进行降级之前要对系统进行梳理看看系统是不是可以丢卒保帅从而梳理出哪些必须誓死保护哪些可降级比如可以参考日志级别设置预案
一般比如有些服务偶尔因为网络抖动或者服务正在上线而超时可以自动降级警告有些服务在一段时间内成功率有波动如在95~100%之间可以自动降级或人工降级并发送告警错误比如可用率低于90%或者数据库连接池被打爆了或者访问量突然猛增到系统能承受的最大阀值此时可以根据情况自动降级或者人工降级严重错误比如因为特殊原因数据错误了此时需要紧急人工降级。
服务降级的目的是为了防止Redis服务故障导致数据库跟着一起发生雪崩问题。因此对于不重要的缓存数据可以采取服务降级策略例如一个比较常见的做法就是Redis出现问题不去数据库查询而是直接返回默认值给用户。
Redis 怎么实现消息队列
使用list类型保存数据信息rpush生产消息lpop消费消息当lpop没有消息时可以sleep一段时间然后再检查有没有信息如果不想sleep的话可以使用blpop, 在没有信息的时候会一直阻塞直到信息的到来。
BLPOP queue 0 //0表示不限制等待时间BLPOP和LPOP命令相似唯一的区别就是当列表没有元素时BLPOP命令会一直阻塞连接直到有新元素加入。 redis可以通过pub/sub主题订阅模式实现一个生产者多个消费者当然也存在一定的缺点当消费者下线时生产的消息会丢失。
PUBLISH channel1 hi
SUBSCRIBE channel1
UNSUBSCRIBE channel1 //退订通过SUBSCRIBE命令订阅的频道。PSUBSCRIBE channel?* 按照规则订阅。 PUNSUBSCRIBE channel?* 退订通过PSUBSCRIBE命令按照某种规则订阅的频道。其中订阅规则要进行严格的字符串匹配PUNSUBSCRIBE *无法退订channel?*规则。 Redis 怎么实现延时队列
使用sortedset拿时间戳作为score消息内容作为key调用zadd来生产消息消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。
pipeline的作用
redis客户端执行一条命令分4个过程 发送命令、命令排队、命令执行、返回结果。使用pipeline可以批量请求批量返回结果执行速度比逐条执行要快。
使用pipeline组装的命令个数不能太多不然数据量过大增加客户端的等待时间还可能造成网络阻塞可以将大量命令的拆分多个小的pipeline命令完成。
原生批命令mset和mget与pipeline对比 原生批命令是原子性pipeline是非原子性。pipeline命令中途异常退出之前执行成功的命令不会回滚。 原生批命令只有一个命令但pipeline支持多命令。
LUA脚本
Redis 通过 LUA 脚本创建具有原子性的命令 当lua脚本命令正在运行的时候不会有其他脚本或 Redis 命令被执行实现组合命令的原子操作。
在Redis中执行Lua脚本有两种方法eval和evalsha。eval命令使用内置的 Lua 解释器对 Lua 脚本进行求值。
//第一个参数是lua脚本第二个参数是键名参数个数剩下的是键名参数和附加参数eval return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]} 2 key1 key2 first second
1) key1
2) key2
3) first
4) secondlua脚本作用
1、Lua脚本在Redis中是原子执行的执行过程中间不会插入其他命令。
2、Lua脚本可以将多条命令一次性打包有效地减少网络开销。
应用场景
举例限制接口访问频率。
在Redis维护一个接口访问次数的键值对key是接口名称value是访问次数。每次访问接口时会执行以下操作
通过aop拦截接口的请求对接口请求进行计数每次进来一个请求相应的接口访问次数count加1存入redis。如果是第一次请求则会设置count1并设置过期时间。因为这里set()和expire()组合操作不是原子操作所以引入lua脚本实现原子操作避免并发访问问题。如果给定时间范围内超过最大访问次数则会抛出异常。
private String buildLuaScript() {return local c \nc redis.call(get,KEYS[1]) \nif c and tonumber(c) tonumber(ARGV[1]) then \nreturn c; \nend \nc redis.call(incr,KEYS[1]) \nif tonumber(c) 1 then \nredis.call(expire,KEYS[1],ARGV[2]) \nend \nreturn c;;
}String luaScript buildLuaScript();
RedisScriptNumber redisScript new DefaultRedisScript(luaScript, Number.class);
Number count redisTemplate.execute(redisScript, keys, limit.count(), limit.period());PS这种接口限流的实现方式比较简单问题也比较多一般不会使用接口限流用的比较多的是令牌桶算法和漏桶算法。
什么是RedLock
Redis 官方站提出了一种权威的基于 Redis 实现分布式锁的方式名叫 Redlock此种方式比原先的单节点的方法更安全。它可以保证以下特性
安全特性互斥访问即永远只有一个 client 能拿到锁避免死锁最终 client 都可能拿到锁不会出现死锁的情况即使原本锁住某资源的 client 挂掉了容错性只要大部分 Redis 节点存活就可以正常提供服务
Redis大key怎么处理
通常我们会将含有较大数据或含有大量成员、列表数的Key称之为大Key。
以下是对各个数据类型大key的描述
value是STRING类型它的值超过5MBvalue是ZSET、Hash、List、Set等集合类型时它的成员数量超过1w个
上述的定义并不绝对主要是根据value的成员数量和大小来确定根据业务场景确定标准。
怎么处理
当vaule是string时可以使用序列化、压缩算法将key的大小控制在合理范围内但是序列化和反序列化都会带来更多时间上的消耗。或者将key进行拆分一个大key分为不同的部分记录每个部分的key使用multiget等操作实现事务读取。当value是list/set等集合类型时根据预估的数据规模来进行分片不同的元素计算后分到不同的片。
Redis常见性能问题和解决方案
Master最好不要做任何持久化工作包括内存快照和AOF日志文件特别是不要启用内存快照做持久化。如果数据比较关键某个Slave开启AOF备份数据策略为每秒同步一次。为了主从复制的速度和连接的稳定性Slave和Master最好在同一个局域网内。尽量避免在压力较大的主库上增加从库Master调用BGREWRITEAOF重写AOF文件AOF在重写的时候会占大量的CPU和内存资源导致服务load过高出现短暂服务暂停现象。为了Master的稳定性主从复制不要用图状结构用单向链表结构更稳定即主从关系为Master–Slave1–Slave2–Slave3…这样的结构也方便解决单点故障问题实现Slave对Master的替换也即如果Master挂了可以立马启用Slave1做Master其他不变。
说说为什么Redis过期了为什么内存没释放
第一种情况可能是覆盖之前的key导致key过期时间发生了改变。
当一个key在Redis中已经存在了但是由于一些误操作使得key过期时间发生了改变从而导致这个key在应该过期的时间内并没有过期从而造成内存的占用。
第二种情况是Redis过期key的处理策略导致内存没释放。
一般Redis对过期key的处理策略有两种惰性删除和定时删除。
先说惰性删除的情况
当一个key已经确定设置了xx秒过期同时中间也没有修改它xx秒之后它确实已经过期了但是惰性删除的策略它并不会马上删除这个key而是当再次读写这个key时它才会去检查是否过期如果过期了就会删除这个key。也就是说惰性删除策略下就算key过期了也不会立刻释放内容要等到下一次读写这个key才会删除key。
而定时删除会在一定时间内主动淘汰一部分已经过期的数据默认的时间是每100ms过期一次。因为定时删除策略每次只会淘汰一部分过期key而不是所有的过期key如果redis中数据比较多的话要是一次性全量删除对服务器的压力比较大每一次只挑一批进行删除所以很可能出现部分已经过期的key并没有及时的被清理掉从而导致内存没有即时被释放。
Redis突然变慢有哪些原因 存在bigkey。如果Redis实例中存储了 bigkey那么在淘汰删除 bigkey 释放内存时也会耗时比较久。应该避免存储 bigkey降低释放内存的耗时。 如果Redis 实例设置了内存上限 maxmemory有可能导致 Redis 变慢。当 Redis 内存达到 maxmemory 后每次写入新的数据之前Redis 必须先从实例中踢出一部分数据让整个实例的内存维持在 maxmemory 之下然后才能把新数据写进来。 开启了内存大页。当 Redis 在执行后台 RDB 和 AOF rewrite 时采用 fork 子进程的方式来处理。但主进程 fork 子进程后此时的主进程依旧是可以接收写请求的而进来的写请求会采用 Copy On Write写时复制的方式操作内存数据。 什么是写时复制 这样做的好处是父进程有任何写操作并不会影响子进程的数据持久化。 不过主进程在拷贝内存数据时会涉及到新内存的申请如果此时操作系统开启了内存大页那么在此期间客户端即便只修改 10B 的数据Redis 在申请内存时也会以 2MB 为单位向操作系统申请申请内存的耗时变长进而导致每个写请求的延迟增加影响到 Redis 性能。 解决方案就是关闭内存大页机制。 使用了Swap。操作系统为了缓解内存不足对应用程序的影响允许把一部分内存中的数据换到磁盘上以达到应用程序对内存使用的缓冲这些内存数据被换到磁盘上的区域就是 Swap。当内存中的数据被换到磁盘上后Redis 再访问这些数据时就需要从磁盘上读取访问磁盘的速度要比访问内存慢几百倍。尤其是针对 Redis 这种对性能要求极高、性能极其敏感的数据库来说这个操作延时是无法接受的。解决方案就是增加机器的内存让 Redis 有足够的内存可以使用。或者整理内存空间释放出足够的内存供 Redis 使用 网络带宽过载。网络带宽过载的情况下服务器在 TCP 层和网络层就会出现数据包发送延迟、丢包等情况。Redis 的高性能除了操作内存之外就在于网络 IO 了如果网络 IO 存在瓶颈那么也会严重影响 Redis 的性能。解决方案1、及时确认占满网络带宽 Redis 实例如果属于正常的业务访问那就需要及时扩容或迁移实例了避免因为这个实例流量过大影响这个机器的其他实例。2、运维层面需要对 Redis 机器的各项指标增加监控包括网络流量在网络流量达到一定阈值时提前报警及时确认和扩容。 频繁短连接。频繁的短连接会导致 Redis 大量时间耗费在连接的建立和释放上TCP 的三次握手和四次挥手同样也会增加访问延迟。应用应该使用长连接操作 Redis避免频繁的短连接。
为什么 Redis 集群的最大槽数是 16384 个
Redis Cluster 采用数据分片机制定义了 16384个 Slot槽位集群中的每个Redis 实例负责维护一部分槽以及槽所映射的键值数据。
Redis每个节点之间会定期发送ping/pong消息心跳包包含了其他节点的数据用于交换数据信息。
Redis集群的节点会按照以下规则发ping消息
(1)每秒会随机选取5个节点找出最久没有通信的节点发送ping消息(2)每100毫秒都会扫描本地节点列表如果发现节点最近一次接受pong消息的时间大于cluster-node-timeout/2 则立刻发送ping消息
心跳包的消息头里面有个myslots的char数组是一个bitmap每一个位代表一个槽如果该位为1表示这个槽是属于这个节点的。
接下来解答为什么 Redis 集群的最大槽数是 16384 个而不是65536 个。
1、如果采用 16384 个插槽那么心跳包的消息头占用空间 2KB 16384/8如果采用 65536 个插槽那么心跳包的消息头占用空间 8KB (65536/8)。可见采用 65536 个插槽发送心跳信息的消息头达8k比较浪费带宽。
2、一般情况下一个Redis集群不会有超过1000个master节点太多可能导致网络拥堵。
3、哈希槽是通过一张bitmap的形式来保存的在传输过程中会对bitmap进行压缩。bitmap的填充率越低压缩率越高。其中bitmap 填充率 slots / N (N表示节点数)。所以插槽数越低 填充率会降低压缩率会提高。 最后给大家分享200多本计算机经典书籍PDF电子书包括C语言、C、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等感兴趣的小伙伴可以自取 https://mp.weixin.qq.com/s?__bizMzg2OTY1NzY0MQmid2247486208idx1sndbeedf47c50b1be67b2ef31a901b8b56chksmce98f646f9ef7f506a1f7d72fc9384ba1b518072b44d157f657a8d5495a1c78c3e5de0b41efdtoken1652861108langzh_CN#rd