牛客面试系列之Redis

✅作者简介:我是18shou,一名即将秋招的java实习生

?系列专栏:牛客面经专栏

?推荐一款八股、面经、模拟面试、刷题神器? 超级无敌之牛客

耗时数小时从牛客整理的面经以及笔记
在这里插入图片描述

Redis数据类型

数据类型 :最大存储数据量
key:512M
string:512M
hash:2^32-1
list:2^32-1
set:2^32-1
sorted setbitmap:512M
hyperloglog:12K

Redis基本数据类型的使用和使用场景

String

String:set(添加键值对),get(获取键值对),decr(自减1),incr(自增1),mget(同时获取一个或多个value)
适合用于高频访问的信息,比如书一个大V的粉丝数量,微博数量等等,可以用Redis的String来存放。如博客访问次数、网站访问量。

hash

hash: hget,hset,hgetall 可以应用在电商网站购物车的设计,用户就是一个key,商品是一个field,数量为value。

list

list:
lpush(从左边插入一个或多个值),rpush(从右边插入一个或多个值),rpop(从右边吐出一个值),lrange(key,start,stop)(按照索引下标获得元素从左到右)
可以运用在微信朋友圈点赞,比如说要求按照点赞的顺序来显示好友信息,如果取消点赞,就移除点赞列表。微博中我的关注列表

set

set: sadd(添加一个或多个值), spop (随机从集合中吐出一个值),
smembers(取出对应键所有值),sunion(sunion:sunion key1 key2 取key1和key2的并集)
假如一个公司里面有很多的员工,在内部的OA系统中就具有700多个角色,3000多个业务操作,20000多种数据,那么如何快速进行业务操作的相关校验呢?
Redis的set是string类型的无序集合,和list不同的是,set中不会出现重复的成员,集合最大的好处就是可以进行交集或差集的操作。
使用场景:共同好友、二度好友,记录访问网站的独立IP等 常用命令:sadd、spop、smembers、sunion等。

zset

zset (有序集合)
Redis中的zset和set一样,都是string类型的集合,也不允许有重复的成员,但不同的是zset的每个元素都会有一个double类型的分数(score)与之关联,而Redis也是通过这个分数来为这个集合排序的。

应用场景:在线游戏的积分排行榜、热点新闻排行榜等 常用命令:zadd、zrange、zrem、zcard等。

标题为什么使用Redis?

因为是基于内存,可持久化的Key-Value数据库,具有速度快,可持久化,丰富数据类型,支持数据备份的功能。

标题为什么Redis的性能高?

(1)Redis的数据是完全存放在内存中的,类似hashmap优势是查找和操作的时间复杂度都是O(1)
(2)Redis采用了单线程,避免了不必要的上下文切换和竞争条件以及多线程导致的相关锁的问题。
(3)采用多路I/O复用模型,非阻塞IO,空闲时把当前线程阻塞,当有IO流事件时,就从阻塞状态唤醒,然后程序会轮询一边所有的流,并一次顺序处理事件的流,避免了大量的无用操作。

标题Redis为什么使用单线程?

(1)使用Redis时几乎不存在CPU瓶颈,主要受限于网络和内存,比如说Linux下Redis可以pipelining每秒处理100万个请求。
(2)使用单线程可维护性高,多线程虽然优秀但是带来的程序执行顺序不确定,并发读写一系列问题,增加系统复杂度,同时线程切换,造成的性能消耗。
(3)Redis采用的时I/O复用模型,处理性能高,没必要用多线程,并且单线程Redis内部实现复杂度低。

Redis过期策略

Redis会把设置了过期时间的key放入一个独立的字典里,在key过期时并不会立刻删除它。Redis会通过如下两种策略,来删除过期的key:

  1. 惰性删除 客户端访问某个key时,Redis会检查该key是否过期,若过期则删除。
  2. 定期扫描 Redis默认每秒执行10次过期扫描(配置hz选项),扫描策略如下:
    1.从过期字典中随机选择20个key;
    2.删除这20个key中已过期的key;
    3.如果过期的key的比例超过25%,则重复步骤1;

Redis淘汰策略

当Redis占用内存超出最大限制(maxmemory)时,
可采用如下策略(maxmemory-policy),让Redis淘汰一些数据,以腾出空间继续提供读写服务:

.noeviction:对可能导致增大内存的命令返回错误(大多数写命令,DEL除外);
· volatile-ttl:在设置了过期时间的key中,选择剩余寿命(TTI)最短的key,将其淘汰; 
.volatile-lru:(在设置了过期时间的key中,选择最少使用的key (LRU),将其淘汰;
. volatile-random:在设置了过期时间的key中,随机选择一些key,将其淘汰;
. allkeys-lru:在所有的key中,选择最少使用的key (LRU),将其淘汰;
.allkeys-random:在所有的key中,随机选择一些key,将其淘汰;

Redis缓存穿透

场景 查询根本不存在的数据,使得请求直达存储层,导致其负载过大,甚至宕机。 ·解决方案 1.缓存空对象
存储层未命中后,仍然将空值存入缓存层。再次访问该数据时,缓存层会直接返回空值。2.布隆过滤器
将所有存在的key提前存入布隆过滤器,在访问缓存层之前,先通过过滤器拦截,若请求的是不存在的key,则直接返回空值。

Redis缓存击穿

场景 一份热点数据,它的访问量非常大。在其缓存失效瞬间,大量请求直达存储层,导致服务崩溃。·解决方案 1.加互斥锁
对数据的访问加互斥锁,当一个线程访问该数据时,其他线程只能等待。
这个线程访问过后,缓存中的数据将被重建,届时其他线程就可以直接从缓存取值。2.永不过期
不设置过期时间,所以不会出现上述问题,这是“物理”上的不过期。为每个value设置逻辑过期时间,当发现该值逻辑
过期时,使用单独的线程重建缓存。

Redis缓存雪崩

场景 由于某些原因,缓存层不能提供服务,导致所有的请求直达存储层,造成存储层宕机。·解决方案
1.避免同时过期 设置过期时间时,附加一个随机数,避免大量的key同时过期。2.构建高可用的Redis缓存 部署多个Redis实例,个别节点宕机,依然可以保持服务的整体可用。3.构建多级缓存
增加本地缓存,在存储层前面多加一级屏障,降低请求直达存储层的几率。4.启用限流和降级措施
对存储层增加限流措施,当请求超出限制时,对其提供降级服务。

Redis分布式锁

场景
修改时,经常需要先将数据读取到内存,在内存中修改后再存回去。在分布式应用中,可能多个进程同时执行上述操作,而读取和修改非原子操作,所以会产生冲突。增加分布式锁,可以解决此类问题基本原理
同步锁:在多个线程都能访问到的地方,做一个标记,标识该数据的访问权限。分布式锁:在多个进程都能访问到的地方,做一个标记,标识该数据的访问权限。实现方式
1.基于数据库实现分布式锁;2.基于Redis实现分布式锁;3.基于zookeeper实现分布式锁;

Redis实现分布式锁的原则

1.安全属性:独享。在任一时刻,只有一个客户端持有锁。
2.活性A:无死锁。即便持有锁的客户端崩溃或者网络被分裂,锁仍然可以被获取。3.活性B:容错。只要大部分Redis节点都活着,客户端就可以获取和释放锁。

单Redis实例实现分布式锁

1.获取锁使用命令: SET resource_name my_random_value NX PX 3000o
NX:仅在key不存在时才执行成功。
PX:设置锁的自动过期时间。
2.通过Lua脚本释放锁: 可以避免删除别的客户端获取成功的锁:
if redis.call ( "get",KEYS[1])==ARGV [1] then return redis.call ( "del",KEYS [1]) else return 0 end
A加锁->A阻塞->因超时释放锁 ->B加锁->A恢复->释放锁

多Redis实例实现分布式锁

Redlock算法,该算法有现成的实现,其Java版本的厍为Redisson。
1.获取当前Unix时间,以毫秒为单位。
2.依次尝试从N个实例,使用相同的key和随机值获取锁,并设置响应超时时间。如果服务器没有在
规定时间内响应,客户端应该尽快尝试另外一个Redis实例。
3.客户端使用当前时间减去开始获取锁的时间,得到获取锁使用的时间。当且仅当大多数的Redis节
点都取到锁,并且使用的时间小于锁失效时间时,锁才算取得成功。
4.如果取到了锁,key的真正有效时间等于有效时间减去获取锁使用的时间。5.如果获取锁失败,客户端应该在所有的Redis实例上进行解锁。

?推荐超级无敌之牛客

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>