首页 > 数据库 >Redis 常见问题总结

Redis 常见问题总结

时间:2023-06-12 18:35:27浏览次数:42  
标签:总结 缓存 过期 Redis 常见问题 内存 key 节点

 

目录 

一、Redis为什么快?

二、Redis合适的应用场景

三、Redis为什么6.0之前不支持多线程

四、Redis为什么6.0之后引入多线程

五、Redis有哪些高级功能

六、为什么需要使用Redis

七、Redis的事务

八、Redis的过期策略以及内存淘汰机制

九、什么是缓存穿透?如何避免?

十、什么是缓存雪崩?如何避免?

十一、Redis如何设计分布式锁

十二、什么是bigkey?会有什么影响?

十三、Redis如何解决key冲突

十四、怎么提高换成命中率

十五、Redis持久化方式有哪些方式?有什么区别?

十六、为什么Redis需要把所有数据放到内存中?

十七、如何保证缓存与数据库双写一致性?

十八、Redis集群方案

十九、Redis集群方案什么情况下会导致整个集群不可用?

二十、Redis集群会有写操作丢失吗?为什么?

二十一、Redis常见性能问题和解决方案

二十二、热点数据和冷数据

二十三、什么情况下可能会导致Redis阻塞

二十四、线上Redis响应慢处理思路

 

一、Redis为什么快?


1、纯内存访问

相比查询数据库(访问磁盘)要快很多

2、单线程,避免上下文切换

内部执行命令为单线程,避免上下文切换带来的CPU开销

3、渐进式ReHash、缓存时间戳

(1)渐进式ReHash:

Redis使用全局哈希表来保存所有键值对,

哈希表相当于一个数组,数组的每个元素称为一个哈系桶,每个哈系桶中保存了键值对的数据。

 

数据增加到一定阈值,数组扩容会导致数据发生移动,此时访问会发生阻塞

渐进式ReHash:把一次性大量拷贝(数组移动)的开销,分摊到多次处理请求的过程中。

Redis默认使用两种全局哈希表,开始插入数据时默认使用哈希表1,此时哈希表2并没有被分配空间。随着数据逐步增多,开始执行ReHash。

  1. 给哈希表2分配更大的空间,

  1. 将哈希表1中的数据重新映射并拷贝到哈希表2中

  1. 释放哈希表1的空间

 

(2)缓存时间戳:

业务中需要用到时间戳时,一般会使用System.currentTimeMillis()或者New Date()等方式获取系统的毫秒时间戳,每一次获取都是一次系统调用(需要调用操作系统中对应的函数,涉及上下文切换),相对比较耗时。

作为单线程的Redis承受不起,因此它由一个定时任务,每毫秒更新一次缓存,获取时间都是从缓存中直接拿。

二、Redis合适的应用场景


常用基本数据类型(5种)

名称

英文名

作用域

字符串

String

缓存、计数器、分布式Session

哈希

Hash

存放对象

列表

list

消息队列、文章列表

集合

set

 

有序集合

ZSET

 

1、字符串(String)

命令的时间复杂度:

字符串这些命令中除了del、mset、mget支持多个键的批量操作,时间复杂度和键的个数相关,为O(n),getrange的字符串长度相关,也是O(n),其余的命令基本上都是O(1)的时间复杂度,在速度上还是非常快的。

(1)缓存

具有支撑高并发的特性,能起到加速读写的作用,降低后端压力

(2)计数器

实现快速计数、查询缓存的功能,同时数据可以异步落地到其他数据源

(3)分布式会话(共享Session)

问题:如果一个分布式Web服务将用户的Session信息保存在各自服务器中,出于负载均衡的考虑,分布式服务会将用户的访问均衡到不同服务器上,用户刷新一次访问,可能会发现需要重新登录。

解决方案:使用Redis将用户的Session进行集中管理,这种情况下只要保证Redis是高可用和扩展性的,每次用户更新或查询登录信息都直接从Redis集中获取。

2、哈希(Hash)

适用于存放对象,相较于String类型存储对象时效率开发效率更高。

3、列表(list)

用来存储多个有序字符串

(1)消息队列

lpush+brpop命令组合即可实现阻塞队列,生产环境客户端使用lpush从列表左侧插入元素,多个消费者客户端使用brpop命令阻塞式的”抢“列表尾部的元素,多个客户端保证了消费的负载均衡和高可用性。

(2)文章列表

每个用户有属于自己的文章列表,现需要分页展示文章列表。此时可以考虑使用列表,有序,且支持按照索引范围获取元素。

还可实现其他数据结构

4、集合(set)

(1)标签(tag)

例如一个用户可能对娱乐、体育感兴趣,另一个用户可能对历史、新闻感兴趣,这些兴趣点就是标签。通过这些数据可以得到喜欢同一个标签的人,这些数据对于用户体验以及增强用户粘度比较重要。

(2)随机数(抽奖活动)

(3)社交图谱

5、有序集合(ZSET)

(1)排行榜

多维度:时间、浏览量、获赞数等等。

Redis高级数据结构

6、Bitmaps

可以实现对位的操作,单独提供了一套命令,可以想象成以位为单位的数组,数组下标叫做偏移量。

 

(1)布隆过滤器

 

7、HyperLogLog

(1)UV

统计每个网页每天的UC数据,HyperLogLog提供不精确的去重计数方案,误差0.81%

三、Redis为什么6.0之前不支持多线程


1、Redis的瓶颈不是CPU,受制于内存、网络

存储于内存,快速读写网络开销大

2、提高Redis性能,Pipeline(命令批量)

每秒100万个请求包装进Pipeline

3、单线程,内部维护成本相对较低,不需要管理多线程安全

命令执行顺序不确定性,读写并发问题

4、多线程(线程切换、加锁/解锁、导致死锁问题)

5、惰性Rehash(渐进式)减少阻塞

一般的公司,单线程Redis就够了。

四、Redis为什么6.0之后引入多线程


1、小数据包。数据-》内存 响应时间 100ns 8w-10wQPS(极限)

2、针对大的公司,需要更大的QPS,IO的多线程(内部执行命令还是单线程)

3、为什么不采用分布式架构---很大的缺点。

服务器数量多,维护成本高。Redis命令 不适用 需要数据分区,无法解决热点数据读写的问题。

数据倾斜、重新分配、扩容、缩容,更加复杂。

本质:多线程任务 分摊到Redis 同步IO中,读写负载。

五、Redis有哪些高级功能


(1)慢查询

快速定位系统中的慢操作,监测发生时间、耗时、命令的详细信息。

(2)Pipeline

 

(3)watch命令:

确保事务中的key有没有被其他客户端修改过,才执行事务,否则不执行(类似于乐观锁)。

(4)Redis+Lua语言实现限流

 

(5)分布式锁

首先需要Redis有互斥的能力,可以使用SETNX命令,(即如果key不存在,才会设置它的值,否则什么也不做。两个客户端进程可以执行这个命令,达到互斥,就可以实现一个分布式锁。

锁的过期时间不好计算

解决方案:分布式锁加入看门狗

加锁时,先设置一个过期时间,然后开启“守护线程”,定时检测这个锁的失效时间,如果快要过期了,操作共享资源还未完成,则自动对锁进行续期,重新设置过期时间。

(6)高并发高可用

主从复制:

提供了复制功能,实现了相同数据的多个Redis副本。每个主节点可以对应多个从节点,复制的数据流只能由主节点复制到从节点。

 

(7)哨兵:Redis Sentinel

背景:主从复制模式下,主节点故障,需要人工将从节点晋升为主节点。

2,8版本开始提供哨兵架构解决此问题。

主从复制的问题

  • 需要手动晋升子节点,同时需要修改应用方的节点地址。

  • 主节点的写能力收到单机限制

  • 主节点的存储能力收到单机的限制

六、为什么需要使用Redis


1、高性能

Mysql(磁盘)毫秒级

Redis(内存)微秒级

更新策略:项目启动时全量同步:热点数据

2、高并发

Mysql 并发量:1000/s

Redis 并发量:100000/s

集群架构

 

七、Redis的事务


回滚机制上,Redis只能对基本语法错误进行判断。运行时错误无法回滚。

八、Redis的过期策略以及内存淘汰机制


1、内存淘汰机制

  • 定期删除(定时扫描策略)

设置了过期时间的key放入独立字典,Redis默认会每秒进行十次过期扫描,不会遍历Key,而是采用简单的贪心策略。

  1. 从过期字典中随机20个key;

  1. 删除其中已经过期的;

  1. 如果过期比例超过1/4,则重复真个步骤;

一定要注意过期时间,如果大批量key过期(雪崩),需要给过期时间设置一个时间范围,不能全部同一时间过期

  • 惰性删除

客户端访问key的时候,redis对key的过期时间进行检查,如果过期就立即删除,不会返回任何东西。

总结:定期删除是集中处理,惰性删除是零散处理。

2、过期策略

  • 从库的过期策略

没有过期扫描,被动执行。

主库key到期时,在AOF文件里增加一条del指令,同步到所有从库。

3、缓存淘汰算法

当Redis内存超出物理内存限制时,内存数据开始和磁盘产生频繁的交换,此时性能会急剧下降。

通过maxmemory参数设置最大使用内存。

当内存超出时,Redis提供了集中策略来决定如何腾出新空间:

  • Noeviction:默认情况下不淘汰。超出maxmemory时只可读、删不准写。

  • volatile-lru:尝试设置了过期时间的key,最少使用的key先淘汰,没有设置过期时间的key不会被淘汰。

  • volatile-ttl:key的剩余寿命ttl的值,ttl越小越优先被淘汰。

  • volatile-random:淘汰过期集合中随机的key。

  • allkeys-lru:淘汰的key对象是全体的key集合。

  • allkeys-random:淘汰的key对象是全体的key集合中随机的key。

  • LRU算法:附加一个链表,按照一定顺序排列。空间满时,会踢掉链表尾部的元素。当链表中的某个元素被访问时,它会移动到链表的表头。链表元素排列顺序相当于元素最近被访问的时间顺序。

  • 近似LRU算法:Redis使用的此算法,使用LRU的原因是该链表需要消耗大量额外内存。

在现有的数据结构上使用随机采样法来淘汰元素。给每个key增加一个额外的小字段(24bit),最后一次访问的时间戳。

随机采样5(maxmemory-sample)个key,淘汰最旧的,重复该操作至内存低于maxmemory。

九、什么是缓存穿透?如何避免?


布隆过滤器的应用:

十、什么是缓存雪崩?如何避免?


原因:

1、Redis失效、宕机(故障)

  • 搭建Redis集群,主从架构

  • RDB持久化、IOF持久化

  • 加入缓存组件:EHCache,搭建多级缓存(容易高并发的数据存入)

  • 加入限流组件:hystrix,超过一定流量后,增加请求限制(保护数据处理层)

2、Redis大量key的ttl过期

  • ttl(过期时间)岔开,增加随机值,避免同一时间全部失效。

 

十一、Redis如何设计分布式锁


1、概念

  • 锁:同一时间只允许一个线程或者一个应用程序进入执行

  • 分布式锁:必须要求Redis有【互斥】能力,可以使用SETNX命令:即key不存在了才会设置它的值,否则什么也不做。

2、问题

  • 如何避免死锁

场景:程序处理业务逻辑异常,或者进程挂了,无法释放锁

避免方案:给锁设置租期(过期时间)

  • 锁的过期时间不好评估这么办?

加入看门狗:开启守护线程,定期检测锁的失效时间,如果快要过期了,业务还没执行完,则续期。

十二、什么是bigkey?会有什么影响?


1、概念:

key对应的value所占内存空间较大

例如一个字符串类型的value最大存到512M,一个列表类型的value最大可以存储2的32次方-1个元素。

2、字符串类型:

体现在单个value值特别大,一般认为超过10kb就是bigkey,和具体OPS相关(不同系统不同并发)。

3、非字符串类型:

哈希、列表、集合、有序集合,体现在元素个数过多。

4、危害:

  • 内存空间不均匀

  • 超时堵塞:单线程操作bigkey比较耗时

  • 网络拥塞:每次获取bigkey产生的网络流量较大

例如:一个bigkey为1MB,每秒访问为1000,则每秒产生1000MB的流量,普通千兆网(按照字节算是128MB/s)的服务器是灭顶之灾,而且服务器通常会采用单机多实例的方式来部署,可能会对其他实例造成影响。

5、解决方案:value拆分

十三、Redis如何解决key冲突


1、业务隔离

2、key的设计

业务模块+系统名称+关键(id),针对用户可以加入(userid)

3、分布式锁

场景:多个客户端并发写key

客户端拿到锁,才能进行操作,避免多个客户端竞争该key

4、时间戳

key拼接时间戳,根据时间戳保证多个客户端的业务执行顺序

十四、怎么提高换成命中率


1、提前加载

2、增加缓存的存储空间,增加缓存的数据

3、调整缓存的存储类型

例:对象通过Hash存储,而不用String。

根据业务做适当调整。

4、调整缓存的存储类型

  • 定时任务更新

  • MySQL通过检测binlog,将消息推送到Redis,更新缓存

  • 通过Mq,业务更新修改数据时,通过MQ发送消息,消费更新缓存

十五、Redis持久化方式有哪些方式?有什么区别?


1、持久化:

将数据写往磁盘,可以有效避免因进程退出造成的数据丢失,下次重启时利用之前持久化的文件恢复数据。

2、RDB(Redis DataBse):

当前数据生成快照(内存中的数据在某一时刻的状态记录)保存到硬盘的过程。

缺点:两次快照有时间间隔。

3、AOF:

已独立日志的方式记录每次写命令,重启时重新执行AOF文件中的命令恢复数据。

缺点:性能较差。

4、生产环境中一般采用混合两者的方式

如果执行bgrewriteaof命令,将内存中已有的数据以二进制格式存放在AOF文件中(模拟RDB),后续命令亦然采用AOF追加方式。

 

十六、为什么Redis需要把所有数据放到内存中?


  1. 内存访问与磁盘访问的差距:

 

  • 几乎是10倍以上,如果不是顺序读取而是随机读取效率会相差更大

  • 同时还有CPU上下文切换的开销

2、Redis通过异步,持久化将数据写入磁盘

3、随着技术的发展,硬件上来说内存也越来越便宜了

4、默认情况下,哪怕Redis内存不够了,也不会发生宕机,而是只可读不能写(Noeviction策略)

5、通过内存淘汰策略,确保整体服务正常运行

十七、如何保证缓存与数据库双写一致性?


1、新增数据类

新增数据时,数据会直接写入数据库,不用对缓存做任何操作;此时缓存没有新增数据,而数据库中是最新值。

2、更新缓存类

(1)先更新缓存,在更新DB(一般不考虑)

原因:缓存更新成功,更新数据库时出现异常,会导致数据源与缓存数据完全不一致,而且很难察觉,因为缓存中的数据一直都存在。

(2)先更新DB,在更新缓存(一般不考虑)

原因:数据库更新成功了,缓存更新失败了,同样会导致数据源与缓存数据完全不一致,也很难察觉。

3、删除缓存

(3)先删除缓存,后更新DB
问题:

两个请求:A(更新)和B(查询)

A -> 删除缓存中的数据 -> 更新数据库

B -> 查询缓存为空 -> 查询数据库 -> 补录到缓存

A -> 还未更新成功/事务还未提交,B -> 查询到的其实是数据库旧值

解决方案:
  • 先淘汰缓存

  • 再写数据库

  • 休眠1秒,再次淘汰缓存

这个休眠的时间需要评估项目的读数据业务逻辑的耗时,确保请求结束时,写请求可以删除读请求造成的缓存脏数据。

(4)先更新DB,后删除缓存

查询:先读缓存 -> 缓存没有就读数据库 -> 取出数据放入缓存 -> 同时返回响应。

更新:先更新数据库 -> 删除缓存

4、如何选择

一般线上更多偏向于删除缓存类操作(容易避免问题)

原因:

  • 删除缓存比在DB中要快,所以一般先更新DB,后删除缓存

  • 问题只会出现在查询比删除慢的情况,出现率相对最少

  • 同时延迟双删可以有效避免缓存不一致情况。

伪代码实现延迟双删:
redis.deykey(X)
db.update(X)
Thread.sleep(N)
redis.delKey(X)

十八、Redis集群方案


1、分布式解决方案 :Redis Cluster

3.0版本推出

场景:单机内存、并发、流量等瓶颈

方案:
(1)客户端分区:

优点:分区逻辑可控

缺点:需要处理数据路由、高可用、故障转移等问题

(2)代理方案:

优点:简化客户端分布式逻辑,升级维护便利

缺点:加重架构部署复杂度和性能损耗

  1. 虚拟槽分区(0~16383)

 

主节点数量基本不可能超过1000个,节点连接需要不断发送ping/pong命令,消耗网络带宽

3、集群功能限制

(1)key批量操作支持有限:mset、mget仅支持相同slot值的key。

(2)key事务操作支持有限:仅支持在同一节点上的事务操作。

(3)key作为数据分区的最小颗粒度,不允许大的键值对(hash、list)映射在不同节点。

(4)不支持多数据库空间。单机为0-15(16个),集群模式仅能使用db0。

(5)复制结构仅支持一层,节点只能复制到主节点,不支持嵌套树状复制结构。

4、搭建集群

方式:

(1)Redis协议手工搭建。

(2)5.0之前有ruby语言脚本搭建。

(3)5.0之后搭建功能合并至redis-cli。

节点数至少奇数点个,官方推荐三主三从。

十九、Redis集群方案什么情况下会导致整个集群不可用?


A、B、C三个节点集群,B节点失败(主故障,且没有替代方案)整个集群都是不可用的。

集群不可用判定:

保护措施:默认情况下当16384个槽点任何一个没有指派到节点时,整个集群不可用。

主节点下线->故障发现->自动完成转移期间,整个集群为不可用状态。

可用通过设置cluster-require-full-coverage配置为no:主节点故障时,不影响其他主节点的可用性。

二十、Redis集群会有写操作丢失吗?为什么?


Redis无法保证数据的强一致性

一般只能向主节点写入数据,再异步同步到子节点

此时如果响应给客户端后还未异步同步成功时,主节点宕机了,子节点升至主节点,此时就会出现写入操作丢失。

二十一、Redis常见性能问题和解决方案


1、持久化 性能问题

早期仅支持全量复制->部分复制(一台机器性能开销过大)

因此开始配置主从 :主节点不再做持久化而是交给从节点来做

2、数据比较重要,开启AOF。策略最好配置每秒同步。

3、主从复制 流畅,建议同一个局域网内操作,负责网络开销过大

4、尽量避免主库压力过大,增加从库

5、主从复制 尽量不要使用网状结构、线性结构

二十二、热点数据和冷数据


1、热数据

访问频次较高,考虑使用缓存Redis

地图信息

点赞数、收藏数、分享数(不断变化)同步Redis

数据更新之前至少读取2次才能放缓存

2、冷数据

访问频次少

不需要放缓存

二十三、什么情况下可能会导致Redis阻塞


1、客户端阻塞

命令执行时间过长: keys* Hgetall smembers 时间复杂度O(N)

2、BIGkey删除

需要释放大量占用内存 zset(100万的元素 删除大概需要2s)

3、清空库

flushdb flushall 涉及删除所有键值对

4、AOF日志同步写,记录AOF日志

大量写的操作

1一个同步写磁盘操作大概耗时1~2ms

5、从库 加载RDB文件

RDB文件过大

6、Redis尽量部署在独立的服务器中

二十四、线上Redis响应慢处理思路


  • 1、紧急处理方案,扩容

  • 2、生产环境查看Redis内存使用率,分析一定时间段内key数量变化

分析是否是大量数据未设置过期时间,或者是因为新版本迭代引起

  • 3、清除bigkey,优化生成bigkey的代码块,调整未设置过期时间的代码块

  • 4、根据业务场景调整淘汰策略

标签:总结,缓存,过期,Redis,常见问题,内存,key,节点
From: https://www.cnblogs.com/shoshana-kong/p/17475800.html

相关文章

  • Redis的rehash的策略
    背景:redis字典(hash表)当数据越来越多的时候,就会发生扩容,也就是rehash对比:java中的hashmap,当数据数量达到阈值的时候(0.75),就会发生rehash,hash表长度变为原来的二倍,将原hash表数据全部重新计算hash地址,重新分配位置,达到rehash目的redis中的hash表采用的是渐进式hash的方式:1、red......
  • redis之hash解析
    Redis底层数据结构之hashhash是日常开发过程中使用Redis的一个数据结构,其底层实现方式有两种,如下所示。一种是zipList,这种是当hash结构的V值较小的时候使用的编码方式。这个已经在上一篇文章中介绍过了。这篇文章主要讲解一下另外一种实现方式,字典dict,当hash结构的V值较大时采用......
  • Redis rehash
     Redisrehash是什么?Redisrehash是一种渐进式的哈希表扩展或收缩的机制,用于保持哈希表的负载因子在一个合理的范围内,提高哈希表的性能和空间利用率12。哈希表是Redis的基础数据结构,用于存储键值对。哈希表由一个数组和一个链表组成,数组的每个元素是一个指向链表的指针,链......
  • 深度学习降噪专题课:总结
    大家好,本课是本次专题课的最后一节课,给出了未来的研究改进方向,谢谢!加QQ群,获得相关资料,与群主交流讨论:106047770本系列文章为线上课程的复盘,每上完一节课就会同步发布对应的文章本课程系列文章可进入合集查看:深度学习降噪专题课系列文章合集未来的研究改进方向1.等待WebNNPo......
  • Redis高可用的三种实现方式
    Redis高可用的三种实现方式一、高可用的概念​高可用(HighAvailability,即HA),指的是通过尽量缩短日常维护操作和突发的系统崩溃所导致的停机时间,以提高系统和应用的可用性。一个业务系统如果全年无一时刻不在提供服务,它的可用性可达100%。那么什么样的系统可以称之为高可用呢,业......
  • redis的消息发布订阅实现
    文章目录前言一、创建好springboot项目,引入核心依赖二、使用步骤1.自定义一个消息接受类2.声名一个消息配置类3.编写一个测试类总结前言一般项目中都会使用redis作为缓存使用,加速用户体验,实现分布式锁等等,redis可以说为项目中的优化,关键技术实现立下了汗马功劳.今天带来它......
  • redis三:key常用命令
    1.keys*显示所有keyexistsk1k2...有几个就显示几2.typekey显示key的类型 3.delkey删除指定的key4unlinkkey非阻塞删除,del原子的有可能阻塞5.expirekey秒为key设置过期时间ttlkey查看还有多少秒过去,-1永不过期,-2表示已过期 6. redis带着16个库,默认在......
  • Redis实现分页和多条件模糊查询方案
    简介: 本文将基于Redis提供条件查询+分页的技术解决方案。 导言Redis是一个高效的内存数据库,它支持包括String、List、Set、SortedSet和Hash等数据类型的存储,在Redis中通常根据数据的key查询其value值,Redis没有模糊条件查询,在面对一些需要分页、排序以及条件查询的场景时(如......
  • Java开发 - 让你少走弯路的Redis集群搭建
    前言前文中,我们已经对Redis的单节点哨兵的搭建方式做了演示和测试,相信大家已经了解了怎么操作,虽然是单节点,但基本已经满足了部分公司的日常需要,毕竟Redis集群不是什么项目都适用,用上了Redis,也未必需要使用哨兵,甚至集群。但今天,我们还是要把Redis哨兵集群的搭建方式给大家做个分享,万......
  • redis工具类
    packagecom.yashi.common.utils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.HashOperations;importorg.springframework.data.redis.core.ListOperations;importorg.springframework.data.redis.cor......