当前位置: 首页 > 科技观察

关于Redis最常见的面试题

时间:2023-03-13 14:30:27 科技观察

1.什么是Redis?Redis是一个基于内存的高性能键值数据库。2.Reids的特点  Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库加载在内存中运行,定期通过异步操作将数据库数据刷到硬盘上存储的依据。由于是纯内存操作,Redis性能优异,每秒可处理超过100,000次读写操作,是目前已知最快的Key-ValueDB。Redis的优秀不仅仅在于性能。Redis最大的魅力在于它支持保存多种数据结构。另外单个值的最大限制是1GB,不像memcached只能保存1MB的数据,所以Redis可以用来实现很多有用的功能。功能。比如用他的List做一个FIFO双向链表,实现一个轻量级的高性能消息队列服务,用他的Set做一个高性能的标签系统等等。此外,Redis还可以为存储的Key-Value设置过期时间,因此也可以作为memcached的增强版使用。Redis的主要缺点是数据库容量受限于物理内存,无法用于海量数据的高性能读写。因此Redis适合的场景主要局限于小数据量的高性能运算和计算。3、使用redis有什么好处?  速度快,因为数据存在内存中,类似于HashMap,HashMap的优点是查找和运算的时间复杂度为O(1)支持丰富的数据类型,支持string、list、set、sortedset、hash支持事务和操作是原子的。所谓原子性,就是所有的数据变化要么被执行,要么根本不执行。丰富的功能:可用于缓存,消息,可key设置过期时间,过期自动删除。4.redis相对于memcached有什么优势?  memcached的所有值都是简单的字符串。Redis作为其替代者,支持更丰富的数据类型。Redis比memcached快得多。Redis可以持久化它的数据。5、Memcache和Redis有什么区别??存储方式Memecache将所有数据存储在内存中,断电后挂掉,数据不能超过内存大小。Redis的一部分存储在硬盘上,保证了数据的持久化。数据支持类型Memcache支持相对简单的数据类型。Redis具有复杂的数据类型。使用不同的底层模型,与客户端通信的底层实现方式和应用协议也不同。Redis直接自己搭建了VM机制,因为一般系统调用系统函数的话,会浪费一定的时间去移动和请求。6、Redis常见性能问题及解决方法: Master写内存快照,save命令调度rdbSave函数,会阻塞主线程工作。当快照比较大时,对性能的影响非常大,服务会断断续续的挂掉。所以Master最好不要写内存快照。MasterAOF持久化,如果不重写AOF文件,这种持久化方式对性能影响最小,但是AOF文件会不断增长,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,尤其不要开启内存快照进行持久化。如果数据很关键,一个Slave会启用AOF备份数据,策略是每秒同步一次。Master调用BGREWRITEAOF重写AOF文件。AOF在改写时会占用大量的CPU和内存资源,导致服务负载高,服务短时间停顿。Redis主从复制的性能问题。为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。数据热点数据相关知识:当redis内存数据集的大小上升到一定规模时,会执行数据淘汰策略(回收策略)。redis提供了6种数据淘汰策略:volatile-lru:从数据集(server.db[i].expires)中选择最近最少使用的有过期时间的数据进行淘汰volatile-ttl:从有过期时间的数据集中淘汰set(server.db[i].expires)选择即将过期的数据。消除volatile-random:从数据集(server.db[i].expires)中选择有过期时间的数据。Eliminateallkeys-lru:从数据集中选择最近最少使用的数据(server.db[i].dict)Eliminateallkeys-random:从数据集中随机选择数据(server.db[i].dict)淘汰no-enviction(驱逐):不驱逐数据 8。请使用Redis和任何语言实现一个恶意登录防护代码,限制每个用户ID在1小时内最多登录5次。具体的登录函数或者函数可以当做一个空函数,就不用写的很详细了。用列表实现:列表中每个元素代表登录时间,只要最近5次登录时间与当前时间相差1小时以内,则禁止??登录。Python写的代码如下:#!/usr/bin/envpython3importredisimportsysimporttimer=redis。StrictRedis(host='127.0.0.1',port=6379,db=0)try:id=sys.argv[1]except:print('inputargumenterror')sys.exit(0)ifr.llen(id)>=5andtime.time()–float(r.lindex(id,4))<=3600:print("youareforbiddenlogining")else:print('youareallowedtologin')r.lpush(id,time.time())#login_func()9。为什么redis需要把所有的数据都放在内存中? Redis为了达到最快的读写速度,将所有数据读入内存,并异步将数据写入磁盘。所以redis具有速度快,数据持久化的特点。如果数据不放在内存中,磁盘I/O速度会严重影响redis的性能。在内存越来越便宜的今天,redis会越来越流行。如果设置了最大内存使用量,现有数据记录数达到内存限制后无法插入新值。10、Redis是单进程单线程redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销11、如何解决redis的并发竞争问题?Redis是单进程单线程模式,使用Queue模式将并发访问变为串行访问。Redis本身没有锁的概念。Redis不存在多个客户端连接的竞争,但是当Jedis客户端并发访问Redis时,会出现连接超时、数据转换错误、阻塞、客户端关闭连接等问题。这些问题都是由于客户端连接混乱造成的。对此有两种解决方案:1、从客户端的角度,为了保证各个客户端正常有序的与Redis通信,将连接池化,客户端的读写采用内部锁synchronized编写Redis操作。2、从服务器的角度,使用setnx实现锁。注意:对于第一种,应用需要自己处理资源的同步。可以使用的方法比较流行,可以使用synchronized或者lock;第二种需要使用Redis的setnx命令,但是需要注意一些问题。12.对redis事物CAS的理解(check-and-set操作实现乐观锁)?与许多其他数据库一样,Redis作为NoSQL数据库也提供了事务机制。在Redis中,MULTI/EXEC/DISCARD/WATCH这四个命令是我们事务实现的基石。相信对于有过关系型数据库开发经验的开发者来说,这个概念并不陌生。尽管如此,我们还是简单罗列一下Redis中事务的实现特点:原子执行。与关系型数据库中的事务相比,Redis事务中如果某条命令执行失败,后续命令仍会执行。我们可以通过MULTI命令来启动一个事务,有关系数据库开发经验的人可以理解为“BEGINTRANSACTION”语句。这条语句之后执行的所有命令都将被视为事务内的操作,最后我们可以通过执行EXEC/DISCARD命令来提交/回滚事务内的所有操作。这两个Redis命令可以认为等同于关系数据库中的COMMIT/ROLLBACK语句。在事务开启之前,如果客户端与服务端通信失败,网络断开,则后续所有要执行的语句都不会被服务端执行。但是,如果网络中断事件发生在客户端执行EXEC命令之后,那么事务中的所有命令都会被服务端执行。当使用Append-Only模式时,Redis会在本次调用中通过调用系统函数write将事务中的所有写操作写入磁盘。但是,如果在写入过程中出现系统崩溃,比如断电导致宕机,那么此时可能只有一部分数据写入了磁盘,而另一部分数据已经丢失。Redis服务器在重启时会进行一系列必要的一致性检查,一旦发现类似问题,会立即退出并给出相应的错误信息。此时,我们就需要充分利用Redis工具包中提供的redis-check-aof工具,它可以帮助我们定位数据不一致错误,回滚部分写入的数据。修复后我们可以再次重启Redis服务器。13、WATCH命令和基于CAS的乐观锁: 在Redis事务中,WATCH命令可以用来提供CAS(check-and-set)功能。假设我们在事务执行前通过WATCH命令监听多个Key,如果WATCH后任意一个Key的值发生变化,EXEC命令执行的事务将被放弃,返回一个Nullmulti-bulkresponse通知调用方事务执行失败。例如,我们再次假设Redis中没有提供incr命令来完成键值的原子递增。我们要想实现这个功能,只能自己编写相应的代码。伪代码如下:val=GETmykeyval=val+1SETmykey$val上面的代码只能保证在单连接的情况下执行结果是正确的,因为如果有多个客户端同时执行这段代码,那么就有会是多线程程序中经常出现的错误场景——racecondition。例如,客户端A和B同时读取mykey的原始值,假设值为10,然后两个客户端都对该值加1,重新设置回Redis服务器,这样就会得到mykey的结果是11,而不是我们想象的12。为了解决类似的问题,我们需要WATCH命令的帮助,看下面的代码:mykey的值,然后将set命令包裹在事务中,这样可以有效保证在每个连接执行EXEC之前,如果当前连接获取到的mykey的值被其他连接的客户端修改,执行EXEC命令的当前连接将失败。这样调用者通过判断返回值就可以知道val是否重置成功了。14、redis持久化的几种方式1、快照(snapshots)默认情况下,Redis将数据快照以二进制文件的形式存储在磁盘上,文件名为dump.rdb。可以配置Redis的持久化策略,比如如果数据集更新次数超过M次,则每隔N秒将数据写入磁盘;或者您可以手动调用命令SAVE或BGSAVE。它是如何工作的Redisforks。子进程开始将数据写入临时RDB文件。当子进程写完RDB文件后,将旧文件替换为新文件。这种方式允许Redis使用copy-on-write技术。2、AOF快照方式不是很健壮。当系统停止,或者Redis被意外杀死时,最后写入Redis的数据就会丢失。这对于一些应用来说可能不是什么大问题,但是对于可靠性要求高的应用来说,Redis并不是一个合适的选择。仅附加文件模式是另一种选择。可以在配置文件中开启AOF模式。3、虚拟内存方式当你的key小,value大的时候,使用VM的效果会更好。因为这样节省的内存比较大。当你的key不小的时候,你可以考虑用一些非常的方法,把一个很大的key变成一个很大的value。例如,您可以考虑将键和值组合成一个新值。参数vm-max-threads可以设置访问交换文件的线程数。设置最好不要超过机器的核心数。如果设置为0,则对交换文件的所有操作都将被序列化。可能会造成比较长的延迟,但是对数据的完整性有很好的保证。自己测试发现虚拟内存的性能也不错。如果数据量大,可以考虑分布式或者其他数据库。15.Redis的缓存失效策略和主键失效机制由于缓存系统要定期清理无效数据,因此需要有主键失效和淘汰策略。在Redis中,具有生命周期的键称为volatile。创建缓存时,为给定的键设置生命周期,当键过期(生命周期为0)时,它可能会被删除。1、一些影响生存时间的操作的生存时间,可以使用DEL命令删除整个key,或者用SET、GETSET命令覆盖掉原来的数据,即修改key对应的值而用另一个相同的key和value覆盖,当前数据的存活时间是不同的。例如,在键上执行INCR命令、在列表上执行LPUSH命令或在哈希表上执行HSET命令不会修改键本身的生命周期。另一方面,如果使用RENAME重命名密钥,则重命名密钥的生命周期与重命名之前相同。RENAME命令的另一种可能性是尝试将具有生命周期的密钥重命名为另一个具有生命周期的another_key。这时候旧的another_key(和它的生命周期)会被删除,然后旧的key会被重命名为another_key,所以新的another_key的生命周期和原来的key是一样的。使用PERSIST命令可以在不删除密钥的情况下移除密钥的生命周期,并使该密钥重新成为持久密钥。2.如何更新生存时间可以对已经有生存时间的key执行EXPIRE命令,新指定的生存时间会替换旧的生存时间。过期时间精度已经控制在1ms以内,主键失效时间复杂度为O(1)。EXPIRE和TTL命令一起使用,TTL可以查看key当前的生存期。设置成功返回1;当键不存在或无法为键设置生命周期时返回0。最大缓存配置:在redis中,允许用户设置使用的最大内存大小。server.maxmemory默认为0,不指定最大缓存。如果新增数据超过最大内存,redis就会崩溃,所以必须要设置。当redis内存数据集的大小上升到一定大小时,就会执行数据淘汰策略。redis提供了6种数据淘汰策略:volatile-lru:从数据集(server.db[i].expires)中选择最近最少使用的有过期时间的数据进行淘汰volatile-ttl:从有过期时间的数据集中淘汰set(server.db[i].expires)选择即将过期的数据。消除volatile-random:从数据集(server.db[i].expires)中选择有过期时间的数据。Eliminateallkeys-lru:从数据集中选择最近最少使用的数据(server.db[i].dict)Eliminateallkeys-random:从数据集中随机选择数据(server.db[i].dict)淘汰no-enviction(驱逐):不驱逐注意这里的6个机制。volatile和allkeys指定是从设置了过期时间的数据集中删除数据,还是从所有数据集中删除数据。下面的lru、ttl、random是三种不同的淘汰策略,加上永不回收的no-envictionpolicy。使用策略规则:如果数据呈现幂律分布,即有的数据访问频率高,有的数据访问频率低,则使用allkeys-lru如果数据呈现均等分布,即所有数据访问频率相同,则使用allkeys-Random三种数据淘汰策略:ttl和random比较容易理解,实现起来也比较简单。主要原因是Lru最近最少使用淘汰策略。在设计中,key会按照过期时间排序,然后淘汰最先过期的key。16、redis最适合的场景Redis也提供了持久化功能,但其实更多的是磁盘备份的功能,与传统的持久化功能有很大区别。那么你可能会有疑问。看起来Redis更像是Memcached的增强版,那么什么时候用Memcached,什么时候用Redis呢?如果单纯比较Redis和Memcached的区别,大部分人会得出以下观点:Redis不仅支持简单的k/v类型数据,还提供了list、set、zset、hash等数据结构的存储。Redis支持数据备份,即主从模式的数据备份。Redis支持数据持久化,可以将内存中的数据保存在磁盘上,重启时可以再次加载使用。1.SessionCache使用Redis最常用的场景是sessioncache。使用Redis缓存会话相对于其他存储(例如Memcached)的优势在于Redis提供了持久性。当维护一个不严格一致的缓存时,如果用户的所有购物车信息都丢失了,大多数人都会不高兴。现在,他们会吗?幸运的是,随着Redis多年来的改进,很容易找到有关如何正确使用Redis进行会话缓存的文档。甚至著名的商业平台Magento也为Redis提供了一个插件。2.FullPageCache(FPC)除了基本的sessiontoken,Redis还提供了一个非常简单的FPC平台。回到一致性问题,即使Redis实例重启,用户也不会因为磁盘持久化而看到页面加载速度下降。这是一个很大的改进,类似于PHP的本地FPC。再次以Magento为例,Magento提供了一个插件来使用Redis作为全页缓存后端。另外,对于WordPress用户,Pantheon有一个非常不错的插件wp-redis,可以帮助你以最快的速度加载你访问过的页面。3.QueueReids在内存存储引擎领域的优势之一是它提供了list和set操作,这使得Redis成为一个很好用的消息队列平台。Redis作为队列的操作类似于本地编程语言(如Python)对列表的push/pop操作。如果您在Google上快速搜索“Redis队列”,您会立即找到大量旨在使用Redis创建非常好的后端工具以满足各种队列需求的开源项目。比如Celery有后台使用Redis作为broker,可以从这里查看。4.排行榜/计数器Redis可以很好地增加或减少内存中的数字。Sets和SortedSets也让我们执行这些操作变得非常简单。Redis只是提供了这两种数据结构。所以,我们想要从排序的集合中得到前10名的用户——我们称之为“user_scores”,我们只需要这样做:当然,这假设你是根据你的用户的分数进行增量排序。如果你想返回用户和用户的分数,你需要这样执行:ZRANGEuser_scores010WITHSCORESAGoraGames是一个很好的例子,用Ruby实现,它的排行榜使用Redis存储数据,你可以在这里看到它。5.发布/订阅最后(但并非最不重要)是Redis的发布/订阅功能。发布/订阅确实有很多用例。我见过人们在社交网络连接中使用它,作为基于发布/订阅的脚本的触发器,甚至使用Redis的发布/订阅功能来构建聊天系统!(不是,是真的,你可以去查一下)。  在Redis提供的所有功能中,我觉得这是人们最不喜欢的一个,虽然它为用户提供了很多功能。