缓存 基础

在项目里面缓存是怎么用的?

项目中缓存是如何使用的?为什么要用缓存?缓存使用不当会造成什么后果?

这个问题,互联网公司必问,要是一个人连缓存都不太清楚,那确实比较尴尬。

只要问到缓存,上来第一个问题,肯定是先问问你项目哪里用了缓存?为啥要用?不用行不行?如果用了以后可能会有什么不良的后果?

这就是看看你对缓存这个东西背后有没有思考,如果你就是傻乎乎的瞎用,没法给面试官一个合理的解答,那面试官对你印象肯定不太好,觉

得你平时思考太少,就知道干活儿。

项目中缓存是如何使用的?

这个,需要结合自己项目的业务来。

比如说:

热点数据,商品信息

排行榜信息

用户状态信息

为什么要用缓存?

用缓存,主要有两个用途:高性能、高并发。

高性能

假设这么个场景,你有个操作,一个请求过来,吭哧吭哧你各种乱七八糟操作mysql,半天查出来一个结果,耗时600ms。但是这个结果可能

接下来几个小时都不会变了,或者变了也可以不用立即反馈给用户。那么此时咋办?

缓存啊,折腾600ms查出来的结果,扔缓存里,一个key 对应一个value,下次再有人查,别走mysql折腾600ms了,直接从缓存里,通过

一个 key 查出来一个value,2ms搞定。性能提升300倍。

就是说对于一些需要复杂操作耗时查出来的结果,且确定后面不怎么变化,但是有很多读请求,那么直接将查询出来的结果放在缓存中,后面

直接读缓存就好。

高并发

mysql 这么重的数据库,压根儿设计不是让你玩儿高并发的,虽然也可以玩儿,但是天然支持不好。mysql 单机支撑到

报警了。

所以要是你有个系统,高峰期一秒钟过来的请求有1万,那一个mysql单机绝对会死掉。你这个时候就只能上缓存,把很多数据放缓存,别放mysql。缓存功能简单,说白了就是key-value 式操作,单机支撑的并发量轻松一秒几万十几万,支撑高并发soeasy。单机承载并发量是mysql单机的几十倍。

缓存是走内存的,内存天然就支撑高并发。

用了缓存之后会有什么不良后果?

常见的缓存问题有以下几个:

缓存与数据库双写不一致

缓存雪崩、缓存穿透、缓存击穿

缓存并发竞争

做过技术预研吗?为什么最终决定用Redis呢?

Redis和Memcached有啥区别?

Redis 支持复杂的数据结构

Redis 相比Memcached 来说,拥有更多的数据结构,能支持更丰富的数据操作。如果需要缓存能够支持更复杂的结构和操作,Redis会是不

错的选择。

Redis 原生支持集群模式

你们是如何保证Redis高并发、高可用的呢?

如何保证redis的高并发和高可用?redis的主从复制原理能介绍一下么?redis的哨兵原理能介绍一下么?

其实问这个问题,主要是考考你,redis单机能承载多高并发?如果单机扛不住如何扩容扛更多的并发?redis会不会挂?既然redis会挂那怎么

保证redis是高可用的?

其实针对的都是项目中你肯定要考虑的一些问题,如果你没考虑过,那确实你对生产系统中的问题思考太少。

如果你用redis 缓存技术的话,肯定要考虑如何用redis 来加多台机器,保证redis 是高并发的,还有就是如何让redis保证自己不是挂掉以后

就直接死掉了,即redis高可用。

由于此节内容较多,因此,会分为两个小节进行讲解。

•redis主从架构

redis 基于哨兵实现高可用

redis 实现高并发主要依靠主从架构,一主多从,一般来说,很多项目其实就足够了,单主用来写入数据,单机几万QPS,多从用来查询数据,

多个从实例可以提供每秒10w的QPS。

如果想要在实现高并发的同时,容纳大量的数据,那么就需要redis集群,使用redis集群之后,可以提供每秒几十万的读写并发。

redis 高可用,如果是做主从架构部署,那么加上哨兵就可以了,就可以实现,任何一个实例宕机,可以进行主备切换。

Redis主从架构

单机的Redis,能够承载的QPS大概就在上万到几万不等。对于缓存来说,一般都是用来支撑读高并发的。因此架构做成主从(master-slave)架构,一主多从,主负责写,并且将数据复制到其它的slave节点,从节点负责读。所有的读请求全部走从节点。这样也可以很轻松实现水平扩容,支撑读高并发。

Redis replication->主从架构->读写分离->水平扩容支撑读高并发

Redis replication的核心机制

•Redis 采用异步方式复制数据到slave节点,不过Redis2.8开始,slave node 会周期性地确认自己每次复制的数据量;

一个master node 是可以配置多个slave node的;

slave node 也可以连接其他的slave node;

slave node 做复制的时候,不会block master node 的正常工作;

slave node 在做复制的时候,也不会block对自己的查询操作,它会用旧的数据集来提供服务;但是复制完成的时候,需要删除旧数据

集,加载新数据集,这个时候就会暂停对外服务了;

slave node 主要用来进行横向扩容,做读写分离,扩容的slave node可以提高读的吞吐量。

注意,如果采用了主从架构,那么建议必须开启master node的持久化,不建议用slave node作为master node的数据热备,因为那样的

话,如果你关掉master的持久化,可能在master宕机重启的时候数据是空的,然后可能一经过复制,slave node 的数据也丢了

另外,master的各种备份方案,也需要做。万一本地的所有文件丢失了,从备份中挑选一份rdb去恢复aster,这样才能确保启动的时候,是有数据的,即使采用了后续讲解的高可用机制,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 部分缺少

主从复制的断点续传

从Redis2.8开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不

是从头开始复制一份。

master node 会在内存中维护一个backlog,master 和slave 都会保存一个replica offset 还有一个 master run id, offset 就是保存在backlog中的。如果master 和slave 网络连接断掉了,slave会让 master 从上次replica offset 开始继续复制,如果没有找到对应的offset,那么就会执行一次resynchronization。

如果根据host+ip 定位master node,是不靠谱的,如果master node 重启或者数据出现了变化,那么slave node 应该根据不同的runid区分。

无磁盘化复制

master 在内存中直接创建 RDB,然后发送给slave,不会在自己本地落地磁盘了。只需要在配置文件中开启 repl-diskless-sync yes

即可。

Java

repl-diskless-sync yes

#等待5s后再开始复制,因为要等更多 slave 重新连接过来

repl-diskless-sync-delay 5

过期key 处理

slave 不会过期key,只会等待master 过期key。如果master过期了一个key,或者通过LRU淘汰了一个key,那么会模拟一条del命令发送

给slave。

复制的完整流程

slave node 启动时,会在自己本地保存master node的信息,包括master node的host 和

ip,但是复制流程没开始。

slave node 内部有个定时任务,每秒检查是否有新的master node 要连接和复制,如果发现,就跟master node建立socket 网络连接。然后slave node 发送 ping命令给master node。如果master设置了requirepass,那么slave node必须发送masterauth的口令过去进行认证。master node 第一次执行全量复制,将所有数据发给slave node。而在后续,master node 持续将写命令,异步复制给 slave node。

复制的完整流程

slave node 启动时,会在自己本地保存master node的信息,包括master node的 host 和

,但是复制流程没开始。

slave node 内部有个定时任务,每秒检查是否有新的master node 要连接和复制,如果发现,就跟master node建立socket 网络连接。然后slave node 发送 ping 命令给master node。如果master设置了requirepass,那么slave node必须发送masterauth的口令过去进行认证。master node 第一次执行全量复制,将所有数据发给slave node。而在后续,master node 持续将写命令,异步复制给slave node。

全量复制

master 执行bgsave,在本地生成一份rdb快照文件。

master node 将rdb 快照文件发送给slave node,如果 rdb复制时间超过60秒(repl-timeout),那么slave node就会认为复制失败,可

以适当调大这个参数(对于千兆网卡的机器,一般每秒传输100MB,6G文件,很可能超过60s)

master node 在生成rdb时,会将所有新的写命令缓存在内存中,在slave node保存了rdb之后,再将新的写命令复制给slave node。

如果在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么停止复制,复制失败。

client-output-buffer-limit slave 256MB 64MB 60

slave node 接收到rdb之后,清空自己的旧数据,然后重新加载rdb到自己的内存中。注意,在清空旧数据之前,slave node 依然会基于增量复制

如果全量复制过程中,master-slave 网络连接断掉,那么slave重新连接master时,会触发增量复制。

master 直接从自己的backlog中获取部分丢失的数据,发送给slave node,默认backlog就是1MB。

master 就是根据slave 发送的psync 中的offset 来从backlog中获取数据的。

heartbeat

主从节点互相都会发送heartbeat信息。

master 默认每隔10秒发送一次heartbeat,slave node每隔1秒发送一个heartbeat。

异步复制

master 每次接收到写命令之后,先在内部写入数据,然后异步发送给slave node2

Redis 如何才能做到高可用

如果系统在365天内,有99.99%的时间,都是可以对外提供服务的,那么就说系统是高可用的。

一个slave挂掉了,是不会影响可用性的,还有其它的slave 在提供相同数据下的相同的对外的查询服务。

但是,如果master node死掉了,会怎么样?没法写数据了,写缓存的时候,全部失效了。slave node 还有什么用呢,没有 master 给它们复

制数据了,系统相当于不可用了。

Redis 的高可用架构,叫做failover 故障转移,也可以叫做主备切换。

master node在故障时,自动检测,并且将某个slave node 自动切换为master node的过程,叫做主备切换。这个过程,实现了Redis的主从

架构下的高可用。

做了持久化配置吗?你们线上是如何配置的呢?

Redis 如果仅仅只是将数据缓存在内存里面,如果Redis宕机了再重启,内存里的数据就全部都弄丢了啊。你必须得用Redis的持久化机制,

将数据写入内存的同时,异步的慢慢的将数据写入磁盘文件里,进行持久化。

如果Redis 宕机重启,自动从磁盘上加载之前持久化的一些数据就可以了,也许会丢失少许数据,但是至少不会将所有数据都弄丢。这个其实一样,针对的都是Redis的生产环境可能遇到的一些问题,就是Redis要是挂了再重启,内存里的数据不就全丢了?能不能重启的时

候把数据给恢复了?

持久化主要是做灾难恢复、数据恢复,也可以归类到高可用的一个环节中去,比如你Redis 整个挂了,然后Redis就不可用了,你要做的事情

就是让Redis变得可用,尽快变得可用。

重启Redis,尽快让它对外提供服务,如果没做数据备份,这时候Redis启动了,也不可用啊,数据都没了。

很可能说,大量的请求过来,缓存全部无法命中,在 Redis 里根本找不到数据,这个时候就死定了,出现缓存雪崩问题。所有请求没有在

Redis命中,就会去mysql数据库这种数据源头中去找,一下子 mysql承接高并发,然后就挂了...

如果你把Redis 持久化做好,备份和恢复方案做到企业级的程度,那么即使你的Redis 故障了,也可以通过备份数据,快速恢复,一旦恢复立

即对外提供服务。

Redis持久化的两种方式

RDB:RDB持久化机制,是对Redis中的数据执行周期性的持久化。

AOF:AOF机制对每条写入命令作为日志,以append-only的模式写入一个日志文件中,在Redis 重启的时候,可以通过回放 AOF日

志中的写入指令来重新构建整个数据集。

通过RDB或AOF,都可以将Redis内存中的数据给持久化到磁盘上面来,然后可以将这些数据备份到别的地方去,比如说阿里云等云服务。如果Redis挂了,服务器上的内存和磁盘上的数据都丢了,可以从云服务上拷贝回来之前的数据,放到指定的目录中,然后重新启动Redis,Redis 就会自动根据持久化数据文件中的数据,去恢复内存中的数据,继续对外提供服务。

如果同时使用RDB和AOF两种持久化机制,那么在Redis 重启的时候,会使用AOF来重新构建数据,因为AOF中的数据更加完整。

RDB优缺点

RDB会生成多个数据文件,每个数据文件都代表了某一个时刻中Redis的数据,这种多个数据文件的方式,非常适合做冷备,可以将这种完整的数据文件发送到一些远程的安全存储上去,比如说Amazon的S3云服务上去,在国内可以是阿里云的ODPS分布式存储上,以预定好的备份策略来定期备份Redis中的数据。

RDB对Redis对外提供的读写服务,影响非常小,可以让Redis保持高性能,因为Redis主进程只需要fork一个子进程,让子进程执行磁

盘10操作来进行RDB持久化即可。

相对于 AOF持久化机制来说,直接基于RDB数据文件来重启和恢复Redis进程,更加快速。

如果想要在Redis故障时,尽可能少的丢失数据,那么RDB没有 AOF好。一般来说,RDB数据快照文件,都是每隔5分钟,或者更长时

间生成一次,这个时候就得接受一旦Redis 进程宕机,那么会丢失最近5分钟(甚至更长时间)的数据。

RDB每次在fork子进程来执行RDB快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,或者

甚至数秒。

AOF优缺点

AOF 可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次 fsync 操作,最多丢失1秒钟的数据。

AOF 日志文件以append-only模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损,即使文件尾部破损,也很容易复制。

AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。因为在rewrite log的时候,会对其中的指令进行压缩,创建出一份需要恢复数据的最小日志出来。在创建新日志文件的时候,老的日志文件还是照常写入。当新的merge后的日志文件ready的时候,再交换新老日志文件即可。

AOF日志文件的命令通过可读较强的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用

flushall命令清空了所有数据,只要这个时候后台rewrite 还没有发生,那么就可以立即拷贝AOF文件,将最后一条 flushall 命令给删了,然后再将该 A0F文件放回去,就可以通过恢复机制,自动恢复所有数据。

对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大。

AOF 开启后,支持的写QPS会比RDB 支持的写QPS低,因为AOF一般会配置成每秒fsync一次日志文件,当然,每秒一次 fsync,性能也还是很高的。(如果实时写入,那么QPS会大降,Redis 性能会大大降低)

以前AOF发生过bug,就是通过AOF记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来。所以说,类似AOF这种较为复杂的基于命令日志 merge 回放的方式,比基于 RDB每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有 bug。不过AOF就是为了避免rewrite过程导致的bug,因此每次rewrite并不是基于旧的指令日志进行merge的,而是基于当时内存中的数据进行指令的重新构建,这样健壮性会好很多。

RDB和AOF到底该如何选择?

不要仅仅使用RDB,因为那样会导致你丢失很多数据;

也不要仅仅使用AOF,因为那样有两个问题:第一,你通过AOF做冷备,没有RDB做冷备来的恢复速度更快;第二,RDB每次简单粗暴

生成数据快照,更加健壮,可以避免 AOF这种复杂的备份和恢复机制的bug:

Redis 支持同时开启开启两种持久化方式,我们可以综合使用AOF和RDB两种持久化机制,用AOF来保证数据不丢失,作为数据恢复的

第一选择;用RDB来做不同程度的冷备,在AOF文件都丢失或损坏不可用的时候,还可以使用ADB来进行快速的数据恢复。

雪崩、穿透、击穿问题你们怎么解决的呢?

其实这是问到缓存必问的,因为缓存雪崩和穿透,是缓存最大的两个问题,要么不出现,一旦出现就是致命性的问题,所以面试官一定会问你。

对于系统A,假设每天高峰期每秒5000个请求,本来缓存在高峰期可以扛住每秒4000个请求,但是缓存机器意外发生了全盘宕机。缓存挂了,此时1秒5000个请求全部落数据库,数据库必然扛不住,它会报一下警,然后就挂了。此时,如果没有采用什么特别的方案来处理这个故障,DBA很着急,重启数据库,但是数据库立马又被新的流量给打死了。

这就是缓存雪崩。大约在3年前,国内比较知名的一个互联网公司,曾因为缓存事故,导致雪崩,后台系统全部崩溃,事故从当天下午持续到晚上凌晨3~4点,

公司损失了几千万

缓存雪崩的事前事中事后的解决方案如下:

事前:Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。

事中:本地ehcache 缓存+hystrix限流&降级,避免MySQL被打死。

事后:Redis持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

用户发送一个请求,系统A收到请求后,先查本地ehcache 缓存,如果没查到再查Redis。如果ehcache和Redis都没有,再查数据库,将数

据库中的结果,写入ehcache 和Redis中。

限流组件,可以设置每秒的请求,有多少能通过组件,剩余的未通过的请求,怎么办?走降级!可以返回一些默认的值,或者友情提示,或者

空值。

好处:

数据库绝对不会死,限流组件保证了每秒有多少个请求能通过

只要数据库不死,就是说,对用户来说,2/5的请求都是可以被处理的。

只要有2/5的请求可以被处理,就意味着你的系统没死,对用户来说,可能就是点击几次刷不出来页面,但是多点几次,就可以刷出来

了。

缓存穿透(Cache Penetration)

对于系统A,假设一秒5000个请求,结果其中4000个请求是黑客发出的恶意攻击。

黑客发出的那4000个攻击,缓存中查不到,每次你去数据库里查,也查不到。

举个栗子。数据库id是从1开始的,结果黑客发过来的请求id全部都是负数。这样的话,缓存中不会有,请求每次都"视缓存于无物",直接

查询数据库。这种恶意攻击场景的缓存穿透就会直接把数据库给打死。

解决方式很简单,每次系统A从数据库中只要没查到,就写一个空值到缓存里去,比如set -999 UNKNOWN。然后设置一个过期时间,这样的话,下次有相同的key来访问的时候,在缓存失效之前,都可以直接从缓存中取数据.

当然,如果黑客如果每次使用不同的负数 id 来攻击,写空值的方法可能就不奏效了。更为经常的做法是在缓存之前增加布隆过滤器,将数据库中所有可能的数据哈希映射到布隆过滤器中。然后对每个请求进行如下判断:

请求数据的key 不存在于布隆过滤器中,可以确定数据就一定不会存在于数据库中,系统可以立即返回不存在。

请求数据的key存在于布隆过滤器中,则继续再向缓存中查询。

使用布隆过滤器能够对访问的请求起到了一定的初筛作用,避免了因数据不存在引起的查询压力。

缓存击穿(Hotspot Invalid)

缓存击穿,就是说某个key 非常热点,访问非常频繁,处干集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存

直接请求数据库,就像是在一道屏障上凿开了一个洞。

不同场景下的解决方式可如下:

•若缓存的数据是基本不会发生更新的,则可尝试将该热点数据设置为永不过期。

·若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于Redis、zookeeper等分布式中间件的分布式互斥

锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。

若缓存的数据更新频繁或者在缓存刷新的流程耗时较长的情况下,可以利用定时线程在缓存过期前主动地重新构建缓存或者延后缓存的过

期时间,以保证所有的请求能一直访问到对应的缓存。

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

。不进行数据淘汰的策略,只有noeviction这一种。

会进行淘汰的7种策略,我们可以再进一步根据淘汰候选数据集的范围把它们分成两类:

在设置了过期时间的数据中进行淘汰,包括volatile-random、volatile-ttl、volatile-lru、volatile-Ifu四种。

在所有数据范围内进行淘汰,包括allkeys-lru、allkeys-random、allkeys-lfu三种。

为什么使用Redis 而不是用 Memcached呢?

Redis

Memcached

1.数据结构不同2.虚拟内存3.分布式

4.存储数据安全5.单Key容量不同6.灾难恢复

进程和线程的区别,使用线程真的能节省时间?

Processes VS Threads

如何找出Redis中的慢查询记录?

slowlog-log-slower-than:用于设置慢查询的评定时间,也就是说

超过此配置项的命令,将会被当成慢操作记录在慢查询日志中,它执

行单位是微秒(1秒等于1000000微秒);

slowlog-max-len:用来配置慢查询日志的最大记录数。

你们是如何对Redis进行性能优化的?

缩短键值对的存储长度;

使用 lazy free(延迟删除)特性;

设置键值的过期时间;

禁用耗时长的查询命令;

使用 slowlog 优化耗时命令;

避免大量数据同时失效;

限制 Redis 内存大小;

检查数据持久化策略;

使用分布式架构来增加读写速度。

Redis单线程为什么这么快

Redis基于Reactor模式开发了网络事件处理器、文件事件处理器file event handler。它是单线程的,所以Redis才叫做单线程的模型,它采用1O多路复用机制来同时监听多个Socket,根据Socket上的事件类型来选择对应的事件处理器来处理这个事件。可以实现高性能的网络通信模型,又可以跟内部其他单线程的模块进行对接,保证了Redis内部的线程模型的简单性。

文件事件处理器的结构包含4个部分:多个Socket、10多路复用程序、文件事件分派器以及事件处理器(命令请求

处理器、命令回复处理器、连2接应答处理器等)。

多个Socket 可能并发的产生不同的事件,10多路复用程序会监听多个Socket,会将Socket 放入一个队列中排队,每次从队列中有序、同步取出一个Socket 给事件分派器,事件分派器把 Socket给对应的事件处理器。然后一个Socket 的事件处理完之后,10多路复用程序才会将队列中的下一个Socket 给事件分派器。文件事件分派器会根据每个Socket 当前产生的事件,来选择对应的事件处理器来处理。

1、Redis启动初始化时,将连接应答处理器跟AE_READABLE事件关联。

2、若一个客户端发起连接,会产生一个AE_READABLE事件,然后由连接应答处理器负责和客户端建立连接,创建客户端对应的socket,同时将这个socket的AE_READABLE事件和命令请求处理器关联,使得客户端可以向主服务器发送命令请求。

3、当客户端向Redis发请求时(不管读还是写请求),客户端socket都会产生一个AE_READABLE事件,触发命令

请求处理器。处理器读取客户端的命令内容,然后传给相关程序执行。

4、当Redis服务器准备好给客户端的响应数据后,会将socket的AE_WRITABLE事件和命令回复处理器关联,当客户端准备好读取响应数据时,会在socket产生一个AE_WRITABLE事件,由对应命令回复处理器处理,即将准备好的响应数据写入socket,供客户端读取。

5、命令回复处理器全部写完到socket后,就会删除该socket的AE_WRITABLE事件和命令回复处理器的映射。

单线程快的原因:

1)纯内存操作

2)核心是基于非阻塞的10多路复用机制

3)单线程反而避免了多线程的频繁上下文切换带来的性能问题

redis的持久化机制

RDB: Redis DataBase 将某一个时刻的内存快照(Snapshot),以二进制的方式写入磁盘。

手动触发:

•save命令,使Redis处于阻塞状态,直到RDB持久化完成,才会响应其他客户端发来的命令,所以在生产环

境一定要慎用

bgsave命令,fork出一个子进程执行持久化,主进程只在fork过程中有短暂的阻塞,子进程创建之后,主进程

就可以响应客户端请求了

自动触发:

save m n:在m秒内,如果有n个键发生改变,则自动触发持久化,通过bgsave执行,如果设置多个、只

要满足其一就会触发,配置文件有默认配置(可以注释掉)

flushall:用于清空redis所有的数据库,flushdb清空当前redis所在库数据(默认是0号数据库),会清空RDB文

件,同时也会生成dump.rdb、内容为空

•主从同步:全量同步时会自动触发bgsave命令,生成rdb发送给从节点

优点:

1、整个Redis数据库将只包含一个文件dump.rdb,方便持久化。

2、容灾性好,方便备份。

3、性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是10最大化。使用单独子进程来进行持久化,主进程不会进行任何10操作,保证了redis的高性能

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

缺点:

1、数据安全性低。RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这

种方式更适合数据要求不严谨的时候)

2、由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务

器停止服务几百毫秒,甚至是1秒钟。会占用cpu

AOF:Append Only File 以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的

方式记录,可以打开文件看到详细的操作记录,调操作系统命令进程刷盘

1、所有的写命令会追加到AOF缓冲中。

2、AOF缓冲区根据对应的策略向硬盘进行同步操作。

3、随着 AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。

4、当Redis重启时,可以加载 AOF文件进行数据恢复。

同步策略:

·每秒同步:异步完成,效率非常高,一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失

·每修改同步:同步持久化,每次发生的数据变化都会被立即记录到磁盘中,最多丢一条

不同步,由操作系统控制,可能会丢失较多数据

优点:

1、数据安全

2、通过append 模式写文件,即使中途服务器宕机也不会破坏已经存在的内容,可以通过redis-check-aof工具

解决数据一致性问题。

3、AOF机制的rewrite 模式。定期对AOF文件进行重写,以达到压缩的目的

。缺点:

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

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

3、运行效率没有RDB高

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

AOF比RDB更安全也更大

RDB性能比AOF好

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

手动触发:

save命令,使Redis 处于阻塞状态,直到RDB持久化完成,才会响应其他客户端发来的命令,所以在生产环

境一定要慎用

bgsave命令,fork出一个子进程执行持久化,主进程只在fork过程中有短暂的阻塞,子进程创建之后,主进程

就可以响应客户端请求了

自动触发:

※save m n:在m秒内,如果有n个键发生改变,则自动触发持久化,通过bgsave执行,如果设置多个、只

要满足其一就会触发,配置文件有默认配置(可以注释掉)

flushall:用于清空redis所有的数据库,flushdb清空当前redis所在库数据(默认是0号数据库),会清空RDB文

件,同时也会生成dump.rdb、内容为空

主从同步:全量同步时会自动触发bgsave命令,生成rdb发送给从节点

优点:

1、整个Redis数据库将只包含一个文件dump.rdb,方便持久化。

2、容灾性好,方便备份。

3、性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是10最大化。使用单独子进程来进行

持久化,主进程不会进行任何10操作,保证了redis的高性能

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

</>

缺点:

Redis的过期键的删除策略

Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存

的key过期了,Redis如何处理。

+惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。

。定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

(expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,

value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)

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

RDB和AOF机制

RDB: Redis DataBase

在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文

件,写入成功后,再替换之前的文件,用二进制压缩存储。

优点:

1、整个Redis数据库将只包含一个文件dump.rdb,方便持久化。

2、容灾性好,方便备份。

3、性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是10最大化。使用单独子进程来进行

持久化,主进程不会进行任何10操作,保证了redis的高性能

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

缺点:

1、数据安全性低。RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这

种方式更适合数据要求不严谨的时候)

2、由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务

器停止服务几百毫秒,甚至是1秒钟。

AOF: Append Only File

以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看

到详细的操作记录

。优点:

1、数据安全,Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。。

2、通过append模式写文件,即使中途服务器宕机也不会破坏已经存在的内容,可以通过redis-check-aof工具

解决数据一致性问题。

3、AOF机制的rewrite 模式。定期对AOF文件进行重写,以达到压缩的目的

·缺点:

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

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

3、运行效率没有RDB高

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

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

AOF比RDB更安全也更大

rdb性能比AOF好

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

Redis分布式锁底层是如何实现的?

1.首先利用setnx来保证:如果key不存在才能获取到锁,如果key存在,则获取不到锁

2.然后还要利用lua脚本来保证多个redis操作的原子性

3.同时还要考虑到锁过期,所以需要额外的一个看门狗定时任务来监听锁是否需要续约

4.同时还要考虑到redis节点挂掉后的情况,所以需要采用红锁的方式来同时向N/2+1个节点申请锁,都申请到了才证明获取锁

成功,这样就算其中某个redis节点挂掉了,锁也不能被其他客户端获取到

Redis主从复制的核心原理

Redis的主从复制是提高Redis的可靠性的有效措施,主从复制的流程如下:

1.集群启动时,主从库间会先建立连接,为全量复制做准备

2.主库将所有数据同步给从库。从库收到数据后,在本地完成数据加载,这个过程依赖于内存快照RDB

3.在主库将数据同步给从库的过程中,主库不会阻塞,仍然可以正常接收请求。否则,redis的服务就被中断了。但是,这些请求中的写操作并没有记录到刚刚生成的RDB文件中。为了保证主从库的数据一致性,主库会在内存中用专门的replication buffer,记录RDB文件生成收到的所有写操作。

4.最后,也就是第三个阶段,主库会把第二阶段执行过程中新收到的写命令,再发送给从库。具体的操作是,当主库完成RDB文件发送

后,就会把此时replocation buffer中修改操作发送给从库,从库再执行这些操作。这样一来,主从库就实现同步了

5.后续主库和从库都可以处理客户端读操作,写操作只能交给主库处理,主库接收到写操作后,还会将写操作发送给从库,实现增量同步

Redis和Mysql如何保证数据一致

1.先更新Mysql,再更新Redis,如果更新Redis失败,可能仍然不一致

2.先删除Redis缓存数据,再更新MysqI,再次查询的时候在将数据添加到缓存中,这种方案能解决1方案的问题,但是在高并发下性能较低,而且仍然会出现数据不一致的问题,比如线程1删除了Redis缓存数据,正在更新Mysql,此时另外一个查询再查询,那么就会把Mysql中老数据又查到Redis中

3.延时双删,步骤是:先删除Redis缓存数据,再更新Mysql,延迟几百毫秒再删除Redis缓存数据,这样就算在更新Mysq|时,

有其他线程读了Mysql,把老数据读到了Redis中,那么也会被删除掉,从而把数据保持一致

Explain语句结果中各个字段分表表示什么

redis集群方案

哨兵模式:

sentinel,哨兵是redis集群中非常重要的一个组件,主要有以下功能:

•集群监控:负责监控redis master和slave进程是否正常工作。

·消息通知:如果某个redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。

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

配置中心:如果故障转移发生了,通知client客户端新的master 地址。

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

·故障转移时,判断一个master node 是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举

,即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的

•哨兵通常需要3个实例,来保证自己的健壮性。

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

·对于哨兵+redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。

Redis Cluster是一种服务端Sharding技术,3.0版本开始正式提供。采用slot(槽)的概念,一共分成16384个槽。将

请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行

方案说明

·通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽(哈希值)区间的数据,默认分配了16384个槽位

每份数据分片会存储在多个互为主从的多节点上

数据写入先写主节点,再同步到从节点(支持配置为阻塞同步)

。同一分片多个节点间的数据不保持强一致性

·读取数据时,当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点

,扩容时需要需要把旧节点的数据迁移一部分到新节点

在redis cluster 架构下,每个redis 要放开两个端口号,比如一个是6379,另外一个就是加1w的端口号,比如

16379。

16379端口号是用来进行节点间通信的,也就是cluster bus的通信,用来进行故障检测、配置更新、故障转移授权。cluster bus用了另外一种二进制的协议,gossip协议,用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间。

优点

。无中心架构,支持动态扩容,对业务透明

具备Sentinel的监控和自动Failover(故障转移能力

客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可

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

缺点

运维也很复杂,数据迁移需要人工干预

只能使用0号数据库

。不支持批量操作(pipeline管道操作)

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

Redis Sharding是Redis Cluster出来之前,业界普遍使用的多Redis实例集群方法。其主要思想是采用哈希算法将

Redis数据的key进行散列,通过hash函数,特定的key会映射到特定的Redis节点上。Java redis客户端驱动jedis,

支持Redis Sharding功能,即Shardedjedis以及结合缓存池的ShardedjedisPool

优点

优势在于非常简单,服务端的Redis实例彼此独立,相互无关联,每个Redis实例像单服务器一样运行,非常容易

线性扩展,系统的灵活性很强

缺点

由于sharding处理放到客户端,规模进一步扩大时给运维带来挑战。

客户端sharding不支持动态增删节点。服务端Redis实例群拓扑结构有变化时,每个客户端都需要更新调整。连接

不能共享,当应用规模增大时,资源浪费制约优化

Redis如何配置Key的过期时间?他的实现原理是什么?

redis设置key的过期时间: 1、EXPIRE。2 SETEX

实现原理:

1、定期删除: 每隔一段时间,执行一次删除过期key的操作。

2、懒汉式删除:当使用get、getset等指令去获取数据时,判断key是否过期。过期后,就先把key删除,再执行后面的操

作。

Redis是将两种方式结合来使用。

懒汉式删除

定期删除:平衡执行频率和执行时长。

定期删除时会遍历每个database(默认16个,检查当前库中指定个数的key(默认是20个),随机抽查这些key,

如果有过期的,就删除。

程序中有一个全局变量记录到秒到了哪个数据库。

Redis线程模型、单线程快的原因

Redis基于Reactor模式开发了网络事件处理器,这个处理器叫做文件事件处理器file event handler。这个文件事件处理器,它是单线程的,所以Redis 才叫做单线程的模型,它采用10多路复用机制来同时监听多个Socket,根据Socket上的事件类型来选择对应的事件处理器来处理这个事件。可以实现高性能的网络通信模型,又可以跟内部其他单线程的模块进行对接,保证了Redis内部的线程模型的简单性。

文件事件处理器的结构包含4个部分:多个Socket、10多路复用程序、文件事件分派器以及事件处理器(命令请求

处理器、命令回复处理器、连接应答处理器等)。

多个Socket 可能并发的产生不同的操作,每个操作对应不同的文件事件,但是10多路复用程序会监听多个Socket,会将Socket 放入一个队列中排队,每次从队列中取出一个Socket 给事件分派器,事件分派器把Socket给对应的事件处理器。

然后一个Socket的事件处理完之后,10多路复用程序才会将队列中的下一个Socket给事件分派器。文件事件分

派器会根据每个Socket 当前产生的事件,来选择对应的事件处理器来处理。

单线程快的原因:

1)纯内存操作

2)核心是基于非阻塞的IO多路复用机制

3)单线程反而避免了多线程的频繁上下文切换带来的性能问题

Redis有哪些数据结构?分别有哪些典型的应用场景?

Redis的数据结构有:

1.字符串:可以用来做最简单的数据,可以缓存某个简单的字符串,也可以缓存某个json格式的字符串,Redis分布式锁

的实现就利用了这种数据结构,还包括可以实现计数器、Session共享、分布式ID

2.哈希表:可以用来存储一些key-value对,更适合用来存储对象

3.列表:Redis的列表通过命令的组合,既可以当做栈,也可以当做队列来使用,可以用来缓存类似微信公众号、微博等

消息流数据

4.集合:和列表类似,也可以存储多个元素,但是不能重复,集合可以进行交集、并集、差集操作,从而可以实现类似,

我和某人共同关注的人、朋友圈点赞等功能

5.有序集合:集合是无序的,有序集合可以设置顺序,可以用来实现排行榜功能

Redis分布式锁底层是如何实现的?

1.首先利用setnx来保证:如果key不存在才能获取到锁,如果key存在,则获取不到锁

2.然后还要利用lua脚本来保证多个redis操作的原子性

3.同时还要考虑到锁过期,所以需要额外的一个看门狗定时任务来监听锁是否需要续约

4.同时还要考虑到redis节点挂掉后的情况,所以需要采用红锁的方式来同时向N/2+1个节点申请锁,都申请到了才证明获

取锁成功,这样就算其中某个redis节点挂掉了,锁也不能被其他客户端获取到

redis 主从复制的核心原理

通过执行slaveof命令或设置slaveof选项,让一个服务器去复制另一个服务器的数据。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。

全量复制:

(1)主节点通过bgsave命令fork子进程进行RDB持久化,该过程是非常消耗CPU、内存(页表复制)、硬盘10的

(2)主节点通过网络将RDB文件发送给从节点,对主从节点的带宽都会带来很大的消耗

(3)从节点清空老数据、载入新RDB文件的过程是阻塞的,无法响应客户端的命令;如果从节点执行

bgrewriteaof,也会带来额外的消耗

部分复制:

1.复制偏移量:执行复制的双方,主从节点,分别会维护一个复制偏移量offset

2.复制积压缓冲区:主节点内部维护了一个固定长度的、先进先出(FIFO)队列作为复制积压缓冲区,当主从节点

offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。

3.服务器运行ID(runid):每个Redis节点,都有其运行ID,运行ID由节点在启动时自动生成,主节点会将自己的运行ID发送给从节点,从节点会将主节点的运行ID存起来。从节点Redis断开重连的时候,就是根据运行ID来判断同步的进度:

。如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部

分复制到底能不能部分复制还要看offset和复制积压缓冲区的情况

。如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的

主节点,只能进行全量复制。

过程原理:

Redis的数据结构及使用场景

Redis的数据结构有:

1.字符串:可以用来做最简单的数据缓存,可以缓存某个简单的字符串,也可以缓存某个json格式的字符串,Redis分布式锁的实现就利用了这种数据结构,还包括可以实现

计数器、Session共享、分布式ID

2.哈希表:可以用来存储一些key-value对,更适合用来存储对象

3.列表:Redis的列表通过命令的组合,既可以当做栈,也可以当做队列来使用,可以用来缓存类似微信公众号、微博等消息流数据

4.集含:和列表类似,也可以存储多个元素,但是不能重复,集合可以进行交集、并集、差集操作,从而可以实现类似,我和某人共同关注的人、朋友圈点赞等功能

5.有序集合:集合是无序的,有序集合可以设置顺序,可以用来实现排行榜功能

Redis集群策略

Redis提供了三种集群策略:

1.主从模式:这种模式比较简单,主库可以读写,并且会和从库进行数据同步,这种模式下,客户端直接连主库或某个从库,但是但主库或从库宕机后,客户端需要手动修

改IP,另外,这种模式也比较难进行扩容,整个集群所能存储的数据受到某台机器的内存容量,所以不可能支持特大数据量

2.哨兵模式:这种模式在主从的基础上新增了哨兵节点,但主库节点宕机后,哨兵会发现主库节点宕机,然后在从库中选择一个库作为进的主库,另外哨兵也可以做集群,从而可以保证但某一个哨兵节点宕机后,还有其他哨兵节点可以继续工作,这种模式可以比较好的保证Redis集群的高可用,但是仍然不能很好的解决Redis的容量上限问题。

3.Cluster模式:Cluster模式是用得比较多的模式,它支持多主多从,这种模式会按照key进行槽位的分配,可以使得不同的key分散到不同的主节点上,利用这种模式可以使

得整个集群支持更大的数据容量,同时每个主节点可以拥有自己的多个从节点,如果该主节点宕机,会从它的从节点中选举一个新的主节点。

对于这三种模式,如果Redis要存的数据量不大,可以选择哨兵模式,如果Redis要存的数据量大,并且需要持续的扩容,那么选择Cluster模式。

布隆过滤器原理,优缺点

位图:int[10],每个int类型的整数是4*8=32个bit,则int[10]一共有320 bit,每个bit非O即1,初始化时都是0

添加数据时,将数据进行hash得到hash值,对应到bit位,将该bit改为1,hash函数可以定义多个,则一个数据添

加会将多个(hash函数个数)bit改为1,多个hash函数的目的是减少hash碰撞的概率

查询数据:hash函数计算得到hash值,对应到bit中,如果有一个为0,则说明数据不在bit中,如果都为1,则该

数据可能在bit中

优点:

·占用内存小

。增加和查询元素的时间复杂度为:O(K),(K为哈希函数的个数,一般比较小),与数据量大小无关

•哈希函数相互之间没有关系,方便硬件并行运算

•布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势

。数据量很大时,布隆过滤器可以表示全集

使用同一组散列函数的布隆过滤器可以进行交、并、差运算

缺点:

课判率,即存在假阳性(Ealse Position),不能准确判断示素是否在集合中

·不能获取元素本身

一般情况下不能从布隆过滤器中删除元素

常见的缓存淘汰算法

FIFO(First In First Out,先进先出),根据缓存被存储的时间,离当前最远的数据优先被淘汰;LRU (Least RecentlyUsed,最近最少使用),根据最近被使用的时间,离当前最远的数据优先被淘汰;LFU(Least Frequently Used,最不经常使用),在一段时间内,缓存数据被使用次数最少的会被淘汰。

分布式系统中常用的缓存方案有哪些

客户端缓存:页面和浏览器缓存,APP缓存,H5缓存,localStorage 和 sessionStorage

CDN缓存:内容存储:数据的缓存,内容分发:负载均衡

nginx缓存:静态资源

服务端缓存:本地缓存,外部缓存

数据库缓存:持久层缓存[(mybatis,hibernate多级缓存),mysq|查询缓存

操作系统缓存:Page Cache、Buffer Cache

什么是缓存穿透?缓存击穿?缓存雪崩?怎么解决?

1、缓存穿透:缓存中查不到,数据库中也查不到。

解决方案: 1》对参数进行合法性校验。2》将数据库中没有查到结果的数据也写入到缓存。

2、缓存击穿:缓存中没有,数据库中有。一般是出现在存数数据初始化以及key过期了的情况。他的问题在于,重新写入缓

存需要一定的时间,如果是在高并发场景下,过多的请求就会瞬间写到DB上,给DB造成很大的压力

解决方案: 1》设置这个热点缓存永不过期。这时要注意在value当中包含一个逻辑上的过期时间,然后另起一个线程,定期

重建这些缓存。

2》加载DB的时候,

要防止并发。

3、缓存雪崩:缓存大面积过期,导致请求都被转发到数据库

解决方案:1》把缓存的时效时间分散开。例如,在原有的统一失效时间基础上,增加一个随机值。

2》对热点数据设置永不过期。

缓存过期都有哪些策略?

定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,但是很消耗内存、许多的过期数据都还存在内存中。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。

定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key(是随机的),并清除其中已过期的key。该策略是定时过期和惰性过期的折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

简述redis数据结构

String:字符串List:列表Hash:哈希表Set:无序集合Sorted Set:有序集合bitmap:布隆过滤器

GeoHash:坐标,借助Sorted Set实现,通过zset的score进行排序就可以得到坐标附近的其它元素,通过将score

还原成坐标值就可以得到元素的原始坐标

HyperLogLog:统计不重复数据,用于大数据基数统计

Streams:内存版的kafka

简述Redis事务实现

1、事务开始

MULT/命令的执行,标识着一个事务的开始。MULT/命令会将客户端状态的f1ags 属性中打开REDIS_MULTI标识来

完成的。

2、命令入队

当一个客户端切换到事务状态之后,服务器会根据这个客户端发送来的命令来执行不同的操作。如果客户端发送的命令为MULTI、EXEC、WATCH、DISCARD中的一个,立即执行这个命令,否则将命令放入一个事务队列里面,然后向客户端返回QUEUED回复

如果客户端发送的命令为EXEC、DISCARD、WATCH、MULTI四个命令的其中一个,那么服务器立即执行这

个命令。

如果客户端发送的是四个命令以外的其他命令,那么服务器并不立即执行这个命令。

首先检查此命令的格式是否正确,如果不正确,服务器会在客户端状态(redisClient)的flags属性关闭

REDIS_MULTI标识,并且返回错误信息给客户端。

如果正确,将这个命令放入一个事务队列里面,然后向客户端返回QUEUED回复

事务队列是按照FIFO的方式保存入队的命令

3、事务执行

客户端发送EXEC命令,服务器执行EXEC命令逻辑。

如果客户端状态的flags 属性不包含REDIS_MULTI标识,或者包含REDIS_DIRTY_CAS或者REDIS_DIRTY_EXEC标识,那么就直接取消事务的执行。

否则客户端处于事务状态(flags有REDIS_MULTI标识)

列中的所有命令,最后将返回结果全部返回给客户端;

redis 不支持事务回滚机制,但是它会检查每一个事务中的命令是否错误。

Redis 事务不支持检查那些程序员自己逻辑错误。例如对String类型的数据库键执行对HashMap类型的操作!

服务器会遍历客户端的事务队列,然后执行事务队

WATCH命令是一个乐观锁,可以为Redis 事务提供check-and-set(CAS)行为。可以监控一个或多个键,

一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。

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

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

打断时,返回空值 nil。

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

UNWATCH命令可以取消watch对所有key的监控。

redis 主从同步机制

1、从节点执行slaveof masterlpport,保存主节点信息

2、从节点中的定时任务发现主节点信息,建立和主节点的socket连接

3、从节点发送信号,主节点返回,两边能互相通信

4、连接建立后,主节点将所有数据发送给从节点(数据同步)

5、主节点把当前的数据同步给从节点后,便完成了复制过程。接下来,主节点就会持续的把写命令发送给从节

点,保证主从数据一致性。

runid:每个redis节点启动都会生成唯一的uuid,每次redis重启后,runld都会发生变化。

offset:主从节点各自维护自己的复制偏移量offset,当主节点有写入命令时,offset=offset+命令的字节长度。从节点在收到主节点发送的命令后,也会增加自己的offset,并把自己的offset发送给主节点。主节点同时保存自己的offset和从节点的offset,通过对比offset来判断主从节点数据是否一致。

repl_backlog_size:保存在主节点上的一个固定长度的先进先出队列,默认大小是1MB。

全量复制:

·从节点发送psync命令,psync runid offset(由于是第一次,runid为?,offset为-1)

·主节点返回FULLRESYNC runld offset,runld是主节点的runId,offset是主节点目前的offset。从节点保存信

·主节点启动bgsave命令fork子进程进行RDB持久化

主节点将RDB文件发送给从节点,到从节点加载数据完成之前,写命令写入缓冲区

从节点清理本地数据并加载RDB,如果开启了AOF会重写AOF

部分复制:

1.复制偏移量:psync runid offset

2.复制积压缓冲区:当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复

制。

。如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部

分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况);

。如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的

主节点,只能进行全量复制。

过程原理:

如何保证Redis与数据库的数据一致?

当我们对数据进行修改的时候,到底是先删缓存,还是先写数据库?

1、如果先删缓存,再写数据库: 在高并发场景下,当第一个线程删除了缓存,还没有来得及写数据库,第二个线程来读取数据,会发现缓存中的数据为空,那就会去读数据库中的数据(旧值,脏数据),读完之后,把读到的结果写入缓存(此时,第一个线程已经将新的值写到缓存里面了),这样缓存中的值就会被覆盖为修改前的脏数据。

总结:在这种方式下,通常要求写操作不会太频繁。

解决方案:1》先操作缓存,但是不删除缓存。将缓存修改为一个特殊值(-999)。客户端读缓存时,发现是默认值,就休眠一

小会,再去查一次Redis。-》 特殊值对业务有侵入。 休眠时间,可能会多次重复,对性能有影响。

2》延时双删。 先删除缓存,然后再写数据库,休眠一小会,再次删除缓存。-》 如果数据写操作很频繁,同样还是会有脏数

据的问题。

2、先写数据库,再删缓存:如果数据库写完了之后,缓存删除失败,数据就会不一致。

总结:始终只能保证一定时间内的最终一致性。

解决方案: 1》给缓存设置一个过期时间 问题:过期时间内,缓存数据不会更新。

2》引入MQ,保证原子操作。

解决方案:将热点数据缓存设置为永不过期,但是在value当中写入一个逻辑上的过期时间,另外起一个后台线程,扫描这些

key,对于已逻辑上过期的缓存,进行删除。

如何保证数据库与缓存的一致性?

由于缓存和数据库是分开的,无法做到原子性的同时进行数据修改,可能出现缓存更新失败,或者数据库更新失败

的情况,这时候会出现数据不一致,影响前端业务

·先更新数据库,再更新缓存。缓存可能更新失败,读到老数据

。先删缓存,再更新数据库。并发时,读操作可能还是会将旧数据读回缓存

,先更新数据库,再删缓存。也存在缓存删除失败的可能

最经典的缓存+数据库读写的模式,Cache Aside Pattern。

读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。

更新的时候,先更新数据库,然后再删除缓存。

为什么是删除而不是更新?

删除更加轻量,延迟加载的一种实现,更新可能涉及多个表、比较耗时

延时双删:先删除缓存,再更新数据库,休眠1s、再次删除缓存。写数据的休眠时间则在读数据业务逻辑的耗时基础上,加几百ms即可。这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据,并发还是可能读到旧值覆盖缓存

终极方案:

将访问操作串行化

1.先删缓存,将更新数据库的操作放进有序队列中

2.从缓存查不到的查询操作,都进入有序队列

会面临的问题:

1.读请求积压,大量超时,导致数据库的压力:限流、熔断

2.如何避免大量请求积压:将队列水平拆分,提高并行度。

3保证相同请求路由正确

如何避免缓存穿透、缓存击穿、缓存雪崩?

缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请

求而崩掉。

解决方案:

·缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。

•给每一个缓存数据增加相应的缓存标记,记录缓存是否失效,如果缓存标记失效,则更新数据缓存。

。缓存预热

·互斥锁

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

而崩掉。

解决方案:

。接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;

从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设

置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击

采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个

bitmap 拦截掉,从而避免了对底层存储系统的查询压力

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

解决方案

·设置热点数据永远不过期。

·加互斥锁

说一下你知道的redis高可用方案

主丛

哨兵模式:

sentinel,哨兵是redis集群中非常重要的一个组件,主要有以下功能:

·集群监控:负责监控redis master 和slave进程是否正常工作。

消息通知:如果某个redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。

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

。配置中心:如果故障转移发生了,通知client客户端新的master 地址。

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

·故障转移时,判断一个master node是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举

。即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的

·哨兵通常需要 3个实例,来保证自己的健壮性。

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

对于哨兵+redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。

Redis Cluster是一种服务端Sharding技术,3.0版本开始正式提供。采用slot(槽)的概念,一共分成16384个槽。将

请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行

方案说明

·通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽(哈希值)区间的数据,默认分配了16384个槽位

每份数据分片会存储在多个互为主从的多节点上

•数据写入先写主节点,再同步到从节点(支持配置为阻塞同步)

,同一分片多个节点间的数据不保持强一致性

读取数据时,当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点

。扩容时需要需要把旧节点的数据迁移一部分到新节点

在redis cluster 架构下,每个redis 要放开两个端口号,比如一个是 6379,另外一个就是加1w的端口号,比如

16379。

16379端口号是用来进行节点间通信的,也就是cluster bus的通信,用来进行故障检测、配置更新、故障转移授权。cluster bus 用了另外一种二进制的协议,gossip协议,用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间。

优点

·无中心架构,支持动态扩容,对业务透明

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

,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可

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

缺点

·运维也很复杂,数据迁移需要人工干预

,只能使用0号数据库

·不支持批量操作(pipeline管道操作)

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

Redis Sharding是Redis Cluster出来之前,业界普遍使用的多Redis实例集群方法。其主要思想是采用哈希算法将Redis数据的key进行散列,通过hash函数,特定的key会映射到特定的Redis节点上。Java redis客户端驱动jedis,支持Redis Sharding功能,即Shardedjedis以及结合缓存池的ShardedjedisPool

优点

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

缺点

由于sharding处理放到客户端,规模进一步扩大时给运维带来挑战。

客户端sharding不支持动态增删节点。服务端Redis实例群拓扑结构有变化时,每个客户端都需要更新调整。连接

不能共享,当应用规模增大时,资源浪费制约优化

相关推荐
04Koi.3 小时前
Redis--常用数据结构和编码方式
数据库·redis·缓存
Ven%5 小时前
如何修改pip全局缓存位置和全局安装包存放路径
人工智能·python·深度学习·缓存·自然语言处理·pip
weisian1515 小时前
Redis篇--常见问题篇8--缓存一致性3(注解式缓存Spring Cache)
redis·spring·缓存
向阳12185 小时前
mybatis 缓存
java·缓存·mybatis
HEU_firejef6 小时前
Redis——缓存预热+缓存雪崩+缓存击穿+缓存穿透
数据库·redis·缓存
weisian1517 小时前
Redis篇--常见问题篇7--缓存一致性2(分布式事务框架Seata)
redis·分布式·缓存
凡人的AI工具箱8 小时前
每天40分玩转Django:Django表单集
开发语言·数据库·后端·python·缓存·django
快乐非自愿8 小时前
.NET 9 中的 多级缓存 HybridCache
缓存·.net
P.H. Infinity8 小时前
【Redis】配置序列化器
数据库·redis·缓存
Dream it possible!8 小时前
LeetCode 热题 100_LRU 缓存(35_146_中等_C++)(哈希表 + 双向链表)(构造函数声明+初始化列表=进行变量初始化和赋值)
c++·leetcode·缓存