Redis是一个非常火热的一个“专有词”在互联网和大数据中占有一席之位。Redis是一款高性能的NOSQL系列的非关系型数据库,也是之所以为何这么火的原因了吧!下面是小编为大家总结的一些关于Redis的面试题(个人总结)希望大家会喜欢。
Redis的基础
什么是Redis ?简述它的优缺点:Redis的全称是Remote DictionarylServer ,本质上是一个Key-Value类型的内存数据库,很像memcached ,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
为什么Redis需要把所有数据放到内存中:Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以redis具有快速和数据持久化的特征,如果不将数据放在内存中,磁盘I/O速度为严重影响redis的性能。在内存越来越便宜的今天, redis将会越来越受欢迎,如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。
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的缓存穿透、缓存崩溃、缓存击穿的理解?
1.缓存穿透:是指查询一个数据库一定不存在的数据。
正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或 者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。
发生场景:
如果传入的参数为-1,会是怎么样?这个-1,就是一定不存在的对象。就会每次都去查询数据库,而每次查询都是空,每次又都不会进行缓存。假如有恶意攻击,就可以利用这个漏洞,对数据库造成压力,甚至压垮数据库。即便是采用UUID,也是很容易找到一个不存在的KEY,进行攻击。
2.缓存击穿: 是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
发生场景:某一个商品爆款的时候会导致这种情况的产生。解决方案:
设置缓存永不过期(或者过期时间比较大)。
设置双重缓存备份A和B,当A缓存失效时,使用B缓存。
3.缓存雪崩:缓存在同一时间内大量键过期(失效),接着来的一大波请求瞬间都落在了数据库中导致连接异常。
解决方案:
也是像解决缓存穿透一样加锁排队;
建立备份缓存,缓存A和缓存B,A设置超时时间,B不设置超时时间,先从A读缓存,A没有读B,并且更新A缓存和B缓存;
设置缓存超时时间的时候加上一个随机的时间长度,比如这个缓存key的超时时间是固定的5分钟加上随机的2分钟,酱紫可从一定程度上避免雪崩问题;
Redis 和 Memecache 有什么区别?
Redis和Memcache都是将数据存放在内存中,都是内存数据库。不过memcache还可用于缓存其他东西,例如图片、视频等等。
Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储。
虚拟内存–Redis当物理内存用完时,可以将一些很久没用到的value 交换到磁盘
过期策略–memcache在set时就指定,例如set key1 0 0 8,即永不过期。Redis可以通过例如expire 设定,例如expire name 10
分布式–设定memcache集群,利用magent做一主多从;redis可以做一主多从。都可以一主一从
存储数据安全–memcache挂掉后,数据没了;redis可以定期保存到磁盘(持久化)
灾难恢复–memcache挂掉后,数据不可恢复; redis数据丢失后可以通过aof恢复
Redis支持数据的备份,即master-slave模式的数据备份。
Redis的分布式锁如何实现,有什么优缺点?
1.分布式锁需要解决的问题
互斥性:任意时刻只能有一个客户端拥有锁,不能同时多个客户端获取
安全性:锁只能被持有该锁的用户删除,而不能被其他用户删除
死锁:获取锁的客户端因为某些原因而宕机,而未能释放锁,其他客户端无法获取此锁,需要有机制来避免该类问题的发生
容错:当部分节点宕机,客户端仍能获取锁或者释放锁。
2.如何通过Redis实现分布式锁:(非完善方法)
SETNX key value :如果key不存在,则创建并赋值
时间复杂度: 0(1)
返回值:设置成功,返回1;设置失败,返回0。
但是此时我们获取的key是长期有效的,所以我们应该如何解决长期有效的问题呢?
如何解决SETNX长期有效的问题?
EXPIRE key seconds
设置key的生存时间,当key过期时(生存时间为0) ,会被自动删除
缺点:原子性得不到满足
3.如何通过Redis实现分布式锁:(正确方式)
SET key value [EX seconds] [PX milliseconds] [NX|XX]
EX second :设置键的过期时间为second秒
PX millisecond :设置键的过期时间为millisecond毫秒
NX :只在键不存在时,才对键进行设置操作
XX:只在键已经存在时,才对键进行设置操作
SET操作成功完成时,返回OK ,否则返回nil
4.大量的key同时过期的注意事项
集中过期,由于清除大量的key很耗时,会出现短暂的卡顿现象
解放方案:在设置key的过期时间的时候,给每个key加上随机值
特殊场景1:超时后使用del 导致误删其他线程的锁
又是一个极端场景,假如某线程成功得到了锁,并且设置的超时时间是30秒。
如果某些原因导致线程B执行的很慢很慢,过了30秒都没执行完,这时候锁过期自动释放,线程B得到了锁。
随后,线程A执行完了任务,线程A接着执行del指令来释放锁。但这时候线程B还没执行完,线程A实际上删除的是线程B加的锁。
怎么避免这种情况呢?可以在del释放锁之前做一个判断,验证当前的锁是不是自己加的锁。
至于具体的实现,可以在加锁的时候把当前的线程ID当做value,并在删除之前验证key对应的value是不是自己线程的ID。
if判断和释放锁是两个独立操作,不是原子性。
我们都是追求极致的程序员,所以这一块要用Lua脚本来实现:
这样一来,验证和删除过程就是原子操作了。
特殊场景2: 出现并发的可能性
还是刚才的场景1,虽然我们避免了线程A误删掉key的情况,但是同一时间有A,B两个线程在访问代码块,仍然是不完美的。
怎么办呢?我们可以让获得锁的线程开启一个守护线程,用来给快要过期的锁“续航”。
当过去了29秒,线程A还没执行完,这时候守护线程会执行expire指令,为这把锁“续命”20秒。守护线程从第29秒开始执行,每20秒执行一次。
当线程A执行完任务,会显式关掉守护线程。
另一种情况,如果节点1 忽然断电,由于线程A和守护线程在同一个进程,守护线程也会停下。这把锁到了超时的时候,没人给它续命,也就自动释放了。
由于文章限制小编把剩下的面试题整理成文档和视频的形势列在下方,面试哪里还不懂?赶紧领取资料吧!
获取方式:转发文章关注小编,++++Vx : bjmsb2020来进行领取吧~~~~