概述

Redis优缺点

优点
  • 读写性能优异。
  • 支持数据持久化,支持AOF和RDB两种持久化方式。
  • 支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
  • 支持的数据结构丰富。
  • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
缺点
  • 数据库容量受到物理内存的限制。
  • Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
  • Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。

Redis为什么这么快

  1. 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。
  2. 数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的。
  3. 采用单线程,避免了不必要的上下文切换和竞争条件。
  4. 使用多路 I/O 复用模型,非阻塞 IO。

数据结构

Redis支持的数据类型

数据类型可以存储的值操作应用场景
String字符串、整数、浮点数对整个字符串或者其中一部分进行操作<br/>对整数和浮点数进行自增或者自减操作做简单的键值对缓存
List列表从两端压入或弹出元素<br/>对单个或多个元素进行修剪只保留一个范围内的元素存储一些列表型的数据结构,类似粉丝列表、文章的评论列表之类的数据
Hash包含键值对的无序散列表添加、获取、移除键值对<br/>获取所有键值对<br/>检查某个键是否存在存储结构化的数据,比如一个对象
Set无序集合检查一个元素是否存在于集合中<br/>计算交集、并集、差集<br/>从集合里面随机获取元素可以利用交集把两个人的粉丝列表整一个交集
Zset有序集合根据分值范围或者成员来获取元素<br/>计算一个键的排名去重但可以排序,如获取排名前几名的用户

三种特殊的数据类型:

  • Geospatial(地理空间)。该功能可以推算出地理位置信息,两地之间的距离。
  • HyperLogLog。是统计基数的利器,用于统计网站的 UV。
  • bitmap(位图)。通过最小的单位 bit 来进行 0 或者 1 的设置。常用于统计用户信息,比如活跃粉丝和不活跃粉丝、登录和未登录、是否打卡等。

zset跳表的数据结构

应用场景

String的应用场景

  • 商品编号、订单号采用INCR(递增)命令生成
  • 用INCR记录点赞数
  • 文章的阅读数,只要点击了地址,阅读数就加一。

hash的应用场景

相当于:Map<string, map>

  • 购物车早期

List的应用场景

  • 微信文章订阅公众号

    1. 公众号发布了文章分别是11和22。
    2. 我关注了他们,只要他们发布了新文章,就会安装进我的List:lpush likeauthor:uid9543 11 22
    3. 查看自己订阅的全部文章,类似分页,下面0~10就是一次性显示10条:lrange likeauthor:uid9543 0 10

Set的应用场景

  • 微信抽奖小程序

    • 一个用户点击参与按钮:SADD key 用户ID
    • 显示已经有多少人参与:SCARD key
    • 抽奖(从set中任意选取N个中奖人)

      • SPOP key 3,随机抽奖3人,元素会删除
      • SRANDMEMBER key 3,随机抽奖3人,元素不删除
  • 微信朋友圈点赞,并且显示所有点赞的用户
  • 好友的共同关注(交集)
  • 可能认识的人(差集)

Zset的应用场景

  • 根据商品销售对商品进行排序显示(分数是销量)
  • 抖音热搜

持久化

持久化就是把内存的数据写到磁盘中去,防止服务器因为宕机,导致内存数据丢失。Redis 支持 RDB(默认) 和 AOF 两种持久化机制,当下次重启时利用之前持久化的文件即可实现数据恢复。

RDB (Redis DataBase) 和 AOF (Append-Only File)

RDB:按照一定的时间将内存的数据以快照的方式保存到硬盘中,对应产生的数据文件为 dump.rdb,通过配置文件中的save参数来定义快照的周期。

AOF:只追加文件。将Redis执行的每次写命令记录到单独的日志文件中。

优缺点是什么?

  • AOF 每条数据都写入文件中,比 RDB 更安全。
  • 但是 AOF 文件比 RDB 文件大,且恢复速度慢。
  • RDB 性能比 AOF 好。RDB 使用 fork 子进程来完成写操作,让主进程继续处理命令,使 IO 最大化。

如果想数据的安全性更高,应该同时使用两种持久化功能。如果可以承受数分钟以内的数据丢失,可以只使用 RDB 持久化。

也不一定都只使用 AOP,使用 RDB 定时生成 RDB 快照(snapshot)非常便于进行数据库备份。

数据过期

Redis数据过期的删除策略

过期策略通常有以下三种:

定时删除
  • 含义:在设置 key 的过期时间的同时,为该 key 创建一个定时器,到了过期时间就会立即清除该 key。
  • 优点:保证内存尽快释放,对内存友好。
  • 缺点:会占用大量的 CPU 资源去处理过期的数据,性能影响严重。
惰性删除
  • 含义:key 过期的时候不删除,每次从数据库获取 key 的时候去检查是否过期,若过期,则删除,并返回 null。
  • 优点:可以最大化的节省 CPU 资源
  • 缺点:若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露(无用的垃圾占用了大量的内存)
定期删除
  • 含义:每隔一段时间执行一次删除过期 key 的操作。
  • 优点和缺点都是介于上面两者之间。
  • 可以设置删除的执行频率,值越大频率越快,对Redis性能损耗也更大。

    可以设置 maxmemory 最大值,当以用内存超过这个值时,就主动触发删除策略。

tips:Redis同时使用了惰性删除与定期删除。

集群方案

随着 Redis 使用场景越来越多,技术发展越来越完善,在 Redis 整体服务上的容错、扩容、稳
定各个方面都需要不断优化。

分布式:一个业务分拆多个子业务,部署在不同的服务器上
集群:同一个业务,部署在多个服务器上

集群是解决高可用的,而分布式是解决高性能、高并发的

主从模式

为了 Redis 服务避免单点故障,通常的做法是将 Redis 数据复制多个副本以部署在不同的服务器上。这样即使有一台服务器出现故障,其他服务器依然可以继续提供服务。

Redis 服务器分为两类:一类是主数据库(Master),另一类是从数据库(Slave)。

主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。

优点:① 一个主,可以有多个从,并以非阻塞的方式完成数据同步;② 分散主服务的压力,实现读写分离

缺点:① 不具备容错和恢复功能;② Redis 的主从复制采用全量复制,需要服务器有足够的空余内存

哨兵模式

Redis 提供的哨兵机制,哨兵的作用就是监控 Redis 系统的运行状况。基本原理是:心跳机制 + 投票裁决。哨兵模式主从可以切换,具备基本的故障转移能力。

它的功能包括以下两个:

  • 监控主数据库和从数据库是否正常运行。
  • 主数据库出现故障时自动将从数据库转换为主数据库。

心跳机制:在命令传播阶段,从服务器默认以每秒一次的频率,向主服务器发送命令。用于检测主服务器的网络连接状态。

投票裁决:如果主库故障了,根据投票数自动将从库转换为主库。

如何保证缓存与数据库双写时的数据一致性?

  • 读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。
  • 更新的时候,先删除缓存,再更新数据库。如果数据库更新失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致。

内存相关

Redis的内存淘汰策略

Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据。

redis为我们提供了以下 8 种内存淘汰策略:

  1. no-eviction(没有被驱逐):当内存使用超过配置的时候会返回错误,不会驱逐任何键。
  2. allkeys-lru:移除最久没有使用的键。
  3. volatile-lru:在设置了过期时间的键中,移除最久没有使用的键。
  4. allkeys-lfu:移除使用频率最少的键。
  5. volatile-lfu:在设置了过期时间的键中,移除使用频率最少的键。
  6. allkeys-random:随机删除。
  7. volatile-random:在设置了过期时间的键中,随机删除。
  8. volatile-ttl:在设置了过期时间的键中,优先删除马上过期的键。

Redis 的内存淘汰策略的选取并不会影响过期的 key 的处理。内存淘汰策略用于处理内存不足时的需要申请额外空间的数据,过期策略用于处理过期的缓存数据。

  • MySQL里有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据?
  1. Redis的内存淘汰策略。
  2. Redis的最大内存设置。

首先计算出20w数据所需的内存空间,设置最大内存,然后选择合适的内存淘汰策略。

Redis内存用完了会发生什么

如果达到设置的上限,Redis的写命令会返回错误信息,读命令可以正常返回。

缓存异常

缓存雪崩

指缓存同一时间大面积失效,导致后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。

解决方案
  1. 优化缓存过期时间。设置缓存数据的过期时间时,增加一个随机时间,防止同一时间大量数据过期。
  2. 保持缓存层的高可用性。使用Redis 哨兵模式或者Redis 集群部署方式,即便个别Redis 节点下线,整个缓存层依然可以使用。
  3. 使用互斥锁重建缓存。根据 key 去缓存层查询数据,当缓存层为命中时,对 key 加锁,然后从存储层查询数据,将数据写入缓存层,最后释放锁。

缓存穿透

指查询一个根本不存在的数据,缓存层和存储层都不会命中,导致每次请求都要到存储层去查询,造成数据库短时间内承受大量请求而崩掉。

解决方案
  1. 接口层增加校验。例如用户校验,把 id<0 的直接拦截。
  2. 缓存空对象。设为 key-null ,缓存有效时间可以设短些。
  3. 布隆过滤器拦截。在访问缓存层和存储层之前,将存在的 key 用布隆过滤器提前保存起来,做第一层拦截。

缓存击穿

指缓存中没有,但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,他们同时读缓存没有数据,又同时去数据库中取数据,导致数据库压力增大。

和缓存雪崩不同的是,缓存击穿指并发查同一条数据。而缓存雪崩是不同数据都过期了,很多数据查不到从而查数据库。

解决方案
  1. 设置热点数据永远不过期。
  2. 使用互斥锁重建缓存。

缓存预热

就是系统上线后,将相关的缓存数据直接加载到缓存系统。

缓存降级

就是缓存失效或者缓存服务挂掉的情况下,我们也不去访问数据库。我们直接访问内存部分数据缓存或者直接返回默认数据。

缓存降级的最终目的是保证核心服务可用,即使是有损的。

事务

Redis事务相关命令

  • MULTI 命令:用于开启一个事务,它总是返回OK。
  • EXEC 命令:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。
  • DISCARD 命令:清空事务队列,放弃执行事务,并从事务状态中退出。
  • WATCH 命令:是一个乐观锁,可以为 Redis 提供 CAS 行为。可以监控一个或多个键,一旦其中有一个键被修改或删除,之后的事务就不会执行。监控一直持续到 EXEC 命令。
  • UNWATCH 命令:取消 WATCH命令 对所有 key 的监控。

Redis事务特点

  • redis事务是一个队列中,一次性、顺序性、排他性的执行一系列命令

Redis 在事务失败时不进行回滚,而是继续执行余下的命令。(不支持原子性)

如果在一个事务中的命令编译时出现语法错误,那么所有的命令都不会执行。放入执行队列时就直接抛出异常。

如果在一个事务中出现运行错误,那么正确的命令会被执行。

RedLock

Redis 官方站提出了一种权威的基于 Redis 实现分布式锁的方式名叫 Redlock,此种方式比原先的单节点的方法更安全。

分布式问题

Redis实现分布式锁

Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对 Redis 的连接并不存在竞争关系,可以使用 SETNX 命令实现分布式锁。

SETNX key value        # key 不存在时,设置 key 的值。
  1. 使用 SETNX 命令获取锁,若返回 0(key已存在,锁已存在)则获取失败,反之获取成功。
  2. 为了防止获取锁后程序出现异常,导致其他线程/进程调用 SETNX 命令总是返回 0 而进入死锁状态,需要为该key 设置一个 “合理” 的过期时间
  3. 释放锁,则使用 DEL 命令将锁数据删除。

Redis的分布式锁的缺陷

Redis 高可用最常见的方案就是主从复制(master-slave),这种模式也给 redis 分布式锁挖了一坑。

Redis 集群环境下,假如现在 A 客户端想要加锁,他会根据路由规则选择一台 master 节点写入 key,在加锁成功后,master 节点会把 key 复制给对应的 slave 节点。

如果这个时候 master 节点宕机了,进行了主备切换。B 客户端在新的 master 节点上加锁成功,而 A 客户端也以为自己成功加了锁。

此时就会导致同一时间内,有多个客户端对一个分布式锁完成了加锁,导致各种脏数据的产生。


为什么算法这么难
16 声望3 粉丝

我想要清净一下~