redis

什么是缓存穿透

答:查询一个不存在的数据,mysql查询不到数据也不会直接写入缓存,就会导致每次请求都查数据库

解决方案一:缓存空数据(优点:简单;缺点:消耗内存,可能发生数据不一致的问题)

解决方案二:布隆过滤器(优点:内存占用较少,没有多余key;缺点:实现复杂,存在误判)

 什么是布隆过滤器

答:布隆过滤器主要是用于检索一个元素是否在一个集合中。

一般使用的是redisson实现的布隆过滤器。它的底层主要是先去初始化一个比较大数组,里面存放的二进制0或1。在一开始都是0,当一个key来了之后经过3次hash计算,模于数组长度找到数据的下标然后把数组中原来的0改为1,这样的话,三个数组的位置就能标明一个key的存在。

 什么是缓存击穿

答:给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮

解决方法一:缓存失效时先使用redis的setnx去设置一个互斥锁,成功去load db并回设缓存,否则重试get缓存的方法(强一致,性能差)

解决方法二:设置当前key逻辑过期,设置key时同时设置一个过期时间字段一块存入缓存中,查询时取出判断是否过期,如果过期则开通另一线程进行数据同步,当前线程正常返回旧数据(高可用,性能优,不能保证数据绝对一致)

 什么是缓存雪崩

答:在同一时段大量的缓存key同时失效或者redis服务宕机,导致大量请求到达数据库,带来巨大压力

解决方法:将key的缓存失效时间分散开,可以在原有失效时间基础上增加一个随机值(设置redis集群,添加多级缓存Guava或Caffeine)

处理三大问题的保底策略:降级限流策略

 Redis的优缺点

答:优点:1、速度快:基于内存的数据库,读写速度非常快

2、支持多种数据结构:String(存储简单的字符串数据),List(存储有序的元素列表,支持排序和去重),Hash(存储对象和映射关系,哈希可以将一个对象的多个属性存储在一个键值对中,并且可以快速地根据某个属性进行查询),Set(存储不重复的元素集合),Zset(Sorted Set)(存储有序的元素集合)

3、高可用:支持主从复制,哨兵模式,集群

4、支持事务:保证一组操作的原子性

5、可扩展性:支持水平扩展,通过增加节点来提高系统的并发能力和存储能力

缺点:1、数据易丢失:如果服务器重启或关闭,数据将会丢失,需要使用持久化来保证数据的安全性

2、内存占用大:数据存储在内存中,数据量较大会占用较多的内存空间

3、不支持复杂查询:不支持复杂的查询操作,如关联查询,聚合查询等

4、性能受网络影响:如果网络延迟较大,可能会影响redis的性能

 Redis作为缓存,mysql的数据如何与redis进行同步(双写一致性)?

答:延迟双删:如果是写操作,会先把缓存中的数据删除,然后更新数据库,最后再延时删除缓存中的数据,由于不好确定延时多久,所以在延时的过程中可能会出现脏数据,并不能保证强一致性,一般不采用它。

解决方法一:允许延时一致(最终一致性)的业务,采用异步通知

① :使用MQ中间件,更新数据之后,通知缓存删除

② :利用canal(卡no)中间件,不需要修改业务代码,伪装为mysql的一个从节点,canal通过读取binlog数据更新缓存

解决方法二:允许强一致性的,采用Redisson(瑞迪森)提供的读写锁

① 共享锁:读锁readLock(瑞老科),加锁之后,其他线程可以共享读操作【读读不互斥,读写互斥】

② 排他锁:独占锁writeLock(r老科),底层使用的也是setnx(赛科斯),保证了同时只能有一个线程操作,加锁之后,阻塞其他线程读写操作【读写都互斥】

 redis作为缓存,数据的持久化是怎么做的?

答:解决方法一:RDB:(Redis Database Backup file)Redis数据备份文件,也叫Redis数据快照,把内存中的所有数据都记录到磁盘中,当Redis实例故障重启后,从磁盘读取快照文件,恢复数据

RDB执行原理:bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据,完成fork后读取内存数据并写入RDB文件

Fork采用的是copy-on-write技术:

① :当主进程执行读操作时,访问共享内存

② :当主进程执行写操作时,则会拷贝一份数据,执行写操作

解决方法二:AOF(默认关闭):(Append Only File)追加文件,Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件,当Redis实例宕机恢复数据时,会从这个文件中再次执行一遍命令来恢复数据

宕机恢复速度:RDB二进制文件,体积小,恢复快,但是有可能丢数据;AOF恢复慢,但是丢数据风险小,可以设置刷盘策略,everysec每秒刷盘策略

目前一般都是两种结合使用

 Redis的数据过期策略有哪些

答:惰性删除(/duò xìng):给key设置一个过期时间,访问该key时判断是否过期,如果过期,则删除

优点:对CPU友好,对用不到的key不用浪费时间定期检查

缺点:对内存不友好,如果key过期未使用,也不会释放内存

定期删除:定期检查一定量的key是否过期

①:SLOW(斯咯)模式:定时任务,执行频率默认为10hz,每次不超过25ms,可以通过配置文件redis.conf中的hz来修改次数

②:FAST(法斯特)模式:执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms

优点:可以通过限制删除操作执行时长和频率来减少对CPU的影响,也有效释放过期键占用的内存

缺点:难以确定删除操作执行的时长和频率

问8: Redis的数据淘汰策略有哪些

答:LRU算法(Least Recently Used):最近最少使用;用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高

LFU算法(Least Frequently Used):最少频率使用;统计每个key的访问频率,值越小淘汰优先级越高

①:noeviction(默认):不淘汰任何key,内存满时不允许写入新数据

②:volatile-ttl:对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰

③:allkeys-random:对全体key,随机进行淘汰

④:volatile-random:对设置了TTL的key,随机进行淘汰

⑤:allkeys-lru:对全体key,基于LRU算法进行淘汰

⑥:volatile-lru:对设置了TTL的key,基于LRU算法进行淘汰

⑦:allkeys-lfu:对全体key,基于LFU算法进行淘汰

⑧:volatile-lfu:对设置了TTL的key,基于LFU算法进行淘汰

 Redis分布式锁如何实现

答:Redis实现分布式锁主要利用Redis的setnx(赛克斯)(SET if not exists如果不存在,则SET)命令

获取锁:SET lock value NX(互斥)EX(设置超时时间)10

释放锁:DEL key

加锁,设置过期时间等操作都是基于lua脚本完成,它可以保证命令的原子性

 Redis实现分布式锁如何合理的控制锁的有效时长

答:在redisson(瑞第森)的分布式锁中,提供了一个WatchDog(看门狗),一个线程获取锁成功后,WatchDog会给持有锁的线程续期(默认是每隔10秒续期一次)

 redisson锁可以重入吗

答:可以重入,多个锁重入需要判断是否是当前线程,在redis中进行存储的时候使用的hash结构,来储存线程信息和重入的次数

 redisson锁能解决主从数据一致的问题吗

答:不能解决,redis思想是AP高可用思想,可以使用redisson提供的红锁来解决,但是实现复杂,性能差,运维繁琐,如果业务中必须保持数据的强一致性,可以采用zookeeper(CP强一致)实现的分布式锁

红锁(RedLock):不能只在一个redis实例上创建锁,应该在多个redis实例上创建锁(n/2+1),避免在一个redis实例上加锁

 Redis集群有哪些方案

答:1、主从复制(保证不了Redis的高可用):高并发读

2、哨兵模式(实现主从集群的自动故障恢复):高可用

3、Redis分片集群:海量数据存储和高并发写

 什么是Redis主从同步

答:单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。一般都是一主多从,主节点负责写数据,从节点负责读数据

 主从同步数据的流程

答:replication id:简称replid(r铺累特),是数据集的标记,id一致则说明是同一数据集,每一个master都有唯一的replid,slave则会继承master节点的replid

Offset:偏移量,随着记录在repl_baklog(r铺雷森拜可老歌)日志文件中的数据增多而逐渐增大,slave完成同步时也会记录当前同步的offset,如果slave的offset小于master的offset,说明slave数据落后于master,需要更新

流程一:全量同步:

1、 从节点请求主节点同步数据(replid,offset)

2、 主节点判断是否是第一次请求,是第一次就与从节点同步版本信息

3、 主节点执行bgsave,生成RDB文件后,发送给从节点去执行

4、 在RDB生成执行期间,主节点会以命令的方式记录到缓冲区(repl_baklog日志文件)

5、 把生成之后的命令日志文件发送给从节点进行同步

流程二:增量同步:

1、 从节点请求主节点同步数据,主节点判断不是第一次请求,不是第一次就获取从节点的offset值

2、 主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步

 哨兵模式

答:1、监控:Sentinel会不断检查master和slave是否按预期工作

2、自动故障恢复:如果master故障,Sentinel会将一个slave提升为master,当故障实例恢复后也以新的master为主

3、通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端

哨兵选主规则:

1、 首先判断主与从节点断开时间长短,如超过指定值就排除该从节点

2、 然后判断从节点的slave-priority值,越小优先级越高

3、 如果slave-prority值一样,则判断slave节点的offset值,越大优先级越高

4、 最后是判断slave节点的运行id大小,越小优先级越高

 服务状态监控

答:Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping的命令:

1、 主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线

2、 客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半

问18: 使用redis是单点还是集群

答:主从(1主1从)+哨兵就行。单节点不超过10G内存,如果redis内存不足则可以给不同服务分配独立的Redis主从节点

redis集群脑裂

答:集群脑裂是由于主节点和从节点和sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到主节点,所以通过选举的方式提升了一个从节点为主,这样就存在了两个master,这样会导致客户端还在老的主节点那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会将老的主节点降为从节点,这时再从新master同步数据,就会导致数据丢失

解决方法:修改redis配置,设置最少的从节点数量min-replicas-to-write以及缩短主从数据同步的延迟时间min-replicas-max-lag,达不到要求就拒绝请求

 redis的分片集群的作用

答:1、集群中有多个master,每隔master保存不同数据

2、每个master都可以有多个slave节点

3、master之间通过ping监测彼此健康状态

4、客户端请求可以访问集群任意节点,最终都会被转发到正确节点

 redis分片集群中数据是怎么存储和读取的

答:1、redis分片集群引入了哈希槽的概念,redis集群有16384个哈希槽

2、将16384个插槽分配到不同的实例(master)

3、读写数据时根据key的有效部分计算哈希值,对16384取余(有效部分,如果key前面有大括号,大括号里的内容就是有效部分,如果没有,则以key本身作为有效部分),余数作为插槽,寻找插槽所在的实例

 redis是单线程的为什么还那么快

答:1、redis是纯内存操作,执行速度非常快

2、采用单线程,避免不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题

3、使用I/O多路复用模型,非阻塞IO

 I/O多路复用模型

答:是指利用单个线程来同时监听多个Socket,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。目前的I/O多路复用都是采用的epoll模式实现,它会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能

Redis网络模型就是使用I/O多路复用结合事件的处理器来应对多个Socket请求

1、 连接应答处理器

2、 命令回复处理器(redis6.0之后,为了提升性能,使用了多线程来处理回复事件)

命令请求处理器(redis6.0之后,将命令的转换使用了多线程,增加命令转换速度,命令执行时还是单线程)

什么是redis?

redis是一个高性能的key-value数据库,它是完全开源免费的,而且redis是一个NOSQL类型数据库,是为了解决高并发、高扩展,大数据存储等一系列的问题而产生的数据库解决方案,是一个非关系型的数据库

Reids的特点

Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。

Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个value的最大限制是1GB,不像 memcached只能保存1MB的数据,因此Redis可以用来实现很多有用的功能,比方说用他的List来做FIFO双向链表,实现一个轻量级的高性 能消息队列服务,用他的Set可以做高性能的tag系统等等。另外Redis也可以对存入的Key-Value设置expire时间,因此也可以被当作一 个功能加强版的memcached来用。

Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。

使用redis有哪些好处?

3.1 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)

3.2 支持丰富数据类型,支持string,list,set,sorted set,hash

String

常用命令 :set/get/decr/incr/mget等;

应用场景 :String是最常用的一种数据类型,普通的key/value存储都可以归为此类;

实现方式:String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr、decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。

Hash

常用命令 :hget/hset/hgetall等

应用场景 :我们要存储一个用户信息对象数据,其中包括用户ID、用户姓名、年龄和生日,通过用户ID我们希望获取该用户的姓名或者年龄或者生日;

实现方式:Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口。如图所示,Key是用户ID, value是一个Map。这个Map的key是成员的属性名,value是属性值。这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field),也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据。当前HashMap的实现有两种方式:当HashMap的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,这时对应的value的redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时redisObject的encoding字段为int。

List

常用命令 :lpush/rpush/lpop/rpop/lrange等;

应用场景 :Redis list的应用场景 非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现;

实现方式:Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。

Set

常用命令 :sadd/spop/smembers/sunion等;

应用场景 :Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的;

实现方式:set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。

Sorted Set

常用命令 :zadd/zrange/zrem/zcard等;

应用场景 :Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。

实现方式:Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。

redis相比memcached有哪些优势?

4.1 memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型4.2 redis的速度比memcached快很多 (3) redis可以持久化其数据

Memcache与Redis的区别都有哪些?

5.1 存储方式 Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。Redis有部份存在硬盘上,这样能保证数据的持久性。

5.2 数据支持类型 Memcache对数据类型支持相对简单。Redis有复杂的数据类型。

5.3 使用底层模型不同 它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

redis适用于的场景?

Redis最适合所有数据in-momory的场景,如:

6.1 会话缓存(Session Cache)

最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。

6.2 全页缓存(FPC)

除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。

6.3 队列

Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。

如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。

6.4 排行榜/计数器

Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:

当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:

ZRANGE user_scores 0 10 WITHSCORES

Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。

6.5 发布/订阅

最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。

redis的缓存失效策略和主键失效机制

作为缓存系统都要定期清理无效数据,就需要一个主键失效和淘汰策略.

在Redis当中,有生存期的key被称为volatile。在创建缓存时,要为给定的key设置生存期,当key过期的时候(生存期为0),它可能会被删除。

1、影响生存时间的一些操作

生存时间可以通过使用 DEL 命令来删除整个 key 来移除,或者被 SET 和 GETSET 命令覆盖原来的数据,也就是说,修改key对应的value和使用另外相同的key和value来覆盖以后,当前数据的生存时间不同。

比如说,对一个 key 执行INCR命令,对一个列表进行LPUSH命令,或者对一个哈希表执行HSET命令,这类操作都不会修改 key 本身的生存时间。另一方面,如果使用RENAME对一个 key 进行改名,那么改名后的 key的生存时间和改名前一样。

RENAME命令的另一种可能是,尝试将一个带生存时间的 key 改名成另一个带生存时间的 another_key ,这时旧的 another_key (以及它的生存时间)会被删除,然后旧的 key 会改名为 another_key ,因此,新的 another_key 的生存时间也和原本的 key 一样。使用PERSIST命令可以在不删除 key 的情况下,移除 key 的生存时间,让 key 重新成为一个persistent key 。

2、如何更新生存时间

可以对一个已经带有生存时间的 key 执行EXPIRE命令,新指定的生存时间会取代旧的生存时间。过期时间的精度已经被控制在1ms之内,主键失效的时间复杂度是O(1),

EXPIRE和TTL命令搭配使用,TTL可以查看key的当前生存时间。设置成功返回 1;当 key 不存在或者不能为 key 设置生存时间时,返回 0 。

最大缓存配置 在 redis 中,允许用户设置最大使用内存大小 server.maxmemory 默认为0,没有指定最大缓存,如果有新的数据添加,超过最大内存,则会使redis崩溃,所以一定要设置。redis 内存数据集大小上升到一定大小的时候,就会实行数据淘汰策略。redis 提供 6种数据淘汰策略:

volatile-lru: 从已设置过期时间的数据集( server.db\[i\].expires)中挑选最近最少使用的数据淘汰

volatile-ttl: 从已设置过期时间的数据集( server.db\[i\].expires)中挑选将要过期的数据淘汰

volatile-random: 从已设置过期时间的数据集( server.db\[i\].expires)中任意选择数据淘汰

allkeys-lru: 从数据集( server.db\[i\].dict)中挑选最近最少使用的数据淘汰

allkeys-random: 从数据集( server.db\[i\].dict)中任意选择数据淘汰

no-enviction(驱逐): 禁止驱逐数据

注意这里的6种机制,volatile和allkeys规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据,后面的lru、ttl以及random是三种不同的淘汰策略,再加上一种no-enviction永不回收的策略。

使用策略规则:

1、 如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用allkeys-lru2、 如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random

三种数据淘汰策略:

ttl和random比较容易理解,实现也会比较简单。主要是Lru最近最少使用淘汰策略,设计上会对key 按失效时间排序,然后取最先失效的key进行淘汰

为什么redis需要把所有数据放到内存中?

Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以redis具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘I/O速度为严重影响redis的性能。在内存越来越便宜的今天,redis将会越来越受欢迎。

如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。

Redis是单进程单线程的

redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销

redis的并发竞争问题如何解决?

Redis为单进程单线程模式,采用队列模式将并发访问变为串行访问。Redis本身没有锁的概念,Redis对于多个客户端连接并不存在竞争,但是在Jedis客户端对Redis进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是

由于客户端连接混乱造成。对此有2种解决方法:

10.1 客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。

10.2 服务器角度,利用setnx实现锁。注:对于第一种,需要应用程序自己处理资源的同步,可以使用的方法比较通俗,可以使用synchronized也可以使用lock;第二种需要用到Redis的setnx命令,但是需要注意一些问题。

redis常见性能问题和解决方案

11.1 Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。

11.2 Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久

化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。

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

11.4 Redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。

redis事物的了解CAS(check-and-set 操作实现乐观锁)?

和众多其它数据库一样,Redis作为NoSQL数据库也同样提供了事务机制。在Redis中,MULTI/EXEC/DISCARD/WATCH这四个命令是我们实现事务的基石。相信对有关系型数据库开发经验的开发者而言这一概念并不陌生,即便如此,我们还是会简要的列出

Redis中

事务的实现特征:

12.1 在事务中的所有命令都将会被串行化的顺序执行,事务执行期间,Redis不会再为其它客户端的请求提供任何服务,从而保证了事物中的所有命令被原子的执行。

12.2 和关系型数据库中的事务相比,在Redis事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。

12.3 我们可以通过MULTI命令开启一个事务,有关系型数据库开发经验的人可以将其理解为"BEGIN TRANSACTION"语句。在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过执行EXEC/DISCARD命令来提交/回滚该事务内的所有操作。这两个Redis命令可被视为等同于关系型数据库中的COMMIT/ROLLBACK语句。

12.4 在事务开启之前,如果客户端与服务器之间出现通讯故障并导致网络断开,其后所有待执行的语句都将不会被服务器执行。然而如果网络中断事件是发生在客户端执行EXEC命令之后,那么该事务中的所有命令都会被服务器执行。

12.5 当使用Append-Only模式时,Redis会通过调用系统函数write将该事务内的所有写操作在本次调用中全部写入磁盘。然而如果在写入的过程中出现系统崩溃,如电源故障导致的宕机,那么此时也许只有部分数据被写入到磁盘,而另外一部分数据却已经丢失。Redis服务器会在重新启动时执行一系列必要的一致性检测,一旦发现类似问题,就会立即退出并给出相应的错误提示。此时,我们就要充分利用Redis工具包中提供的redis-check-aof工具,该工具可以帮助我们定位到数据不一致的错误,并将已经写入的部分数据进行回滚。修复之后我们就可以再次重新启动Redis服务器了。

WATCH命令和基于CAS的乐观锁?

在Redis的事务中,WATCH命令可用于提供CAS(check-and-set)功能。假设我们通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Null multi-bulk应答以通知调用者事务

执行失败。例如,我们再次假设Redis中并未提供incr命令来完成键值的原子性递增,如果要实现该功能,我们只能自行编写相应的代码。其伪码如下:

val = GET mykey val = val + 1 SET mykey $val

以上代码只有在单连接的情况下才可以保证执行结果是正确的,因为如果在同一时刻有多个客户端在同时执行该段代码,那么就会出现多线程程序中经常出现的一种错误场景--竞态争用(race condition)。比如,客户端A和B都在同一时刻读取了mykey的原有值,假设该值为10,此后两个客户端又均将该值加一后set回Redis服务器,这样就会导致mykey的结果为11,而不是我们认为的12。为了解决类似的问题,我们需要借助WATCH命令的帮助,见如下代码:

WATCH mykey val = GET mykey val = val + 1 MULTI SET mykey $val EXEC

和此前代码不同的是,新代码在获取mykey的值之前先通过WATCH命令监控了该键,此后又将set命令包围在事务中,这样就可以有效的保证每个连接在执行EXEC之前,如果当前连接获取的mykey的值被其它连接的客户端修改,那么当前连接的EXEC命令将执行失败。这样调用者在判断返回值后就可以获悉val是否被重新设置成功。

使用过Redis分布式锁么,它是什么回事?

先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。

这时候对方会告诉你说你回答得不错,然后接着问如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?

这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。紧接着你需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结果是你主动思考出来的,然后回答:我记得set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!对方这时会显露笑容,心里开始默念:摁,这小子还不错。

假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?

使用keys指令可以扫出指定模式的key列表。

对方接着追问:如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?

这个时候你要回答redis关键的一个特性:redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。

使用过Redis做异步队列么,你是怎么用的?

一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。

如果对方追问可不可以不用sleep呢?list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。

如果对方追问能不能生产一次消费多次呢?使用pub/sub主题订阅者模式,可以实现1:N的消息队列。

如果对方追问pub/sub有什么缺点?在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。

如果对方追问redis如何实现延时队列?我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么详细。但是你很克制,然后神态自若的回答道:使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。

到这里,面试官暗地里已经对你竖起了大拇指。但是他不知道的是此刻你却竖起了中指,在椅子背后。

如果有大量的key需要设置同一时间过期,一般需要注意什么?

如果大量的key过期时间设置的过于集中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。

Redis如何做持久化的?

bgsave做镜像全量持久化,aof做增量持久化。因为bgsave会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要aof来配合使用。在redis实例重启时,会使用bgsave持久化文件重新构建内存,再使用aof重放近期的操作指令来实现完整恢复重启之前的状态。

对方追问那如果突然机器掉电会怎样?取决于aof日志sync属性的配置,如果不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。但是在高性能的要求下每次都sync是不现实的,一般都使用定时sync,比如1s1次,这个时候最多就会丢失1s的数据。

对方追问bgsave的原理是什么?你给出两个词汇就可以了,fork和cow。fork是指redis通过创建子进程来进行bgsave操作,cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。

Pipeline有什么好处,为什么要用pipeline

可以将多次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性。使用redis-benchmark进行压测的时候可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。

Redis的同步机制了解么?

Redis可以使用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

是否使用过Redis集群,集群的原理是什么?

Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。

Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。

  • 41
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值