redis
Nosql概述
为什么使用Nosql
- 单机时代
- 进入大数据时代,一般的数据库无法进行分析处理
- 以前一个网站访问量不大,单个数据库可以满足需求。而且更多是静态html网页,服务器根本没有太大压力
- 如今,整个网站瓶颈
- 数据量太大,一个服务器放不下
- 数据的索引(B+tree),一个服务器内存也放不下
- 访问量(读写混合),一个服务器也承受不了
- Memcached(缓存)+MySql+垂直拆分(读写分离)
- 网站大多数情况下都是在执行读业务,每次都去查询数据库的话十分麻烦且效率低,为了减轻从服务器读取数据的压力,可以使用缓存来保证效率
- 缓存发展过程:优化数据结构和索引–>文件缓存(IO)–>Memcached(当时最热门技术)
- 分库分表+水平拆分(MySql集群)、
- 使用分库分表解决读写压力
- MySql集群满足当时大多数需求
- 至今
- 数据量很大,变化很快,MySql等关系型数据库不适用
- 若使用MySql存储博客、图片、文件等,数据库表增大,读写效率低
- 如果有一种数据库能拿来专门处理这种数据,MySql压力就变小
- 为什么使用NoSql
- 用户的个人信息、社交网络、地理位置、用户自己产生的数据、用户日志等数据爆发式增长
- 这时候需要使用NoSql数据库,可以很好地处理这些情况
什么是NoSql
-
NoSql = Not Only Sql
-
NoSql泛指非关系型数据库,随着web2.0互联网的诞生,传统的关系型数据库很难对付大规模的高并发数据
-
很多数据存储不需要一个固定的格式,不需要多余的操作就可以横向扩展
-
NoSql特点
- 方便扩展:数据之间没有关系,很好扩展
- 大数据量高性能:NoSql的缓存是记录级别的,是一种细粒度的缓存,性能比较高
- 数据类型多样:不需要事先设计数据库,随取随用
-
实践:NoSql + MySql
NoSql四大分类
- KV键值对
- 文档型数据库(bson格式,和json一样)
- MongoDB:
- 一个基于分布式文件存储的数据库,C++编写,主要用来处理大量文档型数据库
*是一个介于关系型数据库和非关系型数据库的中间产品,是分关系型数据库中功能最丰富的
- 一个基于分布式文件存储的数据库,C++编写,主要用来处理大量文档型数据库
- MongoDB:
- 列存储数据库
- HBase
- 分布式文件系统
- 图关系数据库
- 存储关系数据,如社交网络、推荐系统
- Neo4j、InfoGrid
Redis
概述
- Redis(Remote Dictionary Server),即远程字典服务
- key-value数据库
- 周期性把更新的数据写入磁盘或者把修改操作写入追加的记录文件
- 实现master-slave(主从)同步
- 最热门NoSql技术之一,被称之为结构化数据库
Redis能做什么
- 内存存储、持久化、内存中的数据是断电即失的,持久化很重要(rdb、aof)
- 效率高、可以用于高速缓存
- 发布订阅系统
- 地图信息分析
- 计时器、计数器(如浏览量、点赞量等)
- 消息中间件MQ
- …
为什么快
- Redis是基于内存操作,CPU不是性能瓶颈,Redis是根据机器的内存和网络带宽,既然可以使用单线程来实现,就是用单线程
- 误区1:高性能的服务器一定是多线程的
- 误区2:多线程(涉及上下文切换)一定比单线程效率高
- Redis是将所有的数据放在内存中,所以说使用单线程去操作效率就是最高的,多线程涉及耗时的上下文切换,对于内存系统来说,没有上下文切换效率就是最高的,多次读写都在同一个cpu上
数据类型
- Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)
String
- string 是 redis 最基本的类型,一个 key 对应一个 value
- string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据比如jpg图片或者序列化的对象
- string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB
>set name "xxx" >get name
- 使用场景:
- value除了是字符串还可以是数字
- 计数器
- 统计多单位的数量uid:4454:follow 0
- 对象缓存存储
Hash
- Redis hash 是一个键值(key=>value)对集合
- Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象
HMSET
设置了两个 field-value 对,HGET
获取对应 field 对应的 value- 每个 hash 可以存储
2
32
−
1
2^{32}-1
232−1键值对(40多亿)
>HMSET person name "xxx" age 18 >HGET person name >HGET person age
- 应用场景
- 使用与经常变更的信息,如用户信息之类
- 更适合对象的存储,string更适合存储字符串
List
- List是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
- 列表最多可存储
2
32
−
1
2^{32}-1
232−1元素
>lpush userlist xxx >lpush userlist yyy >lpush userlist zzz >lrange userlist 0 10 # 列出前10个元素,闭区间
- 实际是一个双向链表
- 应用场景
- 可以用来实现栈,队列,消息队列等
Set
- Redis 的 Set 是 string 类型的无序集合
- 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)
- 集合中最大的成员数为
2
32
−
1
2^{32}-1
232−1
>sadd users aa >sadd users bb >sadd users cc >smenbers users
- 应用场景
- 可以进行集合的交并补运算
- 可以实现共同关注,共同爱好,共同好友、推荐好友等功能
zset
- Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员
- 不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序
- zset的成员是唯一的,但分数(score)却可以重复
>zadd users 0 aa >zadd users 2 bb >zadd users 1 cc >zrangebyscore users 0 10
- 底层实现:跳表
- 应用场景:
- 重要信息,带权重进行判断
- 排行版应用实现,top K
Redis 命令
Redis事务
- Redis事务本质:
- 一组命令的集合,一个事务中的所有命令都会被序列化,在事务的执行过程中,会按顺序执行
- 一次性、顺序性、排他性
- Redis事务没有隔离级别的概念
- 所有的命令在事务中,并没有被直接执行,只有发起执行命令(exec)的时候才会按顺序执行
- Redis单条命令保证原子性,但是事务不保证原子性
- 编译时异常:代码有问题,命令有错
- 事务所有的命令都不会被执行
- 运行时异常:执行命令时某些命令执行异常、
- 除了异常命令,其余命令都可以正常执行
- 编译时异常:代码有问题,命令有错
- Redis 最简单的事务实现方式是使用 MULTI 和 EXEC 命令将事务操作包围起来
- 开启事务(multi)
- 命令入队(…)
- 执行事务(exec)
Redis实现乐观锁
- 使用
watch
可以当作redis的乐观锁操作 - 更新数据时判断,在此期间目标数据是否被修改(是指就是CAS)
- 获取version
- 比较version
- 不一致则执行失败
- 示例
- 客户端1执行以下指令,在exec执行之前客户端2插入,对money进行修改,以下事务将执行失败
> set money 100 > set out 0 > watch money # 监视money对象 > multi # 开启事务 > decrby money 20 > incrby out 20 > exec # 事务执行之前,客户端2执行修改,执行失败,返回nil > # 可以unwatch,重复上面指令,直至执行成功
- 客户端2执行以下指令,修改被客户端1监视的money数据
> get money > set money 1000
- 客户端1执行以下指令,在exec执行之前客户端2插入,对money进行修改,以下事务将执行失败
Redis.conf
- redis服务启动的时候,通过配置文件启动,里面设置redis的参数
- 单位,对大小写不敏感
- 可以包含其他配置文件
- 网络配置
- 绑定本地ip
- 保护模式开启及端口号
- 绑定本地ip
- 通用配置
- 是否以后台运行(守护进程)方式启动,一般需要开启
- 如果以后台进程方式启动redis服务,需要指定pid进程文件
- 日志级别
- 日志保存文件名
- 数据库的数量
- 是否以后台运行(守护进程)方式启动,一般需要开启
- 快照
-
用于持久化,在规定时间内,执行了指定数次操作,数据就会被持久化到文件
save 3600 1 # 3600秒内,至少有1个key被修改,进行持久化 save 300 10 # 300秒内,至少有10个key被修改,进行持久化 save 60 10000 # 60秒内,至少有10000个key被修改,进行持久化,高并发场景
-
持久化出错,是否继续工作
-
是否压缩RDB文件,需要消耗cpu资源
-
RDB持久化时,是否校验RDB文件,检查错误
-
保存RDB文件的目录文件名
-
- 安全
- 可以设置redis密码
> config get requirepass # 获取redis密码 > config set requirepass "123456" # 设置redis密码 > auth 123456 # 使用密码进行登录
- 可以设置redis密码
- 客户端最大数量限制,默认最大10000
- 配置最大内存设置
- 达到最大配置内存的处理策略
- AOF配置
- 默认不开启,redis默认持久化方式是RDB
- AOF文件名
- 持久化频率
appendfsync always # 每次修改都会同步 appendfsync everysec # 每秒执行一次同步,可能会丢失1s内的数据 appendfsync no # 不执行同步,操作系统进行数据同步,速度最快
- 默认不开启,redis默认持久化方式是RDB
Redis持久化
- Redis是内存数据库,如果不讲内存中的数据库状态保存到磁盘中,那么一旦服务器进程退出,服务器中的数据库状态也会丢失,所以需要持久化功能对内存中数据库状态进行持久化
- Redis持久化方式有两种:RDB和AOF
RDB(Redis DataBase)
-
在指定的时间间隔内将内存中的数据集块快照写入磁盘,也就是snapshot快照,恢复时是将快照文件直接读到内存中
-
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件
-
整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能
-
如果需要大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加地高效
-
RDB的缺点是最后一次持久化后的数据可能丢失
-
Redis默认持久化方式是RDB
-
持久化触发机制,产生一个dump.rdb文件
- save规则满足的情况下,会触发rdb持久化
- 执行flushall命令
- 退出redis
-
从rdb文件中恢复数据
- 只需要将rdb文件放入redis启动目录即可,redis启动的时候会自动检查dump.rdb文件并恢复其中的数据
- 查看需要存放rdb文件的位置
> config get dir > 1) "dir" > 2) "/usr/local/bin" # 如果这个目录存在rdb文件,启动redis就会自动恢复其中的数据
-
优点
- 只有一个文件 dump.rdb,方便持久化
- 容灾性好,一个文件可以保存到安全的磁盘
- 适合大规模的数据恢复,比AOF启动效率高
- 性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
-
缺点
- 数据安全性低,需要一定的时间间隔才会进行持久化操作,最后修改的数据可能因为以外宕机而丢失,所以这种方式更适合数据要求不严谨的时候
- fork进程的时候,会占用一定的内存空间
AOF(Append Only File)
-
简单地说就是,将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化日志文件中命令再执行一次,恢复数据
-
只允许追加文件不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,Redis重启的时候根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
-
AOF保存的文件是appendonly.aof文件
-
如果aof文件被破坏,可以使用
redis-check-aof
对aof文件进行修复> redis-check-aof --fix appendonly.aof
-
AOF持久化默认关闭,需要手动开启
-
持久化频率可配置如下
-
当两种方式(RDB、AOF)同时开启时,数据恢复Redis会优先选择AOF恢复
-
重写(rewrite)机制:以默认配置为例,如果aof文件大小超过64mb,redis会fork一个心得进程来将aof文件进行重写
-
优点
- 数据安全,aof 持久化可以配置
appendfsync
属性,有always
,每进行一次命令操作就记录到 aof 文件中一次 - 通过
append
模式写文件,即使中途服务器宕机,可以通过redis-check-aof
工具解决数据一致性问题 - AOF 机制的
rewrite
模式。AOF 文件没被 rewrite 之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的 flushall)
- 数据安全,aof 持久化可以配置
-
缺点
- AOF 文件比 RDB 文件大,且恢复速度慢
- 数据量增大时,AOF比RDB 启动效率低
RDB和AOF对比
- AOF文件比RDB更新频率高
- AOF比RDB更安全也更大
- RDB性能比AOF好
- 如果两种模式都开启了
- 优先使用AOF文件恢复数据。因为在通常情况下AOF文件保存的数据要比RDB文件保存的数据要完整
- RDB的数据不实时,而且redis重启也只会找AOF文件恢复数据,但是不建议只使用AOF持久化数据。因为RDB更适合用于备份数据库(AOF不断变化,不好备份),快速重启,而且不会有AOF可能潜在的bug
- 性能建议
- 因为RDB文件只用作后备用途,建议只在Slave上面持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条持久化规则
- 如果Enable AOF,好处是在最恶劣的情况下也只会丢失不超过两秒的数据,启动脚本较为简单只加载自己的AOF文件就可以了,代价:一是带来了持续的IO操作,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要磁盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64mb太小,可以设到5G以上,默认超过原大小100%大小重写可以改到适当的数值
- 如果不Enable AOF,仅靠Master-Slave Replication实现高可用性也可以,能省掉一大笔io操作,也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时挂掉,会丢失十几分钟的数据,启动脚本也要比较Master和Slave中的RDB文件,载入较新的那个,微博就是这种架构
Redis发布订阅
- Redis发布订阅(pub/sub)是一种消息通信模式:发布者(pub)发送消息,订阅者(sub)接收消息
- 微信、微博等包含关注、订阅的系统
- Redis客户端可以订阅任意数量的频道
- 发布/订阅消息图
- 三要素:消息发送者、频道、消息订阅者
- 三者关系:当有新消息通过PUBLISH命令发送给频道channel1时,这个新消息会被发送给订阅它的三个客户端
- 原理
- 通过SUBSCRIBE命令订阅某频道后,redis-server里维护一个字典,字典的键就是一个个channel,而字典的值则是一个链表,链表中保存了所有订阅这个channel的客户端。SUBSCRIBE命令的关键,就是将客户端添加到给定的channel的订阅链表中
- 通过PUBLISH命令向订阅者发送消息,redis-server会使用给定的频道作为键,在它所维护的channel字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者
- PUB/SUB从字面上理解就是发布(Publish)和订阅(Subscribe),在redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有定于它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能
- 稍微复杂的场景,使用消息中间件MQ
Redis主从复制
- 主从复制:是指将一台redis服务器的数据,复制到其他的redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。Master以写为主,Salve以读为主
- 默认情况下,每台redis服务器都是主节点;且一个主节点可以有多个从节点(或者没有从节点),但一个从节点只能有一个主节点
- 主从复制的作用主要包括:
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复,实际上是一种服务的冗余
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即些redis数据时应用连接主节点,读redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的的场景下,通过多个从节点分担读负载,可以大大提高redis服务器的并发量
- 高可用基石:除了上述作用外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制时redis高可用的基础
- 一般来说,要将redis运用于工程项目中,只使用一台redis是玩万万不能的,原因如下:
- 从结构上,单个redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大
- 从容量上,单个redis服务器内存容量有限,就算一台redis服务器内存容量为256G,也不能将所有内存用作redis存储内存,一般来说,单台redis最大使用内存不应该超过20G
- 电商网站上的商品,一般那都是因此上传,无数次浏览的,说专业点也就是写少读多。对于这种场景,可以使用如下架构
- 环境配置
- 默认情况下,每个redis服务都是主节点
- 只配置从库,不配置主库
- 复制3个配置文件(一主二从),然后修改对应的信息
- 端口
- pid名字
- log文件名
- dump.rdb名
- 启动服务(单机多服务)
> # 启动master > redis-server redis-master.conf > # 启动slave1 > redis-server redis-slave1.conf > # 启动slave2 > redis-server redis-slave2.conf
- 配置从机,设置主机
> # 连接从机1 > redis-cli -p 6380 > # 设置主机 > SLAVEOF 127.0.0.1:6379 > # 连接从机2 > redis-cli -p 6381 > # 设置主机 > SLAVEOF 127.0.0.1:6379
- 使用
info replication
命令查看主从信息 - 上述例子是通过命令配置主从关系,真实场景下,主从关系应该在配置文件中配置,形成永久的关系。如下图,在从机配置文件中,写死主机的ip和端口号,达到配置主机的目的
- 主机可以写,不能读,从机只能读,不能写。主机中所有信息和数据,都会自动被从机保存
- 测试:主机断开连接,从机依旧连接到主机,但是没有写操作。如果主机重连,从机依旧可以获得主机写的数据
- 如果使用命令配置的主从关系,如果从机重启后,从机会变回主机,原来主机写的数据不会同步到重启的从机。但只要将该重启的从机变为从机,立马就可以从主机获得数据
- 主机断开连接,从机可以使用
SLAVEOF no one
让自己变成主机 - 复制原理
- slave启动成功连接到master后会发送一个sync同步命令
- master接到命令后,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步
- 全量复制:slave服务在接收到数据库文件数据后,将其存盘并加载到内存中
- 增量复制:master继续将新的所有收集到的修改命令一次传到slave,完成同步
- 只要是重新连接master,一次完全同步(全量复制)将被自动执行
哨兵模式
- 主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费时费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。Redis从2.8开始正式提供了Sentinel(哨兵)架构来解决这个问题
- 哨兵是一种特殊的模式,首先redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待redis服务器相应,从而监控运行的多个redis实例
- 哨兵是 redis 集群机构中非常重要的一个组件,主要有以下功能:
- 集群监控:负责监控 redis master 和 slave 进程是否正常工作。
- 消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
- 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
- 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。
- 哨兵用于实现 redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。
- 故障转移时,判断一个 master node 是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题。
- 即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就很坑爹了。
- 哨兵的核心知识
- 哨兵至少需要 3 个实例,来保证自己的健壮性。
- 哨兵 + redis 主从的部署架构,是不保证数据零丢失的,只能保证 redis 集群的高可用性。
- 对于哨兵 + redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。
- 假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观认为主服务器不可用,这个现象称为主观下线,当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,哨兵之间会进行一次投票,投票的结果由一个哨兵发起,进行failover故障转移操作。切换成功后,就会通过发布-订阅模式,让每个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线
缓存异常
-
缓存雪崩
- 缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉
- 解决方案
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
- 一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。
- 给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。
-
缓存穿透
- 缓存穿透是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉
- 解决方案
- 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
- 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
- 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力
-
缓存击穿
- 缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库
- 解决方案
- 设置热点数据永远不过期
- 加互斥锁,互斥锁
-
缓存预热
- 缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
- 解决方案
- 直接写个缓存刷新页面,上线时手工操作一下
- 数据量不大,可以在项目启动的时候自动进行加载
- 定时刷新缓存;
-
缓存降级
- 当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
- 缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
- 在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
- 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
- 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
- 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
- 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
- 服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
-
热点数据和冷数据
- 热点数据,缓存才有价值
- 对于冷数据而言,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。频繁修改的数据,看情况考虑使用缓存
- 对于热点数据,比如我们的某IM产品,生日祝福模块,当天的寿星列表,缓存以后可能读取数十万次。再举个例子,某导航产品,我们将导航信息,缓存以后可能读取数百万次。
- 数据更新前至少读取两次,缓存才有意义。这个是最基本的策略,如果缓存还没有起作用就失效了,那就没有太大价值了。
- 那存不存在,修改频率很高,但是又不得不考虑缓存的场景呢?有!比如,这个读取接口对数据库的压力很大,但是又是热点数据,这个时候就需要考虑通过缓存手段,减少数据库的压力,比如我们的某助手产品的,点赞数,收藏数,分享数等是非常典型的热点数据,但是又不断变化,此时就需要将数据同步保存到Redis缓存,减少数据库压力。
-
缓存热点key
- 缓存中的一个Key(比如一个促销商品),在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
- 解决方案
- 对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询