为什么redis使用单线程反而更快?
内存操作:
redis是一个内存数据,内存的读写数据天生就是快;
单线程模型:
单线程模型避免了多线程上下文切花带来的开销
采用事件驱动的IO多路复用模型:
事件驱动的IO多路复用模型结合了非阻塞IO和事件驱动的优点,非常适合处理大量并发连接的场景。通过事件循环和事件处理器的机制,能高效的管理多个IO流,同时避免了线程上下文切换的开销,是现代高性能服务器架构的核心技术之一。
命令的原子性:
大部分命令都是原子性,命令的原子性和单线程模型可以确保在任何时刻只有一个命令在执行,消除了对复杂的数据结构的保护和锁的需求,提高整体效率。
数据结构优化:
redis使用了高效的数据结构,如跳跃表、压缩列表、哈希表等,这些数据结构提供了快速的查找和操作速度,有助于维持高性能。
C语言编写更接近于底层语言
TIP:主要瓶颈是网络IO和CPU的计算能力,而不是线程间的并行处理。在多核CPU环境下,Redis从v4.0开始引入了多线程来处理后台任务(如AOF重写),并在v6.0中增加了对多线程的网络处理支持。
redis有哪些常用的数据类型?
redis常用的数据类型有5种:String、List、Hash、Set、Sorted Set;如下图所示
String:
底层结构叫SDS(Simple Dynamic String),是一个带长度信息的字节数组;规定字符串长度不得超过512M;当字符串长度小于或等于44时,使用embstr形式存储,大于44时,使用raw形式存储;
- embstr形式存储:SDS的存储一般是连续的(意思就是redisObject对象头和SDS对象连续存在一起,malloc时一起分配)。
- raw形式存储:SDS不是连续存储的(需要两次malloc分配)
Hash: 存对象
使用
字典
(dict)。字典由一个哈希表和多个压缩列表或字典组成,当元素较少时使用压缩列表(ziplist),节省内存;当元素较多时转换成普通的哈希表。7.0+版本较少时使用的紧凑列表listpack;List: 可以做队列
使用
压缩列表
(ziplist)或 QuickList(快速列表)。对于小列表,使用压缩列表来节省空间;对于大列表,则使用双向链表。7.0+版本压缩列表
(ziplist)替换为紧凑列表(listpack);ZSet: 存储的value是不能重复的,分值score是可以重复的;
使用
字典
(dict)和跳跃表
(skiplist)。字典用来存储Value和分数Score的映射,跳跃表用来按照分数排序;当数据比较少的时候用ziplist存储,否则用跳跃表。Set: 有去重的功能
使用
整数集合
(intset)或字典
(dict)。当集合中的所有元素都可以被表示为整数时,使用整数集合来节省内存;当集合中有非整数元素时,转换成字典结构Stream
使用
字典
和快速链表
(quicklist)。每个流元素存储在快速链表中,整个流则是一个字典,其中键是流ID,值是快速链表
有序集合(Zset)底层是如何实现的?
有序集合是由hash 字典+ (ziplist(压缩列表)或 skiplist(跳跃表))组成。hash字典用来存储Value和分数Score的映射;
- 压缩列表ziplist本质上就是一个字节数组,压缩列表是一块连续的内存空间,没有任何冗余间隙;
- 跳跃表skiplist是一种有序的双向连链表结构;
tip: 只有完全满足以下两个条件才能使用ziplist,反之则使用skiplist
- 有序集合的元素个数小于128个
- 有序集合的所有元素成员的长度都必须小于64字节;
redis如何保证数据不丢失?
保存数据不丢失主要在两个方面:1. 持久化;2. 集群部署
持久化:
RDB持久化:快照方式持久化,将某一个时刻的内存数据,以二进制的方式写入磁盘;
优点:
- 速度快:
- 空间占用小
- 恢复速度快
- 可靠性高
缺点:
- 实时性差
- 数据可能会丢失
AOF持久化:文件追加持久化,记录所有非查询操作命令,并以文本的形式追加;
优点:
- 数据不容易丢失
- 实时性好
- 数据可读性强
缺点:
- 写入性能低
- 占用磁盘空间大
- AOF文件可能会出现损坏;
混合持久化:RDB+AOF混合方式的持久化,在写入的时候,先把当前的数据以RDB的形式写入文件的开头,再将后序的操作命令以AOF的格式存入文件;
TIP: 总结下RDB和AOF的优缺点主要在5个方面:
- 写入方式:RDB是通过快照的形式,以二进制的方式写入磁盘中,AOF是通过追加命令的形式,以命令的方式写入在AOF文件中;
- 数据恢复:RBD是某一时间点的数据,可以直接加载到内存中快速恢复;AOF则需要逐条执行命令恢复,需要时间长;
- 数据完整性:RDB文件保存的是某一时间点的数据快照,宕机后,可能会丢失一部分数据;AOF是记录redis所有的命令,因此AOF丢失的命令会少很多;
- 文件大小:RDB文件以二进制形式存储,文件小;AOF文件存储的是命令内容,则文件大;
- 性能影响:AOF文件追加写的方式不会阻塞;RDB文件在快照时可能会阻塞redis服务;
集群部署:
- 主从同步
- 哨兵模式
- redis 集群
redis的内存淘汰策略有哪些?什么时候触发呢?
redis在3种情况下会触发内存的淘汰策略:
- 当写操作内存超过限制:我们可以通过
config get maxmemory
查询redis能使用的最大内存值,当使用超过这个maxmemory
时就会触发内存淘汰策略;- 启用AOF重写操作:如果使用AOF持久化方式并开启AOF重写功能时,那么在执行AOF重写过程中会触发内存淘汰策略。AOF重写是将AOF原来的文件重写为一份更为紧凑的格式,减少内存的占用;
- 通过命令主动释放内存:通过主动执行MEMORY PURGE命令释放内存;
淘汰策略:
- noeviction:不淘汰任何数据,当内存不足时,新增操作会报错,Redis 默认内存淘汰策略;
- allkeys-lru:淘汰整个键值中最久未使用的键值;
- allkeys-random:随机淘汰任意键值;
- allkeys-lfu:淘汰整个键值中最少使用的键值。
- volatile-lru:淘汰所有设置了过期时间的键值中最久未使用的键值;
- volatile-lfu:淘汰所有设置了过期时间的键值中,最少使用的键值
- volatile-random:随机淘汰设置了过期时间的任意键值;
- volatile-ttl:优先淘汰更早过期的键值;
TIP: LRU是在一段时间范围内最久未使用,基于时间的维度;LFU是在一段时间范围内使用次数最少的,基于访问频率的维度;
缓存雪崩、缓存击穿、缓存穿透是什么?怎么解决?
缓存雪崩:指缓存中存在大量的键同时过期或失效,导致大量请求直接访问到数据库或服务;
- 原因:
- 大量缓存键同时过期
- 缓存服务器故障
- 解决方案:
- 设置随机过期时间
- 实现缓存预热:在服务启动或者缓存失效前,提前加载热点数据;
- 使用分布式缓存集群:减少单节点压力,提高系统的可用性和抗压性;
- 服务熔断或降级
- 增加监控和告警机制
缓存击穿:某个热点数据过期或失效,导致大量数据同时访问数据库或服务;
- 原因:
- 热点数据失效
- 并发访问热点数据
- 解决方案:
- 设置热点数据永不过期或过期时间比较长
- 加互斥锁或分布式锁;加分布式锁,只有一个线程能进入;
- 限制并发访问;限制热点数据并发访问的量;
缓存穿透:恶意请求查询不存在缓存和数据库中的数据,导致这些请求直接访问数据库;
- 原因:
- 恶意请求
- 高并发请求(正常请求):当有大量的并发请求通过查询不存的数据时,可能会导致缓存无法命中,从而触发缓存穿透;
- 解决方案:
- 使用布隆过滤器:
- 缓存空值处理:可以将查询出来的空值做一个短期的缓存,防止相同的请求打到数据库;
- 异步缓存加载:缓存未命中时,可以异步加载数据到缓存中,通过使用分布式锁来保证只有一个线程去加载数据,避免重复加载;
- 限制恶意请求:通过访问频率控制、验证码等手段