redis
2020-10-09 16:54:07 34 举报
AI智能生成
redis
作者其他创作
大纲/内容
数据结构
基本数据结构
字符串string
应用场景:
1.缓存功能:String字符串是最常用的数据类型,不仅仅是Redis,各个语言都是最基本类型,因此,利用Redis作为缓存,配合其它数据库作为存储层,利用Redis支持高并发的特点,可以大大加快系统的读写速度、以及降低后端数据库的压力。
2.计数器:许多系统都会使用Redis作为系统的实时计数器,可以快速实现计数和查询的功能。而且最终的数据结果可以按照特定的时间落地到数据库或者其它存储介质当中进行永久保存。
3.共享用户Session:用户重新刷新一次界面,可能需要访问一下数据进行重新登录,或者访问页面缓存Cookie,但是可以利用Redis将用户的Session集中管理,在这种模式只需要保证Redis的高可用,每次用户Session的更新和获取都可以快速完成。大大提高效率。
1.缓存功能:String字符串是最常用的数据类型,不仅仅是Redis,各个语言都是最基本类型,因此,利用Redis作为缓存,配合其它数据库作为存储层,利用Redis支持高并发的特点,可以大大加快系统的读写速度、以及降低后端数据库的压力。
2.计数器:许多系统都会使用Redis作为系统的实时计数器,可以快速实现计数和查询的功能。而且最终的数据结果可以按照特定的时间落地到数据库或者其它存储介质当中进行永久保存。
3.共享用户Session:用户重新刷新一次界面,可能需要访问一下数据进行重新登录,或者访问页面缓存Cookie,但是可以利用Redis将用户的Session集中管理,在这种模式只需要保证Redis的高可用,每次用户Session的更新和获取都可以快速完成。大大提高效率。
字典hash
应用场景:
Redis中的Hashes类型可以看成具有String Key和String Value的map容器
Redis中的Hashes类型可以看成具有String Key和String Value的map容器
列表list
应用场景:
List 存储一些列表型的数据结构,类似粉丝列表、文章的评论列表之类的东西。
比如,我们常用的博客网站的文章列表,当用户量越来越多时,而且每一个用户都有自己的文章列表,而且当文章多时,都需要分页展示,这时可以考虑使用Redis的列表,列表不但有序同时还支持按照范围内获取元素,可以完美解决分页查询功能。大大提高查询效率
List 存储一些列表型的数据结构,类似粉丝列表、文章的评论列表之类的东西。
比如,我们常用的博客网站的文章列表,当用户量越来越多时,而且每一个用户都有自己的文章列表,而且当文章多时,都需要分页展示,这时可以考虑使用Redis的列表,列表不但有序同时还支持按照范围内获取元素,可以完美解决分页查询功能。大大提高查询效率
集合set
应用场景:
Set 是无序集合,会自动去重,有需要去重的功能可以直接放进set,自动就完成了去重
你当然也可以基于 JVM 内存里的 HashSet 进行去重,但是如果你的某个系统部署在多台机器上呢?得基于Redis进行全局的 Set 去重。
可以基于 Set 玩儿交集、并集、差集的操作,比如交集吧,我们可以把两个人的好友列表整一个交集,看看俩人的共同好友是谁?对吧
Set 是无序集合,会自动去重,有需要去重的功能可以直接放进set,自动就完成了去重
你当然也可以基于 JVM 内存里的 HashSet 进行去重,但是如果你的某个系统部署在多台机器上呢?得基于Redis进行全局的 Set 去重。
可以基于 Set 玩儿交集、并集、差集的操作,比如交集吧,我们可以把两个人的好友列表整一个交集,看看俩人的共同好友是谁?对吧
有序集合SortedSet
应用场景:
Sorted set 是排序的 Set,去重但可以排序
排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等
Sorted set 是排序的 Set,去重但可以排序
排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等
其他数据结构
HyperLogLog,Geo,Pub/Sub
用于防止缓存击穿的数据结构
Redis Module,像BloomFilter,RedisSearch,Redis-ML
数据一致性
使用redis的方式:
1.读的时候,先读缓存,缓存没有的时候读取数据库,然后取出数据放入缓存并返回响应
2.写的时候,先更新数据库再删除缓存。
1.读的时候,先读缓存,缓存没有的时候读取数据库,然后取出数据放入缓存并返回响应
2.写的时候,先更新数据库再删除缓存。
存在的问题:
更新DB成功后删除缓存失败,从而导致数据不一致。
解决办法:
可以先删除缓存,再更新DB,这样即使更新DB操作失败,数据也不会不一致,但是在高并发的情况下,可能出现这种情况。如果当删除完缓存的时候,这时去更新数据库,但还没有更新完,另外一个请求来查询数据,发现缓存里没有,就去数据库里查,还是以上面商品库存为例,如果数据库中产品的库存是100,那么查询到的库存是100,然后插入缓存,插入完缓存后,原来那个更新数据库的线程把数据库更新为了99,导致数据库与缓存不一致的情况
此时的解决办法是:加队列
更新DB成功后删除缓存失败,从而导致数据不一致。
解决办法:
可以先删除缓存,再更新DB,这样即使更新DB操作失败,数据也不会不一致,但是在高并发的情况下,可能出现这种情况。如果当删除完缓存的时候,这时去更新数据库,但还没有更新完,另外一个请求来查询数据,发现缓存里没有,就去数据库里查,还是以上面商品库存为例,如果数据库中产品的库存是100,那么查询到的库存是100,然后插入缓存,插入完缓存后,原来那个更新数据库的线程把数据库更新为了99,导致数据库与缓存不一致的情况
此时的解决办法是:加队列
事务
概念:Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
事务的几个关键命令
事务中发生错误
在调用EXEC命令之前出现错误
可能原因:
命令可能存在语法错误(参数数量错误,错误的命令名称…);或者可能存在某些关键条件,如内存不足的情况(如果服务器使用maxmemory指令做了内存限制)。
redis的结果:
此时redis会返回错误,中止事务并清除命令队列
命令可能存在语法错误(参数数量错误,错误的命令名称…);或者可能存在某些关键条件,如内存不足的情况(如果服务器使用maxmemory指令做了内存限制)。
redis的结果:
此时redis会返回错误,中止事务并清除命令队列
在调用EXEC命令之后出现错误
可能原因:
使用错误的值对某个key进行操作,比如使用list操作命令操作string
redis的结果:
此时redis会执行完剩余的正确命令。错误的命令报错
使用错误的值对某个key进行操作,比如使用list操作命令操作string
redis的结果:
此时redis会执行完剩余的正确命令。错误的命令报错
事务的执行过程
1.开启事务
MULTI 命令的执行标记着事务的开始:这个命令唯一做的就是, 将客户端的 REDIS_MULTI 选项打开, 让客户端从非事务状态切换到事务状态
2.命令入队
当客户端进入事务状态之后, 服务器在收到来自客户端的命令时, 不会立即执行命令, 而是将这些命令全部放进一个事务队列里, 然后返回 QUEUED , 表示命令已入队:
3.执行事务
EXEC命令执行,事务队列中的所有命令会被执行,客户端会从事务状态返回非事务状态,事务执行完成
流程图
Q&A
redis和Memcached 的区别?
1.memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
2.redis可以持久化其数据
3.性能对比由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据中,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起Memcached,还是稍有逊色
2.redis可以持久化其数据
3.性能对比由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据中,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起Memcached,还是稍有逊色
MySQL里有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据?
redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。
redis常见性能问题和解决方案
(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…
这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…
这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
单线程的redis,为什么这么快?
1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路I/O复用模型,非阻塞IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4、使用多路I/O复用模型,非阻塞IO;
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
为什么Redis是单线程的?
官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。PS:就是说单线程已经很快了,没必要多线程
Redis的Key和value的大小限制是多少?
redis的key和string类型value限制均为512MB
一个redis实例可以处理多少key? Hash, List, Set, Sorted Set最多可以装多少元素?
Redis实例可以处理2^32个实例 ,大概2.5亿个键.
每个hash, list, set, and sorted set, 可以装2^32个元素.
换句话说,你的限制取决于你的可用内存
每个hash, list, set, and sorted set, 可以装2^32个元素.
换句话说,你的限制取决于你的可用内存
假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?
使用keys指令可以扫出指定模式的key列表。但是redis是单线程的,keys命令会造成线上服务卡顿,不要使用。可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长
但是对于 SCAN 这类增量式迭代命令来说, 因为在对键进行增量式迭代的过程中, 键可能会被修改, 所以增量式迭代命令只能对被返回的元素提供有限的保证 。
但是对于 SCAN 这类增量式迭代命令来说, 因为在对键进行增量式迭代的过程中, 键可能会被修改, 所以增量式迭代命令只能对被返回的元素提供有限的保证 。
redis数据持久化
两种持久化方式
AOF
概念:以日志形式记录每一次对redis的写命令,可以通过重复执行记录的命令来进行数据恢复
AOF的重写机制:AOF提供了重写机制,去除无用指令以及合并多条指令达到压缩的目的。
更小的文件也意味着更快的加载速率,重写不会阻塞redis
更小的文件也意味着更快的加载速率,重写不会阻塞redis
优点:
1.AOF默认一秒钟执行一次fsync操作,即使发生故障,也最多丢失那一秒钟没来得及写入日志的命令数据。
2.AOF对误操作有更强的容错性。由于AOF是逐条命令的记录,因此当发生灾难性的数据误操作时(比如清库),只要这个时候AOF还没有被重写(重写机制),先把redis停掉,然后把AOF文件拷贝出来,将最后的误操作命令删除(比如清库的FLUSHALL),然后用修改过的AOF做数据恢复即可。
1.AOF默认一秒钟执行一次fsync操作,即使发生故障,也最多丢失那一秒钟没来得及写入日志的命令数据。
2.AOF对误操作有更强的容错性。由于AOF是逐条命令的记录,因此当发生灾难性的数据误操作时(比如清库),只要这个时候AOF还没有被重写(重写机制),先把redis停掉,然后把AOF文件拷贝出来,将最后的误操作命令删除(比如清库的FLUSHALL),然后用修改过的AOF做数据恢复即可。
缺点:
1.AOF文件记录的更详尽,意味着文件占用空间大
2.AOF数据恢复的速度相对于RDB更慢
1.AOF文件记录的更详尽,意味着文件占用空间大
2.AOF数据恢复的速度相对于RDB更慢
RDB
概念:生成某个时刻的redis数据快照,恢复时,直接加载快照即可
优点:
数据恢复速度相当AOF更快
数据恢复速度相当AOF更快
缺点:
1.因为是某个时刻的快照,在这个时刻和下一个备份点之间插入的数据会丢失,不适用于对数据敏感的场景
2.RDB经常需要fork子进程来保存快照,创建的过程是阻塞的重量级操作。如果数据量过大,可能造成线上服务卡顿
1.因为是某个时刻的快照,在这个时刻和下一个备份点之间插入的数据会丢失,不适用于对数据敏感的场景
2.RDB经常需要fork子进程来保存快照,创建的过程是阻塞的重量级操作。如果数据量过大,可能造成线上服务卡顿
持久化的选择,官方推荐是RDB和AOF同时开启,如果系统可以容忍几分钟的数据丢失,可以只用RDB。不建议只用AOF,因为AOF加载较慢。
redis集群
基本概念
1.集群中的每一个redis称为一个节点
2.有两种节点,主节点Master,从节点slave。每个节点都要设置一个优先级,即这个节点在故障切换过程中被选举为master的优先级
3.集群的可用性判断:集群中任何一个master挂了,且这个master没有slave可用,那么这个集群就是挂了
2.有两种节点,主节点Master,从节点slave。每个节点都要设置一个优先级,即这个节点在故障切换过程中被选举为master的优先级
3.集群的可用性判断:集群中任何一个master挂了,且这个master没有slave可用,那么这个集群就是挂了
主从复制
基本概念
复制ID:master节点的数据集的一个随机字符串ID,标记了某个master节点的数据集,当一个master节点重启或者一个slave节点升级为master节点时,复制ID会重新生成
偏移量:可以理解为数据的版本号,每次写数据,偏移量都会递增。偏移量一致,则表示数据同步一致
全量复制:顾名思义,使用RDB做master的完全拷贝。一般发生在新slave节点启动或者旧slave节点重连且无法满足增量数据复制条件的时候
增量复制:master向slave发送每一次写操作的命令,是主从复制优先尝试的方式
主从复制过程:
1.当一个slave启动的时候,会使用PSYNC命令将原本的master的复制ID以及偏移量发送给现在的master,请求同步数据
2.如果masterID还可以追溯(复制ID没变或者复制ID是最近故障的master的ID,还保存在新的master中,节点升级 为master,复制ID变了,可能还是做增量复制),复制积压缓存足够,那就直接做增量复制
3.如果复制ID已经无法追溯,或者挤压缓存不足,则做全量备份
1.当一个slave启动的时候,会使用PSYNC命令将原本的master的复制ID以及偏移量发送给现在的master,请求同步数据
2.如果masterID还可以追溯(复制ID没变或者复制ID是最近故障的master的ID,还保存在新的master中,节点升级 为master,复制ID变了,可能还是做增量复制),复制积压缓存足够,那就直接做增量复制
3.如果复制ID已经无法追溯,或者挤压缓存不足,则做全量备份
全量复制过程
1.master节点执行BGSAVE命令fork子进程,生成RDB快照。同时在缓存新进来的写命令
2.master将快照发送给slave
3.slave收到RDB加载并刷新数据
4.master将缓存的新命令以命令流的形式发送给slave
1.master节点执行BGSAVE命令fork子进程,生成RDB快照。同时在缓存新进来的写命令
2.master将快照发送给slave
3.slave收到RDB加载并刷新数据
4.master将缓存的新命令以命令流的形式发送给slave
积压缓存区的判断过程
1.主从分别维护一个seq,主每次完成一个请求就seq加一,从每次同步完后就更新自己的seq
2.从每次打算同步时,都是携带自己的seq到主,主将自身的seq与从做差结果与挤压缓存区大小 比较,如果小于挤压缓存区大小,直接从挤压缓存区取相应的操作做部分重同步
3.否则说明挤压缓存区的数据不足以cover主从不一致的数据,即做全量同步
1.主从分别维护一个seq,主每次完成一个请求就seq加一,从每次同步完后就更新自己的seq
2.从每次打算同步时,都是携带自己的seq到主,主将自身的seq与从做差结果与挤压缓存区大小 比较,如果小于挤压缓存区大小,直接从挤压缓存区取相应的操作做部分重同步
3.否则说明挤压缓存区的数据不足以cover主从不一致的数据,即做全量同步
如何保持集群对节点状态的一致性:gossip协议
gossip
集群高可用
1.主从模式
概念:最简单的模式,一个集群中有且只有一个主节点,主节点可读写,从节点只读。主节点下线,影响写服务,不影响读服务服务
数据同步机制:当slave启动后,主动向master发送SYNC命令。master接收到SYNC命令后在后台保存快照(RDB持久化)和缓存保存快照这段时间的命令,然后将保存的快照文件和缓存的命令发送给slave。slave接收到快照和命令后,加载快照文件和缓存的执行命令。复制初始化后,master每次接收到的写命令都会同步发送给slave,保证主从数据的一致性
缺点:master挂掉后,不能继续提供写服务(slave不会自动升级为master),因此主从模式,只提供了读写分离的特性,不满足高可用
2.哨兵模式(sentinal)
概念:基于主从模式,多了哨兵的角色。sentinal由一个或多个sentinal实例组成的sentinal系统可以监视任意多个主服务器,以及这些主服务器属下的从服务器,并在被监视的主服务器进入线下状态时,自动将下线主服务器属下的从服务器升级为新的主服务器
自动故障切换机制:
1.当一个master被大多数的sentinal标记为主观下线时,这个master从主观下线标记为客观下线,failover故障机制会被触发
2.投票产生领头sentinal,每个发现master客观下线的sentinal都会要求其他sentinal将自己选为领头去执行故障迁移;投票采用先到先得策略,已经给某个候选人投过票的sentinal,不再接受其他候选人的要求投票请求
3.选举slave升级为master,再淘汰已下线,超时,响应慢的slave之后,领头sentinal先后根据节点的优先级,复制的偏移量(标记slave复制了多少master数据),进程id,选择最优的slave升级为master。将原master和slave都归为新master的slave
4.更新节点配置信息
1.当一个master被大多数的sentinal标记为主观下线时,这个master从主观下线标记为客观下线,failover故障机制会被触发
2.投票产生领头sentinal,每个发现master客观下线的sentinal都会要求其他sentinal将自己选为领头去执行故障迁移;投票采用先到先得策略,已经给某个候选人投过票的sentinal,不再接受其他候选人的要求投票请求
3.选举slave升级为master,再淘汰已下线,超时,响应慢的slave之后,领头sentinal先后根据节点的优先级,复制的偏移量(标记slave复制了多少master数据),进程id,选择最优的slave升级为master。将原master和slave都归为新master的slave
4.更新节点配置信息
缺点:(其实是单master的缺陷)
1.每个节点保存的数据都一样,存在数据冗余,造成内存浪费
2.因为每个节点保存的数据都一样,master的最大存储容量决定了整个集群的容量,上限有限
3.如果数据量较多,故障恢复需要较长的时间
1.每个节点保存的数据都一样,存在数据冗余,造成内存浪费
2.因为每个节点保存的数据都一样,master的最大存储容量决定了整个集群的容量,上限有限
3.如果数据量较多,故障恢复需要较长的时间
参考链接
3.集群模式(cluster)
概念:基于主从模式和哨兵模式,引入分片的设计。通过hash计算,将数据分开存储在对应节点的槽(slot)
分片hash算法
实现:CRC16(key) mod 16384(redis使用)
很好的解决了一致性hash的缺点
一致性hash算法
一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,就是对2^32去模
假设有三台服务器部署集群,首先通过hash(服务器A的IP地址) % 2^32,确定他们在环上的位置。数据访问时: 将数据key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器
优点:
这样删除一个服务器节点或者新增一个服务器节点影响的数据不再是全部数据
这样删除一个服务器节点或者新增一个服务器节点影响的数据不再是全部数据
参考文档
未使用一致性hash原因
一致性哈希算法对于数据分布、节点位置的控制并不是很友好,而Redis Cluster的槽位空间是自定义分配的
集群新增和删除节点
新增节点
1.启动节点(主节点)
2.redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7000将节点加入集群
3.执行redis-cli --cluster reshard 127.0.0.1:7000重新分配集群slot
4.新增节点8为节点7的从节点 redis-cli --cluster add-node 127.0.0.1:7008 127.0.0.1:7000 --cluster-slave --cluster-master-id ef1bcdb677b1c8f8c3d290a9b1ce2e54f8589835
2.redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7000将节点加入集群
3.执行redis-cli --cluster reshard 127.0.0.1:7000重新分配集群slot
4.新增节点8为节点7的从节点 redis-cli --cluster add-node 127.0.0.1:7008 127.0.0.1:7000 --cluster-slave --cluster-master-id ef1bcdb677b1c8f8c3d290a9b1ce2e54f8589835
删除节点
1.先删除从节点再删除主节点,不然哨兵会执行故障转移
2.redis-cli --cluster del-node 127.0.0.1:7000 f24b935a50a788692479c6beaf7c556f6d082253 (7000代表的是集群) 后接删除的节点id
3.下线主节点的时候记得执行reshard,把slot分配给剩余节点
2.redis-cli --cluster del-node 127.0.0.1:7000 f24b935a50a788692479c6beaf7c556f6d082253 (7000代表的是集群) 后接删除的节点id
3.下线主节点的时候记得执行reshard,把slot分配给剩余节点
在节点变化过程中数据不会受到影响
优点:高可用,可扩展。横向扩容可支持海量数据
集群官方文档
keepalived机制(双机热备)
优点:通过VIP的漂移实现主备切换,提供不间断的读写服务。实现简单,成本低
缺点:单机吞吐量有限
集群的脑裂问题
应用
常见问题
缓存击穿
问题描述:缓存的某个热点key一直抗着大量并发查询。在这个key失效的瞬间,大量的查询击穿缓存,打到数据库(想象一个水桶底部破了一个洞)
解决办法:
1.设置热点数据永不过期
2.限流(牺牲部分用户体验保证系统可用性)
1.设置热点数据永不过期
2.限流(牺牲部分用户体验保证系统可用性)
缓存雪崩
问题描述:大量的热点key在同一时间失效,瞬间打崩数据库
解决方法:
1.设置热点数据永不过期,有更新操作就更新缓存就行了
2.存入数据时,设置失效时间上加一个随机值,保证热点key不在同一时间失效
3.redis是集群部署,把热点数据分布在不同的redis库中也能避免全部失效的问题
1.设置热点数据永不过期,有更新操作就更新缓存就行了
2.存入数据时,设置失效时间上加一个随机值,保证热点key不在同一时间失效
3.redis是集群部署,把热点数据分布在不同的redis库中也能避免全部失效的问题
缓存穿透
问题描述:大量查询缓存和数据库中都不存在的数据,导致数据库压力过大,可能挂掉。常被用来使用不存在的key进行恶意攻击
解决办法:
1.增加参数校验
2.从nginx网关层进行配置,对于单个ip,每秒访问次数超过阙值都拉黑
3.布隆过滤器(Bloom Fliter)可以高效的判断,key是否存在于数据库,如果不存在就直接返回,如果存在就去查DB刷新KV然后return
4.如果从数据库查不到的数据可以直接缓存一个null,不过失效时间设置的不宜太长,比如1分钟
1.增加参数校验
2.从nginx网关层进行配置,对于单个ip,每秒访问次数超过阙值都拉黑
3.布隆过滤器(Bloom Fliter)可以高效的判断,key是否存在于数据库,如果不存在就直接返回,如果存在就去查DB刷新KV然后return
4.如果从数据库查不到的数据可以直接缓存一个null,不过失效时间设置的不宜太长,比如1分钟
布隆过滤器
缓存淘汰策略
过期机制
定期删除
redis默认100ms就会去随机检查一些设置了过期时间的key,如果过期了,就删除。ps:随机,如果全部检查,redis早卡死了
惰性删除
获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除
存在的问题
由于redis定期删除是随机抽取检查,不可能扫描清除掉所有过期的key并删除,然后一些key由于未被请求,惰性删除也未触发。这样redis的内存占用会越来越高。此时就需要内存淘汰机制
过期机制的官方文档
常见的缓存淘汰策略
FIFO:First In First Out 先进先出。判断被储存的时间,离目前最远的时间的数据优先被淘汰
LRU:Least Recently Used 最近最少使用。判断最近被使用的时间,离目前最远的数据优先被淘汰
LFU:Least Frequently Used 最不经常使用。在一段时间内,数据使用次数最少的被淘汰
redis提供的缓存淘汰策略
noeviction:当内存达到限制并且客户端尝试执行会让更多内存被使用的命令时,返回错误(大部分的写入指令,但DEL和几个例外不会导致内存增加)
allkeys-lru::尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
volatile-lru:尝试回收最少使用的键(LRU),但仅限于在过期集合的键(有设置过期时间),使得新添加的数据有空间存放。
allkeys-random: 回收随机的键使得新添加的数据有空间存放。
volatile-random::回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键(有设置过期时间)
volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。
如果没有键满足回收的前提条件的话,策略volatile-lru, volatile-random以及volatile-ttl就和noeviction 差不多了
关于淘汰策略的官方文档
分布式锁
先用setnx来争抢锁,抢到之后,再用expire给锁加一个失效时间,防止锁忘记了释放
如果在setnx之后,expire之前,进程crash或者重启,锁就得不到释放。现在可以把setnx和expire合成一条命令使用
jedis实现分布式锁
异步队列
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。 也可以使用 blpop,在没有消息的时候,它会阻塞住直到消息到来。
使用pub/sub主题订阅者模式,可以实现 1:N 的消息队列,但是 在消费者下线的情况下,生产的消息会丢失,此时得使用专业的消息队列如RocketMQ
使用pub/sub主题订阅者模式,可以实现 1:N 的消息队列,但是 在消费者下线的情况下,生产的消息会丢失,此时得使用专业的消息队列如RocketMQ
延时队列
使用sortedset,拿时间戳当作score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理
0 条评论
下一页