Redis面试合集

概念

Redis是一种key-value型非关系数据库。
在这里插入图片描述
特点:

1、速度快,存在于内存中,类似于HashMap,HashMap的操作和查找的时间复杂度都是O(1)

2、支持丰富的数据类型,包括字符串、哈希、列表、集合、有序集合五种数据类型。

3、支持事务,操作都是原子性的,要么都执行要么都不执行。

4、memcached所有值都是简单的字符串,Redis可以持久化数据(RDB、AOF)。

过期策略

定时过期(定时器)、惰性过期(访问时判断)、定期过期(每隔一段时间扫描)

如何保证Redis都是热点数据?(数据回收)

最近最少使用/随机/最近最少使用/更早过期时间的

持久化(RDB和AOF)

RDB:一定时间将内存数据以快照形式保存到硬盘dump.rdb。通过save参数配置快照周期。
保存成一个文件,更安全。数据量大时,启动效率更高。子进程单独进行持久化保证Redis高性能。持久化过程中发生事故数据容易丢失。

AOF:将Redis执行命令写到单独日志文件中。
数据安全。通过append追加到日志,即使服务器宕机也可通过redis-check-aof工具解决数据一致问题。AOF文件更大,恢复更慢,启动效率低。

保证Redis和MYSQL数据一致

缓存单删: 更新DB前先删除缓存,再更新库,查询缓存无数据则直接访问DB。
延时双删: 为了解决缓存单删,在高并发读情况下数据不一致问题。操作数据前,先删除缓存,接着操作DB,然后延迟一段时间,再删除缓存。

定时+增量更新: 主要是利用库里行数据的更新时间字段+定时增量查询。具体为:每次更新库里的行数据,记录当前行的更新时间;然后把更新时间做为一个索引字段(加快查询速度)

定时任务: 每隔几秒,比如5秒(自定义),把库里最近5秒钟的数据查询出来,写入缓存并记录查询结束时间。查询过程和放入缓存都是单线程不存在并发问题。每次同步成功,下次执行把结束时间作为开始时间,当前时间作为结束时间实现增量查询。

优点:架构简单、逻辑和业务解耦、依赖少,最多加个分布式定时任务或者Redis分布式锁。

监听binlog+MQ: 监听MYSQL binlog把数据库的更新插入删除操作通过MQ同步给下游消费者,下游拿到日志和业务数据再放入缓存。

优点:缓存和业务解耦,减少业务代码的侵入性。
缺点:技术和架构复杂,中间件的运维,引入MQ后带来的问题,比如数据乱序:同一条数据先发后至,后发先至到达消费者,通过redis lua+数据的时间戳比较方案,解决并发问题和数据乱序问题

缓存击穿

关键字:单一热点数据、高并发、数据失效。

概念:热点数据缓存过期

解决方案:

互斥锁

当业务线程在处理用户请求时,如果发现访问的数据不在 Redis 里,就加个互斥锁,保证同一时间内只有一个请求来构建缓存(从数据库读取数据,再将数据更新到 Redis 里),当缓存构建完成后,再释放锁。未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。

实现互斥锁的时候,最好设置超时时间,不然第一个请求拿到了锁,然后这个请求发生了某种意外而一直阻塞,一直不释放锁,这时其他请求也一直拿不到锁,整个系统就会出现无响应的现象。

缓存穿透

概念:数据既不在缓存,也不在数据库

解决方案:

非法请求的限制

在 API 入口处我们要判断求请求参数是否合理,请求参数是否含有非法值、请求字段是否存在,如果判断出是恶意请求就直接返回错误,避免进一步访问缓存和数据库。

缓存空值

对NULL值进行缓存

布隆过滤器

数据写入数据库的同时将这个 ID 同步到到布隆过滤器中,请求的 id 不存在布隆过滤器中则说明该请求查询的数据一定没有在数据库中保存,就不要去数据库查询。

BloomFilter 的算法是,首先分配一块内存空间做 bit 数组,数组的 bit 位初始值全部设为 0。

加入元素时,采用 k 个相互独立的 Hash 函数计算,然后将元素 Hash 映射的 K 个位置全部设置为 1。

检测 key 是否存在,仍然用这 k 个 Hash 函数计算出 k 个位置,如果位置全部为 1,则表明 key 存在,否则不存在。

缓存雪崩

概念:大量缓存数据同时过期,业务系统需要重新生成缓存数据,或者Redis宕机,短时间内请求直接访问数据库,大大增加数据库压力,甚至可能导致系统崩溃。

解决方案:

均匀设置过期时间

避免数据设置同一过期时间,可以在生成缓存数据时在业务需求的过期时间上增加一个随机数时间。

互斥锁

跟缓存击穿解决思路一致,同一时间只让一个线程构建缓存,其他线程阻塞排队。

永久缓存,后台更新

其他(Redis宕机)

服务熔断(发现数据异常直接报错)、构建高可用缓存集群(主节点宕机,从节点切换成主节点)

在这里插入图片描述

PHP+Redis分布式锁

Redis分布式锁是通过线程锁来实现,同时只允许一个线程执行,其他线程进入等待状态。

优点:降低MySQL压力,比队列的并发量高。缺点:线程需要排队等待,并发量也不是特别高。

$key = "test:lock:".$id;$uuid = Uuid::uuid1()->getHex();try {
    $ret = Redis::set($key, $uuid, 'EX', 10, 'NX');
    if (!$ret) {
        usleep(10);
        return $this->test($id);
    }
    $stock = Skill::query()->where('id', $id)->value('stock');
    if ($stock > 0) {
        Skill::query()->where('id', $id)->decrement('stock');
        $msg = '抢购成功';
    } else {
        $msg = '库存不足,抢购失败';
    }
    if (Redis::get($key) == $uuid) {
        Redis::del($key);
    }
    return $msg;} catch (\Exception $exception) {
    return '抢购失败';}

PHP+Redis乐观锁

Redis的乐观锁就是借助Redis事务和watch监控,采用事务打断的方式实现

优点:并没有锁定任何资源,多线程可以并行。缺点:这是PHP层面的控制,而PHP是有性能瓶颈的。

$key = 'stock:'.$id;Redis::watch($key);$stock = Redis::get($key);
if (is_null($stock)) {
 return '没有商品';
}
if ($stock == 0) {
 return '库存不足';
}
Redis::multi();Redis::decr($key);
$res = Redis::exec();
if ($res) {
 Skill::query()->where('id', $id)->decrement('stock');
 return '抢购成功';
} else {
 return '抢购失败';}

Nginx结合Lua做桶限流+Redis乐观锁(最优方案)

这种方案是最优方案,直接绕过应用层,在接入层实现限流和防止超卖的操作,只消耗很少的服务器性能,但是可抗并发量级特别大,性能上远超上述几种方案。

逻辑分析:先使用 Nginx+Lua 漏桶算法过滤掉大部分请求,再使用Lua连接Redis,使用Redis乐观锁的方式控制库存。假设只有10个秒杀商品,那这里只保留10个请求进入应用层(PHP和MysqL),应用层不需要进行其他操作,直接操作数据库就可以。

项目应用

1、ERP的商品报表模块,API接口处理好数据,序列化成字符串后给定过期时间存储到Redis中。

2、序列号生成器,我们又很多单号,像订单、生产、调拨,但需求层面要求我们的订单尾号按照顺序来,我们生成器中会根据类别将对应序列号hash进行自增。

3、订单提取模块,

持续更新,未完待续~

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值