Redis常见面试题
1.什么是redis
redis 是一个基于内存的高性能key-value数据库。整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的key-value db。
2.为什么用redis/为什么用缓存
(1)高性能
假如用户第一次访问数据库中某些数据,这个过程会比较慢,因为是从硬盘上读取的,将用户访问的数据存放在缓存中,这样下一次访问这些数据的时候就会直接从缓存中获取,操作缓存是直接操作内存,所以速度相当快,如果数据库中的数据对应的数据改变后,同步改变缓存中相应的数据即可。
(2)高并发
直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存中而不会经过数据库。
3.redis支持数据类型
redis 有 5 种基础数据结构,分别为:string (字符串)、list (列表)、set (集合)、hash (哈希) 和 zset (有序集合)。
string字符串:
格式: set key value
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
string类型是redis最基本的数据类型,一个键最大能存储512mb。
hash(哈希)
格式: hmset name key1 value1 key2 value2
redis hash 是一个键值(key=>value)对集合。
redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
list(列表)
redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
格式: lpush name value
在 key 对应 list 的头部添加字符串元素
格式: rpush name value
在 key 对应 list 的尾部添加字符串元素
格式: lrem name index
key 对应 list 中删除 count 个和 value 相同的元素
格式: llen name
返回 key 对应 list 的长度
set(集合)
格式: sadd name value
redis的set是string类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是o(1)。
zset(sorted set:有序集合)
格式: zadd name score value
redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
4.什么是redis持久化?redis持久化有哪几种持久方式?优缺点是什么?
redis是基于内存操作的,内存断电即失,redis持久化就是把内存的数据写到磁盘中去,防止服务器宕机,内存数据丢失。redis持久化提供两个两种方式rdf(默认)和aof。
rdb:
redis把数据快照存放在磁盘上的二进制文件中,文件名为dump.rdb。你可以配置redis的持久化策略,例如数据集中每n秒钟有超过m次更新,就将数据写入磁盘;或者你可以手工调用命令save或bgsave。
当redis意外宕机,会丢失部分数据。
aof:
aof(append-only-file)持久化即记录所有变更数据库状态的指令,以append的形式追加保存到aof文件中。在服务器下次启动时,就可以通过载入和执行aof文件中保存的命令,来还原服务器关闭前的数据库状态。
对于相同的数据来说,aof文件大小通常要大于rdb文件。aof持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。所以redis意外宕机,redis只会丢失最后一次数据。
5.什么是缓存穿透?如何避免?
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如db)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
如何避免?
1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。
2:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的bitmap中,查询时通过该bitmap过滤。
6.什么是缓存击穿?如何避免
对于设置了过期时间的 key,缓存在某个时间点过期的时候,恰好这时间点对这个 key 有大量的并发请求过来,这些请求发现缓存过期一般都会从后端 db 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把 db 压垮。
解决方案:
i.使用互斥锁:当缓存失效时,不立即去 load db,先使用如 redis 的 setnx 去设置一个互斥锁,当操作成功返回时再进行 load db 的操作并回设缓存,否则重试 get 缓存的方法。
同时保证对于每个key同时只有一个线程去查询后端,其他线程没有获得锁的权限,只需要等待即可。
ii 永远不过期:物理不过期,但逻辑过期(后台异步线程去刷新)。
7.什么是缓存雪崩?如何避免
设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到 db, db 瞬时压力过重雪崩。与缓存击穿的区别:雪崩是很多 key,击穿是某一个key 缓存。
解决方案:
将缓存失效时间分散开,比如可以在原有的失效时间基础上增加一个随机值,比如 1-5 分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
8.redis为什么单线程还这么快?
redis是将所有数据全部放在内存中的,所以说使用单线程去操作效率就是最高的。多线程会产生cpu上下文切换,将会是比较耗时的操作。对于内存系统来说,如果没有上下文切换效率就是最高的。
9、redis内存淘汰机制(mysql里有2000w数据,redis中只存20w的数据,如何保证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(驱逐):当内存数据不足以容纳新写入的数据时。新写入操作时就会报错。
10.redis主从复制
是指将一台redis服务器的数据,复制到其他的redis服务器。前者称为主节点(master/leader),后者称为从结点(slave/follower);数据的复制是单向的,只能由主节点到从结点。master以写为主,slave以读为主。
11.redis事务
redis事务的本质是一组命令的集合,一个事务中所有命令都会被序列化,在事务执行过程中,会按照顺序执行。
redis单条命令保证原子性,但是redis事务中不保证原子性,不并且redis事务没有隔离级别的概念。
redis事务命令:
开启事务(multi)
执行事务(exec)
放弃事务(discard)