简约版八股文(day2)

发布时间 2023-07-02 14:19:47作者: 陈步汀

Redis(内存中->非关系型数据库)

redis是什么,为什么要用redis

redis是基于键值对的NoSQL数据库,经常用来做缓存
用户直接读取数据库中的数据效率是相对比较慢的,如果把数据读取后放到缓存中,下次就可以直接在缓存中读取数据,读取缓存的数据效率要远大于在磁盘中读取数据。
直接操作缓存能够承受的请求是远远大于直接访问数据库的
使用场景: 消息队列(命令) 缓存 锁

redis过期策略

惰性删除:客户端访问一个key的时候,redis先检查它的过期时间,如果发现过期立即删除这个key
定期删除:redis默认每隔100ms随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除

定期清理的两种模式:

  • SLOW模式是定时任务,执行频率默认为10hz,每次不超过25ms,以通过修改配置文件redis.conf 的 hz 选项来调整这个次数
  • FAST模式执行频率不固定每次事件循环会尝试执行,但两次间隔不低于2ms,每次耗时不超过1ms

Redis数据淘汰策略

  1. volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
  2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
  4. allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)
  5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  6. no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。

**MySQL里有2000w数据,Redis中只存 20w的数据,如何保证Redis中的数据都是热点数据? **
答:可以使用 allkeys-lru (挑选最近最少使用的数据淘汰)淘汰策略,那留下来的都是经常访问的热点数据

redis持久化机制(怎么保证redis挂掉后再重启数据可以恢复)

redis持久化机制有两种:
RDB持久化:RDB是一个二进制的快照文件(恢复快),它是把redis内存存储的数据写到磁盘上,当redis实例宕机恢复数据的时候,方便从RDB的快照文件中恢复数据。
AOF持久化:AOF的含义是追加文件(丢数据的风险要小),当redis操作写命令的时候,都会存储这个文件中,当redis实例宕机恢复数据的时候,会从这个文件中再次执行一遍命令来恢复数据.
当Redis崩溃导致数据丢失时,可以根据不同的持久化方式和集群配置来恢复数据。以下是一般情况下的恢复步骤:

【了解】Redis的数据恢复步骤(两种持久化方式 + 集群)

前期准备:

  1. 检查Redis的持久化配置,确定是否启用了持久化机制。

中期操作:
2. 如果启用了RDB持久化:

  • 检查是否存在最近一次成功生成的RDB文件。
  • 将该RDB文件复制到Redis服务器的数据目录下。
  • 重启Redis服务器,它将加载并恢复RDB文件中的数据。
  1. 如果启用了AOF持久化:
    • 检查是否存在最近一次成功写入的AOF文件。
    • 编辑Redis配置文件,将appendonly配置项设置为yes,以确保在启动时加载AOF文件。
    • 重启Redis服务器,它将通过重播AOF文件中的写操作来恢复数据。

后期处理:
4. 对于集群环境:

  • 如果使用Redis Cluster进行数据分片和复制,当一个节点崩溃时,其他正常运行的节点会自动接管失效节点的槽位和数据,从而保证数据的可用性。
  1. 验证和同步:
    • 一旦Redis服务器启动并加载了持久化文件,需要进行数据验证和同步。
    • 验证恢复后的数据是否与原始数据一致,确保数据的完整性。
    • 如果存在其他数据源,如关系型数据库,可以考虑重新加载数据或进行数据同步,以恢复可能丢失的数据。

缓存穿透、缓存击穿、缓存雪崩解决方案

  1. 缓存穿透:指查询一个不存在的数据,由于不会存储不存在的数据,所有每次查询都会直接查询数据库,导致缓存没有发挥作用并可能导致 DB 挂掉。解决方案:

使用布隆过滤器,在缓存查询前,先对数据进行过期,如果数据不存在,则查询数据库并将结果放到缓存中查询返回数据为空,仍把这个数据进行缓存,但过期时间较短

布隆过滤器主要是用于检索一个元素是否在一个集合中。
它的底层主要是先去初始化一个比较大数组,里面存放的二进制0或1。在一开始都是0,当一个key来了之后经过3次hash计算,模于数组长度找到数据的下标然后把数组中原来的0改为1,这样的话,三个数组的位置就能标明一个key的存在。查找的过程也是一样的。

  1. 缓存击穿:指一个热点key突然过期,恰好这个时间点有大量并发请求来请求这个数据,此时由于数据过期,这些请求就会直接请求到数据库,这些请求发现缓存过期一般都会从后端 DB 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把 DB 压垮。解决方案:
    1. 使用互斥锁,为数据设置互斥锁,保证同一时间只有一个线程访问数据库
    2. 设置当前key逻辑过期
      • 设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间
      • 查询的时候,从redis取出数据后判断时间是否过期
      • 如果过期则开通另外一个线程进行数据同步,当前线程正常返回数据,这个数据不是最新

当然两种方案各有利弊:
如果选择数据的强一致性,建议使用分布式锁的方案,性能上可能没那么高,锁需要等,也有可能产生死锁的问题
如果选择key的逻辑删除,则优先考虑的高可用性,性能比较高,但是数据同步这块做不到强一致。

3.缓存雪崩:设置缓存时采用相同的过期时间,导致缓存在某一时刻同时失效,请求全部到数据库,导致数据库崩溃。与缓存击穿的区别:雪崩是很多key,击穿是某一个key缓存。解决方案:
将缓存失效时间散开,比如可以在原有的失效时间上加一个随机时间值

Redis集群方案

主从复制、哨兵模式、Redis分片集群

redis并发竞争key问题

多个客户端同时对同一个key进行读写操作
解决方案:
使用redis事务,事务是多个命令作为一个整体进行操作,使用redis事务可以保证多个客户端对一个key进行操作时,不会发生并发竞争问题。
使用redis锁(乐观锁悲观锁),在修改一个key时,先将其锁定,然后执行修改操作,操作完成后,释放锁

redis和memcached的区别

1.redis支持更丰富的数据类型,redis提供list,set,zset,hash等数据结构的存储,memcached支持简单的数据类型:string
2.redis支持持久化
3.memcached没有原生的集群模式,redis支持cluster集群模式
4.memcached是多线程,非阻塞IO复用的网络模型。redis使用单线程的多路IO复用模型

MongDB(内存和硬盘中读写 ->介于非关系型数据库和关系数据库)

为什么要使用mongdb? 使用场景?

使用MongoDB的原因:

  1. 灵活的数据模型:MongoDB是一种面向文档的数据库,可以存储各种类型的数据,而不需要事先定义固定的表结构。这种灵活的数据模型使得MongoDB适用于快速迭代和需求变化频繁的应用场景。
  2. 高性能和可扩展性:MongoDB支持水平扩展,可以通过添加更多的节点来增加系统的存储容量和吞吐量。它具有高性能的查询和索引功能,适合处理大量数据和高并发读写的场景。
  3. 高可用性和自动故障转移:MongoDB提供了副本集(Replica Set)机制,通过复制数据到多个节点实现数据的冗余存储和故障恢复。当主节点发生故障时,副本集会自动选举新的主节点,确保系统的可用性和数据的安全性。
  4. 地理分布式存储:MongoDB支持分片(Sharding)机制,可以将数据分散存储在多个物理节点上,实现数据的地理分布式存储。这使得MongoDB适用于跨地理位置的多数据中心应用,可以提供更好的性能和用户体验。

MongoDB适用于以下场景:

  1. Web应用和内容管理:MongoDB的灵活性和高性能使其成为构建Web应用和内容管理系统的理想选择。它可以存储各种类型的数据,如用户信息、文章、日志等,并支持复杂的查询和索引操作。
  2. 实时分析和大数据处理:MongoDB的分布式架构和高并发读写能力使其适用于实时分析和大数据处理场景。它可以处理大量的数据并支持复杂的聚合操作,提供快速的数据分析和查询能力。
  3. 物联网(IoT)和传感器数据:MongoDB的扩展性和高可用性使其成为存储和处理物联网和传感器数据的理想选择。它可以处理大量的实时数据并支持时序数据的存储和查询。
  4. 实时推送和即时通讯:MongoDB的低延迟读写和副本集机制使其适用于实时推送和即时通讯应用。它可以存储和处理用户的实时消息,并支持快速的数据更新和查询。

为什么会数据丢失?(MongoDB 丢数据的事情,已经成为传说了)

MongoDB数据丢失的原因可能包括硬件故障、网络问题、软件错误、人为操作失误和数据库故障。

Web开发

cookie和session的区别

1.存储位置不同:cookie存放在客户端,session存放在服务端
2.存储容量不同:单个cookie保存的数据小于等于4kb,一个站点最多保存20个cookie,而session没有上限
3.存储方式不同:cookie只能存储ASCLL字符串,因此无法存储复制的数据类型,如对象。session可以存储任意类型数据
4.隐私策略不同:cookie中的数据是对客户端可见的,session的数据存储在服务器,对客户端是透明的
5.生命周期不同:可以通过设置cookie的属性,让cookie长期有效,session默认失效时间20分钟,服务器把长期没有
活动的session清除
6.对服务器的压力:cookie保存在客户端,不占用服务器资源,session保存在服务器,数量多的话会对造成服务器压力
7.跨域支持不同:cookie支持跨域访问,session不支持

拦截器和过滤器的区别

拦截器是基于Java反射机制和代理模式实现的,过滤器是基于Java过滤器接口实现的
拦截器处理顺序在处理链前面,即请求被接收前,过滤器处理顺序是处理链后面
拦截器通常对请求进行修改,验证,记录等,需要对请求进行全面的处理,过滤器需要对请求进行转换,压缩,安全认证等
拦截器通常用于实现安全认证,权限认证,日志记录等,过滤器通常用于实现数据压缩,字符编码转换,放在xss攻击等

消息队列

消息队列有什么用

消息队列MQ一般用来解决应用解耦,异步处理、流量削峰等问题。
应用解耦:消息队列可以避免模块之间的相互调用,将消息放到消息队列中,有需要的模块可以在直接MQ中调用,减少了模块之间的耦合
异步处理:消息队列的异步处理机制,可以在很多不需要立即处理的消息中使用,比如发送短信。
流量削峰:Mysql不能处理大量的高并发请求,所以在流量高峰期,请求直接到MySQL的话可能导致MySQL挂掉,但高峰期的流量并不会一直存在,所以如果以加服务器的方式来抗压,会导致资源的浪费,所以可以将用户的请求放到消息队列,在随后的时间里平滑的处理这些请求

消息被重复消费怎么解决

1.消息唯一标识:在消息中添加唯一标识,每次消费消息时先检查该标识是否消费过,如果已经消费则忽略消息,可以通过消息体中添加唯一标识或者使用rabbitmq自带的消息ID来实现
2.消息确认机制:消费者成功处理消息后,将消息从队列中删除或标记为已处理
3.消息持久化:将消息持久化到磁盘中,避免消息丢失或者重复发送