百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

一篇文章让你搞定所有redis面试题

mhr18 2024-11-16 23:33 21 浏览 0 评论

Redis是什么

Redis是C语言开发的一个开源的(遵从BSD协议)高性能键值对(key-value)的内存数据库,可以用作数据库、缓存、消息中间件等。它是一种NoSQL(not-only sql,泛指非关系型数据库)的数据库。

redis的优势

  • 性能优秀,数据在内存中,读写速度非常快,支持并发10W QPS
  • 单进程单线程,避免了不必要的上下文切换和竞争条件,不存在多线程导致的CPU切换,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有死锁问题导致的性能消耗。采用IO多路复用机制。虽然没法发挥多cpu,但是可以在机器部署多个(从Redis 4.0版本开始会支持多线程)
  • 丰富的数据类型,支持字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)
  • 支持数据持久化(aof、rdb)。可以将内存中数据保存在磁盘中,重启时加载
  • 主从复制,哨兵,高可用
  • 可以用作分布式锁(lua脚本)
  • 可以作为消息中间件使用,支持发布订阅

五种数据类型

string,一个key对应一个value。value不仅是string,也可以是数字。string类型是二进制安全的,意思是redis的string类型可以包含任何数据,比如jpg图片或者序列化的对象。string类型的值最大能存储512M。

redis的hash是一个string的key和value的映射表,Hash特别适合存储对象。常用命令:hget,hset,hgetall等。

list列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边) 常用命令:lpush、rpush、lpop、rpop、lrange(获取列表片段)等。比如twitter的关注列表,粉丝列表都可以用list结构来实现。数据结构:list就是链表,可以用来当消息队列用。

set是string类型的无序集合。集合是通过hashtable实现的。set中的元素是没有顺序的,而且是没有重复的。常用命令:sdd、spop、smembers、sunion等。

zset和set一样是string类型元素的集合,且不允许重复的元素。常用命令:zadd、zrange、zrem、zcard等。Redis sorted set的内部使用HashMap和跳跃表(skipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。

redis使用

RedisTemplate

引入

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisCacheConfig {

    @Bean
    public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory connectionFactory) {

        RedisTemplate<String, Serializable> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(connectionFactory);
        return template;
    }
}

spring cache集成Redis

spring cache具备很好的灵活性,不仅能够使用SPEL(spring expression language)来定义缓存的key和各种condition,还提供了开箱即用的缓存临时存储方案。


@Service
public class UserServiceImpl implements UserService{

    public static Logger logger = LogManager.getLogger(UserServiceImpl.class);

    private static Map<Integer, User> userMap = new HashMap<>();
    static {
        userMap.put(1, new User(1, "u1", 25));
        userMap.put(2, new User(2, "u2", 26));
        userMap.put(3, new User(3, "u3", 24));
    }


    @CachePut(value ="user", key = "#user.id")
    @Override
    public User save(User user) {
        userMap.put(user.getId(), user);
        return user;
    }

    @CacheEvict(value="user", key = "#id")
    @Override
    public void delete(int id) {
        userMap.remove(id);
    }

    @Cacheable(value = "user", key = "#id")
    @Override
    public User get(Integer id) {
        return userMap.get(id);
    }
}

缓存注解

1、@Cacheable 根据方法的请求参数对其结果进行缓存

  • key:缓存的key,可以为空,如果指定要按照SPEL表达式编写,如果不指定,则按照方法的所有参数进行组合。
  • value:缓存的名称,必须指定至少一个(如 @Cacheable (value='user')或者@Cacheable(value={'user1','user2'}))
  • condition:缓存的条件,可以为空,使用SPEL编写,返回true或者false,只有为true才进行缓存。

2、@CachePut 根据方法的请求参数对其结果进行缓存,和@Cacheable不同的是,它每次都会触发真实方法的调用。

  • key:缓存的key,可以为空,如果指定要按照SPEL表达式编写,如果不指定,则按照方法的所有参数进行组合。
  • value:缓存的名称,必须指定至少一个(如 @Cacheable (value='user')或者@Cacheable(value={'user1','user2'}))
  • condition:缓存的条件,可以为空,使用SPEL编写,返回true或者false,只有为true才进行缓存。

3、@CacheEvict 根据条件对缓存进行清空

  • key:缓存的key,可以为空,如果指定要按照SPEL表达式编写,如果不指定,则按照方法的所有参数进行组合。
  • value:缓存的名称,必须指定至少一个(如 @Cacheable (value='user')或者@Cacheable(value={'user1','user2'}))
  • condition:缓存的条件,可以为空,使用SPEL编写,返回true或者false,只有为true才进行缓存。
  • allEntries:是否清空所有缓存内容,缺省为false,如果指定为true,则方法调用后将立即清空所有缓存
  • beforeInvocation:是否在方法执行前就清空,缺省为false,如果指定为true,则在方法还没有执行的时候就清空缓存。缺省情况下,如果方法执行抛出异常,则不会清空缓存。

缓存和数据库数据一致性

缓存更新策略,更新数据库后及时更新缓存、缓存失败时增加重试机制

雪崩

key批量失效导致大量请求直接请求数据库

解决方法:避免将所有Key的失效时间相同,避免数据同一时间大面积失效

穿透

大量不存在的数据查询,导致一次次请求数据库

解决方法:布隆过滤器(redis本身就支持)

击穿

热点key失效,大量请求达到数据库

解决方法:热点数据永不过期,或者加上互斥锁

Redis和Memcached的区别

  • memcache会把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。redis有部分数据存在硬盘上,这样能保证数据的持久性
  • 数据支持类型上:memcache对数据类型的支持简单,只支持简单的key-value,而redis支持五种数据类型。
  • 使用底层模型不同:它们之间底层实现方式以及与客户端之间通信的应用协议不一样。redis直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
  • value的大小:redis可以达到1GB,而memcache只有1MB。

淘汰策略

  • volatile-lru:从设置了过期时间的KV中找最近最少使用
  • volatile-ttl:从设置了过期时间的KV中找最快过期的
  • volatile-random:从设置了过期时间的KV中随机
  • allkeys-lru:移除最近最少使用的key
  • allkeys-random,随机移除一个key
  • noeviction,当内存使用达到阀值的时候,所有引起申请内存的命令会报错(默认)

删除过期键策略

定时删除

在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作

定时删除操作对于内存来说是友好的,内存不需要操作,而是通过使用定时器,可以保证尽快的将过期键删除,但是对于CPU来说不是友好的,如果过期键比较多的话,起的定时器也会比较多,删除的这个操作会占用到CPU的资源

惰性删除

放任键过期不管,但是每次从键空间中获取键是,都检查取得的键的过期时间,如果过期的话,删除即可。惰性操作对于CPU来说是友好的,过期键只有在程序读取时判断是否过期才删除掉,而且也只会删除这一个过期键,但是对于内存来说是不友好的,如果多个键都已经过期了,而这些键又恰好没有被访问,那么这部分的内存就都不会被释放出来

定期删除

每隔一段时间,程序就对数据库进行一次检查,删除掉过期键。定期删除是上面两种方案的折中方案,每隔一段时间来删除过期键,并通过限制删除操作执行的时长和频率来减少删除操作对CPU时间的影响,除此之外,还有效的减少内存的浪费;但是该策略的难点在于间隔时长,这个需要根据自身业务情况来进行设置

持久化

aof

把所有的对Redis的服务器进行修改的命令都存到一个文件里,命令的集合。

配置文件中的appendonly修改为yes。开启AOF持久化后,你所执行的每一条指令,都会被记录到appendonly.aof文件中。但事实上,并不会立即将命令写入到硬盘文件中,而是写入到硬盘缓存,在接下来的策略中,配置多久来从硬盘缓存写入到硬盘文件。所以在一定程度一定条件下,还是会有数据丢失,不过可以大大减少数据损失。

redis默认使用everysec,就是说每秒持久化一次,而always则是每次操作都会立即写入aof文件中。而no则是不主动进行同步操作,是默认30s一次。当然always一定是效率最低的,个人认为everysec就够用了,数据安全性能又高。

Redis也允许同时使用两种方式,在重启redis后会从aof中恢复数据,因为aofrdb数据损失小嘛。

rdb(默认开启)

快照形式是直接把内存中的数据保存到一个dump的文件中,定时保存,保存策略。

两种方式

  • 手动执行持久化数据命令来让redis进行一次数据快照,save命令(会阻塞其他请求操作,避免使用)和bgsave命令(调用Fork,产生子进程,父进程继续处理请求)
  • 根据配置文件,达到策略的某些条件时来自动持久化数据(save 900 1 代表900秒有一个key变动,做快照)

区别

RDB每次进行快照方式会重新记录整个数据集的所有信息。RDB在恢复数据时更快,可以最大化redis性能,子进程对父进程无任何性能影响。

AOF有序的记录了redis的命令操作。意外情况下数据丢失甚少。AOF的文件体积通常要大于RDB文件的体积。根据所使用的fsync策略,AOF的速度可能会慢于RDB。

主从复制

主从配置结合哨兵模式能解决单点故障问题,提高redis可用性。从节点仅提供读操作,主节点提供写操作。

复制过程

  1. 从节点执行slaveof[masterIP][masterPort],保存主节点信息
  2. 从节点中的定时任务发现主节点信息,建立和主节点的socket连接
  3. 从节点发送Ping信号,主节点返回Pong,两边能互相通信
  4. 连接建立后,主节点将所有数据发送给从节点(数据同步)
  5. 主节点把当前的数据同步给从节点后,便完成了复制的建立过程。接下来,主节点就会持续的把写命令发送给从节点,保证主从数据一致性。

数据同步

  1. 主节点发送数据给从节点过程中,主节点还会进行一些写操作,这时候的数据存储在复制缓冲区中。从节点同步主节点数据完成后,主节点将缓冲区的数据继续发送给从节点,用于部分复制
  2. 节点响应写命令时,不但会把命名发送给从节点,还会写入复制积压缓冲区,用于复制命令丢失的数据补救。

redis2.8之前使用sync[runId][offset]同步命令(仅支持全量复制过程),redis2.8之后使用psync[runId][offset]命令。(支持全量和部分复制),部分复制是指出现网络闪断或者命令丢失等异常情况时,从节点会向主节点要求补发丢失的命令数据,主节点的复制积压缓冲区将这部分数据直接发送给从节点,这样就可以保持主从节点复制的一致性。

runId:每个redis节点启动都会生成唯一的uuid,每次redis重启后,runId都会发生变化。

offset:主节点和从节点都各自维护自己的主从复制偏移量offset,当主节点有写入命令时,offset=offset+命令的字节长度。从节点在收到主节点发送的命令后,也会增加自己的offset,并把自己的offset发送给主节点。主节点同时保存自己的offset和从节点的offset,通过对比offset来判断主从节点数据是否一致。

repl_backlog_size:保存在主节点上的一个固定长度的先进先出队列,默认大小是1MB。

哨兵

哨兵主要功能为

监控

不断检查主服务器和从服务器是否正常运行。每个Sentinel以每秒一次的频率,向它所知的主服务器、从服务器以及其他的Sentinel实例发送一个PING命令,超过时间将节点标记为主观下线。

如果一个主服务器被标记为主观下线,那么正在监视这个服务器的所有Sentinel节点,要以每秒一次的频率确认主服务器的确进入了主观下线状态。有足够数量的Sentinel(达到配置文件指定的数量)在指定的时间范围内同意这一判断,那么这个主服务器被标记为客观下线。

当没有足够数量的Sentinel同意主服务器下线时,主服务器的客观下线状态就会被移除。当主服务器重新向Sentinel的PING命令返回有效回复时,主服务器的主观下线状态就会被移除。

通知

当被监控的某个redis服务器出现问题,Sentinel通过API脚本向管理员或者其他应用程序发出通知。

自动故障转移

当主节点不能正常工作时,Sentinel和其他Sentinel协商客观下线的主节点的状态,如果处于SDOWN状态,则投票自动选出新的主节点。Sentinel会开始一次自动的故障转移操作,它会将与失效主节点是主从关系的其中一个从节点升级为新的主节点,并且将其他的从节点指向新的主节点。

配置提供者

在Redis Sentinel模式下,客户端应用在初始化时连接的是Sentinel节点集合,从中获取主节点的信息。

相关推荐

【推荐】一个开源免费、AI 驱动的智能数据管理系统,支持多数据库

如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!.前言在当今数据驱动的时代,高效、智能地管理数据已成为企业和个人不可或缺的能力。为了满足这一需求,我们推出了这款开...

Pure Storage推出统一数据管理云平台及新闪存阵列

PureStorage公司今日推出企业数据云(EnterpriseDataCloud),称其为组织在混合环境中存储、管理和使用数据方式的全面架构升级。该公司表示,EDC使组织能够在本地、云端和混...

对Java学习的10条建议(对java课程的建议)

不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...

SQLShift 重大更新:Oracle→PostgreSQL 存储过程转换功能上线!

官网:https://sqlshift.cn/6月,SQLShift迎来重大版本更新!作为国内首个支持Oracle->OceanBase存储过程智能转换的工具,SQLShift在过去一...

JDK21有没有什么稳定、简单又强势的特性?

佳未阿里云开发者2025年03月05日08:30浙江阿里妹导读这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。阅前声明:本文介绍的内容基于AJDK21.0.5[1]以及以上...

「松勤软件测试」网站总出现404 bug?总结8个原因,不信解决不了

在进行网站测试的时候,有没有碰到过网站崩溃,打不开,出现404错误等各种现象,如果你碰到了,那么恭喜你,你的网站出问题了,是什么原因导致网站出问题呢,根据松勤软件测试的总结如下:01数据库中的表空间不...

Java面试题及答案最全总结(2025版)

大家好,我是Java面试陪考员最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试题及答案。涉及的内容非常全面,包含:Spring、MySQL、JVM、Redis、Linux、Sprin...

数据库日常运维工作内容(数据库日常运维 工作内容)

#数据库日常运维工作包括哪些内容?#数据库日常运维工作是一个涵盖多个层面的综合性任务,以下是详细的分类和内容说明:一、数据库运维核心工作监控与告警性能监控:实时监控CPU、内存、I/O、连接数、锁等待...

分布式之系统底层原理(上)(底层分布式技术)

作者:allanpan,腾讯IEG高级后台工程师导言分布式事务是分布式系统必不可少的组成部分,基本上只要实现一个分布式系统就逃不开对分布式事务的支持。本文从分布式事务这个概念切入,尝试对分布式事务...

oracle 死锁了怎么办?kill 进程 直接上干货

1、查看死锁是否存在selectusername,lockwait,status,machine,programfromv$sessionwheresidin(selectsession...

SpringBoot 各种分页查询方式详解(全网最全)

一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...

《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略

《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...

LoadRunner(loadrunner录制不到脚本)

一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...

Redis数据类型介绍(redis 数据类型)

介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...

RMAN备份监控及优化总结(rman备份原理)

今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...

取消回复欢迎 发表评论: