首页 > 数据库 >Redis面试题

Redis面试题

时间:2023-02-06 23:22:06浏览次数:38  
标签:面试题 缓存 数据库 Redis value 内存 key

一、Redis是什么?

Redis是一个key-value存储系统,它支持存储的value类型相对更多,包括string、list、set、zset(sorted set --有序集合)和hash。这些数据结构都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,Redis支持各种不同方式的排序。为了保证效率,数据都是缓存在内存中,Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
二、Redis都有哪些使用场景?

    Redis是基于内存的nosql数据库,可以通过新建线程的形式进行持久化,不影响Redis单线程的读写操作
    通过list取最新的N条数据
    模拟类似于token这种需要设置过期时间的场景
    发布订阅消息系统
    定时器、计数器

三、Redis有哪些功能?

1、基于本机内存的缓存

当调用api访问数据库时,假如此过程需要2秒,如果每次请求都要访问数据库,那将对服务器造成巨大的压力,如果将此sql的查询结果存到Redis中,再次请求时,直接从Redis中取得,而不是访问数据库,效率将得到巨大的提升,Redis可以定时去更新数据(比如1分钟)。

2、如果电脑重启,写入内存的数据是不是就失效了呢,这时Redis还提供了持久化的功能。

3、哨兵(Sentinel)和复制

Sentinel可以管理多个Redis服务器,它提供了监控、提醒以及自动的故障转移功能;

复制则是让Redis服务器可以配备备份的服务器;

Redis也是通过这两个功能保证Redis的高可用;

4、集群(Cluster)

单台服务器资源总是有上限的,CPU和IO资源可以通过主从复制,进行读写分离,把一部分CPU和IO的压力转移到从服务器上,但是内存资源怎么办,主从模式只是数据的备份,并不能扩充内存;

现在我们可以横向扩展,让每台服务器只负责一部分任务,然后将这些服务器构成一个整体,对外界来说,这一组服务器就像是集群一样。
四、Redis支持的数据类型有哪些?
   

  • String(字符串)
    • 简介:String是Redis最基础的数据结构类型,它是二进制安全的,可以存储图片或者序列化的对象,值最大存储为512M
    • 简单使用举例: set key valueget key
    • 应用场景:共享session、分布式锁,计数器、限流。
    • 内部编码有3种,int(8字节长整型)/embstr(小于等于39字节字符串)/raw(大于39个字节字符串)
  • Hash(哈希)

    简介:在Redis中,哈希类型是指v(值)本身又是一个键值对(k-v)结构
    简单使用举例:hset key field value 、hget key field
    内部编码:ziplist(压缩列表) 、hashtable(哈希表)
    应用场景:缓存用户信息等。
    注意点:如果开发使用hgetall,哈希元素比较多的话,可能导致Redis阻塞,可以使用hscan。而如果只是获取部分field,建议使用hmget。

  • List(列表)
    •  简介:列表(list)类型是用来存储多个有序的字符串,一个列表最多可以存储2^32-1个元素。
    • 简单实用举例:lpush key value [value ...] 、lrange key start end
    • 内部编码:ziplist(压缩列表)、linkedlist(链表)
    • 应用场景:消息队列,文章列表,
  • Set(集合)
    •  简介:集合(set)类型也是用来保存多个的字符串元素,但是不允许重复元素
    • 简单使用举例:sadd key element [element ...]、smembers key
    • 内部编码:intset(整数集合)、hashtable(哈希表)
    • 注意点:smembers和lrange、hgetall都属于比较重的命令,如果元素过多存在阻塞Redis的可能性,可以使用sscan来完成。
    • 应用场景:用户标签,生成随机数抽奖、社交需求。
  • zset(有序集合)
    • 简介:已排序的字符串集合,同时元素不能重复
    • 简单格式举例:zadd key score member [score member ...]zrank key member
    • 底层内部编码:ziplist(压缩列表)skiplist(跳跃表)
    • 应用场景:排行榜,社交需求(如用户点赞)。

它还有三种特殊的数据结构类型

  • Geospatial
  • Hyperloglog
  • Bitmap


五、Redis取值存值问题

1、先把Redis的连接池拿出来

    JedisPool pool = new JedisPool(new JedisPoolConfig(),"127.0.0.1");
     
    Jedis jedis = pool.getResource();

2、存取值

    jedis.set("key","value");
    jedis.get("key");
    jedis.del("key");
    //给一个key叠加value
    jedis.append("key","value2");//此时key的值就是value + value2;
    //同时给多个key进行赋值:
    jedis.mset("key1","value1","key2","value2");

3、对map进行操作

    Map<String,String> user = new HashMap();
    user.put("key1","value1");
    user.put("key2","value2");
    user.put("key3","value3");
    //存入
    jedis.hmset("user",user);
    //取出user中key1
    List<String> nameMap = jedis.hmget("user","key1");
    //删除其中一个键值
    jedis.hdel("user","key2");
    //是否存在一个键
    jedis.exists("user");
    //取出所有的Map中的值:
    Iterator<String> iter = jedis.hkeys("user").iterator();
    while(iter.next()){
        jedis.hmget("user",iter.next());
    }

六、Redis为什么是单线程的?
    代码更清晰,处理逻辑更简单;
    不用考虑各种锁的问题,不存在加锁和释放锁的操作,没有因为可能出现死锁而导致的性能问题;
    不存在多线程切换而消耗CPU;
    无法发挥多核CPU的优势,但可以采用多开几个Redis实例来完善;

七、Redis真的是单线程的吗?
Redis6.0之前是单线程的,Redis6.0之后开始支持多线程;
redis内部使用了基于epoll的多路服用,也可以多部署几个redis服务器解决单线程的问题;
redis主要的性能瓶颈是内存和网络;
内存好说,加内存条就行了,而网络才是大麻烦,所以redis6内存好说,加内存条就行了;
而网络才是大麻烦,所以redis6.0引入了多线程的概念,
redis6.0在网络IO处理方面引入了多线程,如网络数据的读写和协议解析等,需要注意的是,执行命令的核心模块还是单线程的。
八、Redis持久化有几种方式?

redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)。

RDB,简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上;

AOF,则是换了一个角度来实现持久化,那就是将redis执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。

其实RDB和AOF两种方式也可以同时使用,在这种情况下,如果redis重启的话,则会优先采用AOF方式来进行数据恢复,这是因为AOF方式的数据恢复完整度更高。

如果你没有数据持久化的需求,也完全可以关闭RDB和AOF方式,这样的话,redis将变成一个纯内存数据库,就像memcache一样。
九、Redis和 memecache 有什么区别?

1、Redis相比memecache,拥有更多的数据结构和支持更丰富的数据操作。

(1)Redis支持key-value,常用的数据类型主要有String、Hash、List、Set、Sorted Set。

(2)memecache只支持key-value。

2、内存使用率对比,Redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于memecache。

3、性能对比:Redis只使用单核,memecache使用多核。

4、Redis支持磁盘持久化,memecache不支持。

Redis可以将一些很久没用到的value通过swap方法交换到磁盘。

5、Redis支持分布式集群,memecache不支持。
十、Redis支持的 java 客户端都有哪些?

Redisson、Jedis、lettuce 等等,官方推荐使用 Redisson。
十一、jedis 和 redisson 有哪些区别?

Jedis 和 Redisson 都是Java中对Redis操作的封装。Jedis 只是简单的封装了 Redis 的API库,可以看作是Redis客户端,它的方法和Redis 的命令很类似。Redisson 不仅封装了 redis ,还封装了对更多数据结构的支持,以及锁等功能,相比于Jedis 更加大。但Jedis相比于Redisson 更原生一些,更灵活。
十二、什么是缓存穿透?怎么解决?

1、缓存穿透

一般的缓存系统,都是按照key去缓存查询,如果不存在对用的value,就应该去后端系统查找(比如DB数据库)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。

2、怎么解决?

对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert之后清理缓存。
 
对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该Bitmap过滤。

3、缓存雪崩

当缓存服务器重启或者大量缓存集中在某一时间段失效,这样在失效的时候,会给后端系统带来很大的压力,导致系统崩溃。

4、如何解决?

    在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其它线程等待;
    做二级缓存;
    不同的key,设置不同的过期时间,让缓存失效的时间尽量均匀;

十三、怎么保证缓存和数据库数据的一致性?

1、淘汰缓存

数据如果为较为复杂的数据时,进行缓存的更新操作就会变得异常复杂,因此一般推荐选择淘汰缓存,而不是更新缓存。

2、选择先淘汰缓存,再更新数据库

假如先更新数据库,再淘汰缓存,如果淘汰缓存失败,那么后面的请求都会得到脏数据,直至缓存过期。

假如先淘汰缓存再更新数据库,如果更新数据库失败,只会产生一次缓存穿透,相比较而言,后者对业务则没有本质上的影响。

3、延时双删策略

如下场景:同时有一个请求A进行更新操作,另一个请求B进行查询操作。

    请求A进行写操作,删除缓存
    请求B查询发现缓存不存在
    请求B去数据库查询得到旧值
    请求B将旧值写入缓存
    请求A将新值写入数据库

次数便出现了数据不一致问题。采用延时双删策略得以解决。

    public void write(String key,Object data){
        redisUtils.del(key);
        db.update(data);
        Thread.Sleep(100);
        redisUtils.del(key);
    }

这么做,可以将1秒内所造成的缓存脏数据,再次删除。这个时间设定可根据俄业务场景进行一个调节。

4、数据库读写分离的场景

两个请求,一个请求A进行更新操作,另一个请求B进行查询操作。

    请求A进行写操作,删除缓存
    请求A将数据写入数据库了,
    请求B查询缓存发现,缓存没有值
    请求B去从库查询,这时,还没有完成主从同步,因此查询到的是旧值
    请求B将旧值写入缓存
    数据库完成主从同步,从库变为新值

依旧采用延时双删策略解决此问题。
十四、Redis,什么是缓存穿透?怎么解决?

1、缓存穿透

一般的缓存系统,都是按照key去缓存查询,如果不存在对用的value,就应该去后端系统查找(比如DB数据库)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。

2、怎么解决?

对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert之后清理缓存。
 
对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该Bitmap过滤。

3、缓存雪崩

当缓存服务器重启或者大量缓存集中在某一时间段失效,这样在失效的时候,会给后端系统带来很大的压力,导致系统崩溃。

4、如何解决?

    在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其它线程等待;
    做二级缓存;
    不同的key,设置不同的过期时间,让缓存失效的时间尽量均匀;

十五、Redis怎么实现分布式锁?

使用Redis实现分布式锁

redis命令:set users 10 nx ex 12   原子性命令

    //使用uuid,解决锁释放的问题
    @GetMapping
    public void testLock() throws InterruptedException {
        String uuid = UUID.randomUUID().toString();
        Boolean b_lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 10, TimeUnit.SECONDS);
        if(b_lock){
            Object value = redisTemplate.opsForValue().get("num");
            if(StringUtils.isEmpty(value)){
                return;
            }
            int num = Integer.parseInt(value + "");
            redisTemplate.opsForValue().set("num",++num);
            Object lockUUID = redisTemplate.opsForValue().get("lock");
            if(uuid.equals(lockUUID.toString())){
                redisTemplate.delete("lock");
            }
        }else{
            Thread.sleep(100);
            testLock();
        }
    }

备注:可以通过lua脚本,保证分布式锁的原子性。
十六、Redis分布式锁有什么缺陷?

Redis 分布式锁不能解决超时的问题,分布式锁有一个超时时间,程序的执行如果超出了锁的超时时间就会出现问题。

Redis容易产生的几个问题:

    锁未被释放
    B锁被A锁释放了
    数据库事务超时
    锁过期了,业务还没执行完
    Redis主从复制的问题

十七、Redis如何做内存优化?

1、缩短键值的长度

    缩短值的长度才是关键,如果值是一个大的业务对象,可以将对象序列化成二进制数组;
    首先应该在业务上进行精简,去掉不必要的属性,避免存储一些没用的数据;
    其次是序列化的工具选择上,应该选择更高效的序列化工具来降低字节数组大小;
    以JAVA为例,内置的序列化方式无论从速度还是压缩比都不尽如人意,这时可以选择更高效的序列化工具,如: protostuff,kryo等

2、共享对象池

对象共享池指Redis内部维护[0-9999]的整数对象池。创建大量的整数类型redisObject存在内存开销,每个redisObject内部结构至少占16字节,甚至超过了整数自身空间消耗。所以Redis内存维护一个[0-9999]的整数对象池,用于节约内存。 除了整数值对象,其他类型如list,hash,set,zset内部元素也可以使用整数对象池。因此开发中在满足需求的前提下,尽量使用整数对象以节省内存。

3、字符串优化

4、编码优化

5、控制key的数量
————————————————
版权声明:本文为CSDN博主「哪 吒」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/guorui_java/article/details/117194603

标签:面试题,缓存,数据库,Redis,value,内存,key
From: https://www.cnblogs.com/minch/p/17097018.html

相关文章

  • 前端面试题学习-个人总结笔记 Day 4 JS
    前端面试题学习-个人总结笔记Day4JS这是看别人总结的基础上再度总结的,总结的链接如下链接1.对闭包的理解+严格模式+判断对象是否属于某个类+map某个案例2.......
  • 代码随想录算法Day04| 24. 两两交换链表中的节点 19.删除链表的倒数第N个节点 面试题
    24.两两交换链表中的节点题目链接: 24.两两交换链表中的节点-力扣(LeetCode)题目给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点......
  • Redis 实战篇 Part 1
    ......
  • 前端面试题学习-个人总结笔记 Day 3 JS
    前端面试题学习-个人总结笔记Day3JS这是看别人总结的基础上再度总结的,总结的链接如下链接1.JS基本数据类型+内部属性[[Class]]+内置对象2.内置对象3.JS......
  • Redis性能调优基础
    Redis架构分析              计算交集和并集          命令执行流程            RDB快照机制......
  • 小连姐面试题
    面试小意见温馨提示:整体面试:心态要好、语速要慢、形态举止沉稳、语言表达简练、逻辑阐述清晰第一部分:自我介绍1、这位考生,请做一下自我介绍!注意点:在进行自我介绍时,态......
  • Redis常用命令之操作List类型
    场景Centos中Redis的下载编译与安装(超详细):霸道的程序猿获取编程相关电子书、教程推送与免费下载。实现List类型是一个链表结构的集合,其主要功能有push、pop、获取元素等......
  • Redis常用命令之操作Set(集合)
    号霸道的程序猿获取编程相关电子书、教程推送与免费下载。实现set集合是string类型的无序集合,set是通过hashtable实现的,对集合我们可以取交集、并集、差集SADD命令语法SAD......
  • #yyds干货盘点# LeetCode面试题:N 字形变换
    1.简述:将一个给定字符串s根据给定的行数numRows,以从上往下、从左到右进行 Z字形排列。比如输入字符串为"PAYPALISHIRING" 行数为3时,排列如下:P A H NAP......
  • String的面试题
    //去除首尾的空格privatestaticStringclaerTrim(Stringstr){Stringst="";intflag=0;intfirst=0,last=str.length();for(inti=0;i<s......