Java进阶篇-Redis八股文

一、Redis基本介绍

Redis是使用C语言开发的非关系型数据库,其数据存储在内存中,读写速度非常快。Redis经常用于缓存、分布式锁、消息队列。Redis支持多种数据类型、事务、持久化、Lua脚本多种方案

1.1 Redis数据类型及适用范围

String:简单的key-value,相当于Java中的Map。适用于统计用户访问次数、简单的热点存储

list:使用双向链表实现,相当于Java中的双向队列。适用于消息队列、慢查询

set:无序集合,相当于java中的HashSet。适用于存放不能重复的数据

zset:有序集合,在set的基础上加了一个权重参数score。

hash:相当于Java中的HashMap,使用数组+链表实现。

bitmap:存储连续的二进制数字。适用于保存一些布尔形式的热点数据,例如是否签到、是否在线等。

1.2 关于Redis线程

Redis在6.0之前是单线程模式,因为单线程更容易维护。将性能瓶颈不再于CPU挂钩,而是内存和网络。

6.0之后引入了多线程的模式,主要用于提高网络IO读写性能。因为对于一些较大的键值对删除操作Redis释放空间会比较消耗时间。

严格来说Redis4.0之后就不是单线程了,除了主线程外,它还有一些后台线程处理一些较为缓慢的操作,例如清理脏数据、无用连接的释放、大key的删除等等

1.3 Redis如何判断数据过期

Redis通过一个叫做过期字典(可以看作hash表)来保存数据过期的时间,过期字典的键指向Redis数据库中的某个key,过期字典的值是一个long类型的整数,这个整数保存了key所指向的数据库键的过期时间。

1.4 Redis怎么删除过期的数据

1.惰性删除,在取出key时进行过期检查,过期了就删除

2.定期删除,定时抽取一批key执行删除过期key

1.5 Redis内存淘汰机制

Redis配置文件中可以设置内存的最大使用量(MaxMemory)当内存达到上限时,Redis的写操作就会返回错误信息,但读命令可以正常返回。

redis提供了8种淘汰策略

volatile-lru:从已设置过期时间的数据集中调选最近最少使用的数据进行淘汰

volatile-ttl:从已设置过期时间得数据集中挑选即将过期的数据淘汰

volatile-random:从已设置过期时间的数据集中任意选择数据淘汰

allkeys-lru:从所有的数据集中移除最近最少使用的数据淘汰

allkeys-random:从所有数据集中任意选择数据淘汰

no-eviction:禁止驱逐数据,对新写入操作直接报错 default

volatile-lfu:从已设置过期时间的数据集中挑选最不常用的数据淘汰

allkeys-lfu:从所有的数据集中挑选最不常用的数据淘汰

二、Redis的三大失效可能

2.1 Redis缓存穿透

缓存穿透是指查询一个不存在的数据,每次都要在缓存中查找一次,不存在又要去数据库中查找一次,最后返回空。这将导致每次请求都会到数据库中进行查询,如果被恶意利用,则会出现大量请求落到数据库中。

解决方案:

1.做好参数校验,对于不合法的参数直接抛出异常返回给客户端

2.缓存无效key,对于缓存和数据库都查不到的数据就缓存一个key-null的数据到redis中。并设置过期时间。但无法防止key键每次都不同的情况。

3.布隆过滤器,将所有可能存在的值都存放在布隆过滤器中。当用户请求时,先判断用户的值是否存在布隆过滤器中,不存在的话,直接返回请求参数错误的信息给客户端,存在的话再去查询缓存。

2.2 Redis 缓存雪崩

redis缓存雪崩是指在同一时间内,大量的缓存过期。导致请求直接大面积的请求到数据库中,最终导致数据库宕机。

解决方案:

1.设置不同的失效时间,比如在原有的失效时间上加一个随机值,降低过期时间的重复率。

2.搭建的集群,增加缓存数量,当其中一台挂掉后其它的还可以继续工作。

3.本地缓存+限流

2.3 Redis 缓存击穿

redis缓存击穿是指key对应的数据虽然存在,但在redis中已经过期,此时如果有大量的并发请求,这些请求发现缓存过期后通常会从数据库中重新查询并回填到Redis中,但是大并发请求可能瞬间将数据库压垮

解决方案:

业界常用的是使用mutex,简单来说就是在缓存失效的时候,判断拿出来的值是否为空,不立即去load db。

三、Redis持久化机制

3.1 RDB(默认方式)

在指定时间间隔后,将内存中的数据快照写入二进制文件(默认为dump.rdb)将其保存在磁盘中,在恢复时直接读取快照文件进行数据恢复。在写入时Redis会使用新的RDB文件替换原来的RDB文件。并删除旧的RDB文件。

触发机制有以下四种

1.save:同步命令,在执行save命令期间,会占用当前Redis主进程并阻塞所有客户端的请求,直到RDB过程完成。

2.bgsave:异步命令,执行该命令时,Redis会fock子进程,子进程在后台异步进行快照保存,同时快照还可以响应客户端请求。

3.自动化flushall:通过配置文件对Redis进行设置,让他自动进行数据集保存操作

4.退出Redis,也会自动产生rdb文件。

优势:RDB文件紧凑、全量备份、恢复数据快,适合大规模数据恢复

劣势:需要一定的时间间隔操作,如果在快照持久化期间redis意外宕机,最后一次修改的数据就会丢失。

3.2 AOF机制

以日志(默认为appendonly.aof)的形式来记录每个写的操作,将Redis执行过的所有除了读的指令记录下来并保存在AOF文件中,Redis启动时会读取该文件重新执行所有指令来恢复数据

触发机制:

1.appendfsync always:每次发生数据变更会立即记录到磁盘,严重降低Redis速度,但数据完整性较好

2.appendfsync everysec:每秒同步,将多个写命令同步到硬盘记录,但是如果在这一秒内宕机,数据则会丢失

3.appendfsync no:让操作系统决定何时进行同步

优势:支持是实时持久化,可以更好的保护数据不丢失,使用做灾难性的误删除和紧急恢复

劣势,对于RDB数据文件更大,修复速度很慢。

四、Redis分布式锁

实现背景:例如在多个线程或多个服务需要对同一条数据进行修改时,这时为了保证数据的正确性,则只能由一个线程或一个服务修改成功。

redis分布式锁使用String类型即可实现。

锁的获取:setnx。也就是set if not eXixts,表示key不存在,才会设置它的值,否则什么也不做。

锁的释放:当某个线程或服务操作完后,释放掉锁资源,也就是锁删除。此时当其它任意服务或线程就可以再次获取锁了。

但是如果出现了当某个服务获取到锁以后,redis宕机了,那么这个key就会一直存在导致其它的服务永远无法再获取锁。

解决方案:设置锁过期时间,超时自动删除即可。

但是这会出现新的问题,假定锁的失效时间为5s,但是服务A执行方法的时间需要10s。这就会导致服务A还没有执行完锁就被删除了。此时锁就可以被其它服务获取,从而可能导致数据错乱。

解决方案:使用Redisson守护线程功能。redisson会去检测server是否还在执行业务。如果还在执行业务且锁即将过期,那么就会重新给锁设置过期时间。

猜你喜欢

转载自blog.csdn.net/qq_33351639/article/details/129121109