Redis

文章是阅读笔记总结
参考:
硬核!16000 字 Redis 面试知识点总结,建议收藏!
为什么一定要有redis?
【原创】分布式之数据库和缓存双写一致性方案解析
Redis详解(五)------ redis的五大数据类型实现原理

1.Redis是什么?

  • 键值对内存数据库。
  • key是String类型,Value支持5种数据类型(String, List, HashMap, Set, ZSet)。底层数据类型(SDS, 双向链表,压缩列表list, HashMap, HashTable, 跳表,整数集合)。
  • 单线程,IO多路复用
  • 支持数据持久化
  • 主从复制,哨兵实现高可用
  • 不仅作为数据库,缓存,还可以作为分布式锁,消息中间件。

2.五种数据类型?

redisObject 对象示所有的 key 和 value。type 表示一个 value 对象具体是何种数据类型,即五种类型其中之一;encoding 是不同数据类型在 Redis 内部的存储方式。

  • String ,不仅是 String,也可以是数字。是二进制安全(不以空作为结束条件,而是长度)。jpg 图片或者序列化的对象,一些复杂的计数功能的缓存。

  • Hash键值(key-value)的集合。特别适合存储对象,单点登录存储用户信息。底层数据结构是压缩列表或HashMap。

  • List, 简单的字符串列表, Twitter 的关注列表,粉丝列表都可以用 List 结构来实现。底层数据结构是压缩列表或双向链表。

  • Set,String 类型的无序集合。底层数据结构是 hashtable。自动去重,求交集,差集。底层数据结构是整数数组或HashTable。

  • Zset,String 类型有序的集合,提供一个优先级(score)的参数来为成员排序。底层数据结构是HashMap 和跳跃表。HashMap 里放的是成员到 Score 的映射,跳跃表里存放的是所有的成员,排序依据是 HashMap 里存的 Score。HashMap实现O(1)的单值查询,跳跃表实现排序和log级别的范围查询。排行榜应用,取TOP N操作。

  • 五种数据类型的底层数据结构

  • String

    • int 编码是用来保存整数值,raw编码是用来保存长字符串,而embstr是用来保存短字符串。
      Redis_第1张图片
      raw编码的字符串对象
      在这里插入图片描述
      embstr编码创建的内存块结构
  • 列表对象

    • 编码可以是 ziplist(压缩列表,列表保存元素个数小于512个,每个元素长度小于64字节) 和 linkedlist(双端链表)
    • 创建一个 key = ‘numbers’,value = ‘1 three 5’ 的三个值的列表。
      Redis_第2张图片
      Redis_第3张图片
  • 哈写对象

    • 编码可以是 ziplist(压缩列表,列表保存元素个数小于512个,每个元素长度小于64字节)或者 hashtable。
      Redis_第4张图片
      Redis_第5张图片
  • 集合对象

    • 集合对象的编码可以是 intset (整数数组,集合对象中所有元素都是整数,集合对象所有元素数量不超过512)或者 hashtable。
      Redis_第6张图片
      Redis_第7张图片
  • 有序集合对象

    • 编码可以是 ziplist(保存的元素数量小于128, 保存的所有元素长度都小于64字节。) 或者 skiplist。
      Redis_第8张图片

3.redis和数据库双写一致性问题?

主要思想:有可能将读到的脏数据写入内存,将读到的脏数据删除掉。延迟双删,和更新再删缓存,以及消息队列补偿删除缓存失败,都是为了确保,将更新过程中,有可能读到并写入内存的脏数据删除,让下一次读请求时,到数据库读最新数据并种入内存。

采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。

分析过程:

  • 三种更新策略:

    • 1.先更新数据库,再更新缓存
    • 2.先删除缓存,再更新数据库
    • 3.先更新数据库,再删除缓存
  • 1.先更新数据库,再更新缓存

    • 同时有请求A和请求B进行更新操作,那么会出现
    • 线程A更新了数据库
    • 线程B更新了数据库
    • 线程B更新了缓存
    • 线程A更新了缓存
      这就出现请求A更新缓存应该比请求B更新缓存早才对,但是因为网络等原因,B却比A更早更新了缓存。这就导致了脏数据,因此不考虑。
  • 2.先删除缓存,再更新数据库

    • 请求A进行写操作,删除缓存
    • 请求B查询发现缓存不存在
    • 请求B去数据库查询得到旧值
    • 请求B将旧值写入缓存
    • 请求A将新值写入数据库

上述情况就会导致不一致的情形出现。而且,如果不采用给缓存设置过期时间策略,该数据永远都是脏数据。

如何解决呢?采用延时双删策略
(1)先淘汰缓存
(2)再写数据库(这两步和原来一样)
(3)休眠1秒,再次淘汰缓存
这么做,可以将1秒内所造成的缓存脏数据,再次删除。

这个1秒怎么确定的,具体该休眠多久呢?

  • 写数据的休眠时间则在读数据业务逻辑的耗时基础上,加几百ms即可。这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。

采用这种同步淘汰策略,吞吐量降低怎么办?

  • 那就将第二次删除作为异步的。自己起一个线程,异步删除。这样,写的请求就不用沉睡一段时间后了,再返回。这么做,加大吞吐量。

第二次删除,如果删除失败怎么办?

  • 第(3)种更新策略的解析。

  • 3.先更新数据库,再删除缓存

  • 假设这会有两个请求,一个请求A做查询操作,一个请求B做更新操作,那么会有如下情形产生

    • 1.缓存刚好失效
    • 2.请求A查询数据库,得一个旧值
    • 3.请求B将新值写入数据库
    • 4.请求B删除缓存
    • 5.请求A将查到的旧值写入缓存

如果发生上述情况,确实是会发生脏数据。然而,发生这种情况的概率又有多少呢?

步骤(3)的写数据库操作比步骤(2)的读数据库操作耗时更短,才有可能使得步骤(4)先于步骤(5)。数据库的读操作的速度远快于写操作的。因此步骤(3)耗时比步骤(2)更短,这一情形很难出现。

有强迫症,一定要解决怎么办?

  • 给缓存设有效时间是一种方案。其次,采用策略(2)里给出的异步延时删除策略,保证读请求完成以后,再进行删除操作。

还有其他造成不一致的原因么?

  • 如果删缓存失败了怎么办,那不是会有不一致的情况出现么。比如一个写数据请求,然后写入数据库了,删缓存失败了,这会就出现不一致的情况了。这也是缓存更新策略(2)里留下的最后一个疑问。

如何解决?

  • 提供一个保障的重试机制即可,这里给出两套方案。
  • 方案一:
    Redis_第9张图片
    (1)更新数据库数据;
    (2)缓存因为种种问题删除失败
    (3)将需要删除的key发送至消息队列
    (4)自己消费消息,获得需要删除的key
    (5)继续重试删除操作,直到成功

缺点:对业务线代码造成大量的侵入。

  • 方案二:
    Redis_第10张图片

(1)更新数据库数据
(2)数据库会将操作信息写入binlog日志当中
(3)订阅程序提取出所需要的数据以及key
(4)另起一段非业务代码,获得该信息
(5)尝试删除缓存操作,发现删除失败
(6)将这些信息发送至消息队列
(7)重新从消息队列中获得该数据,重试操作。

4.Redis过期删除策略,内存淘汰策略?

  • 定期随机删除过期数据 + 惰性删除过期数据,当访问到过期数据再删除。
  • 随机删没删除,也没访问到的过期数据怎么删除?
    • 那就当内存满的时候,淘汰数据换内存空间。
  • 淘汰策略?
    • 随机删除
    • 删除最近最少使用,推荐这种。
    • 在设置了过期时间的数据随机删除
    • 在设置了过期时间的数据中删除最近最少使用数据。

5.Redis 的持久化机制?

周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件中,以保证数据的持久化。

Redis 的持久化策略有两种:

  • RDB:快照形式是直接把内存中的数据保存到一个 二进制dump 的文件中,定时保存。
    • 当Redis需要持久化时,fork一个子进程将数据写到磁盘的临时文件中,然后将原来的RDB替换掉。
    • 适合备份,可以每小时备份或每一天备份
    • 适合灾难恢复,快速恢复到原来的数据
    • 但发生故障时,因为不是每秒备份,会丢失数据。
  • AOF:把所有的对 Redis 的服务器进行修改的命令都存到一个文件里,命令的集合。Redis 默认是快照 RDB 的持久化方式。
    • 每一个写命令都通过write函数追加到aof文件中
    • 优点:redis变得非常耐久,设置每秒Fsync一次或每次修改Fsync一次。
    • 缺点:体积大,根Fsync策略,AOF速度可能慢于RDB。

当 Redis 重启的时候,它会优先使用 AOF 文件来还原数据集,因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。

如果可以承受数分钟内的数据丢失,使用RDB持久。
AOF将Redis执行的每条命令追加到磁盘,巨大的写入会降低Redis性能。

6.主从复制

为解决单点问题,一般都需要对Redis配置从节点。使用哨兵监听主节点的存活状态,如果主节点挂掉,从节点能继续提供缓存功能。

Redis主从复制过程和原理?

从节点负责读操作,主节点负责写操作。因为读多写少,多个从节点。

  • 复制过程? 建立连接 + 数据同步 + 持续将写命令发送给从节点 -> 保持主从一致性

    • 从节点保存主节点信息,定时任务发现主节点信息,建立和主节点的socket连接
    • 从节点发送Ping信号,主节点返回Pong,两边能互相通信。
    • 建立连接后,主节点将所有数据同步给从节点。
    • 主节点把当前的数据同步给从节点后,就完成了复制过程。接下来,主节点会持续的把写命令发送给从节点,保持主从数据一致性。
  • 同步过程?主节点数据同步到从节点,另同步到缓冲区用于从节点数据丢失补救;主节点复制过程中另产生的写命令写入到缓冲区

    • 2.8之前使用sync命令,2.8以后使用psync命令
  • 因为主节点发送数据给从节点过程中,主节点还会进行一些写操作,这时候的数据存储在复制缓冲区。

  • 从节点同步完主节点数据后,主节点将缓冲区的数据发送给从节点,用于部分复制。

  • 主节点响应写命令时,不但会把命令发送给从节点,还会令复制积压到缓冲区,用于复制命令丢失的数据补救。

    • 第一次连接,全量复制
    • 部分复制
  • 全量复制和部分复制的过程?

  • 全量复制 从节点请求复制,主节点生成RDB发送给从节点,从节点加载到数据库中
    Redis_第11张图片

    • 从节点发送psync命令(?是第一次发送的runIdm, -1是第一次的offset)
    • 主节点发现从节点是第一次复制,返回FULLRESYNC{runId}{offset}, runId是主节点runId, offset是主节点目前offset。
    • 主节点开始生成RDB文件,发送给从节点。在从节点加载数据期间,主节点生成的写命令放在缓冲区,
    • 从节点清理自己的数据库数据,加载RDB文件,将数据保存在自己的数据库中。
  • 部分复制 针对Redis全量复制过高开销的优化,当从节点复制主节点时,出现网络断开,向主节点请求补发丢失的数据,主节点根据offset查找并发动缓冲区的数据。
    Redis_第12张图片

    • 部分复制是针对Redis全量复制的过高开销做出的优化。
    • 从节点正在复制主节点时,如果网络闪断或命令丢失,从节点会向主节点要求补发丢失的命令数据,发起psync,主节点根据offset在缓冲区中积压的数据中查找,发送在缓冲区的数据,不会发送全量数据。

哨兵

  • 主从复制会出现哪些问题?

    • 一旦主节点宕机,从节点晋升为主节点。需要修改应用方的主节点地址,和命令所有从节点去复制新的主节点,整个过程需要人工干预
    • 原生复制弊端在早期版本比较突出,如:Redis复制中断后,从节点发起psync,如果同步不成功,则会全量同步,主库执行全量备份的同时,可能会噪声毫秒级或秒级的卡顿。
      Redis_第13张图片
  • 解决方案

    • 哨兵
  • 哨兵的功能

    • 主节点存活检测,主从运行情况检测,自动故障转移,主从切换
    • 监控:不断检查主服务器和从服务器是否正常
    • 通知:当被监控的某个Redis服务器出问题,哨兵通过API脚本向管理员或者其他应用程序发出通知。
    • 自动故障转移:当主节点不能正常工作时,哨兵自动将其中一个从节点升级为主节点,并将其他的从节点指向新的主节点,避免人工干预。
    • 配置提供者:客户端应用初始化时连接的是哨兵节点集合,从中获取主节点信息。
  • 哨兵工作原理

    • 每秒一次的频率向所知的主节点,从节点,哨兵发送PING命令 —— 当其中一个实例回复时间超过指定值,标记为主观下线 —— 监视这个主服务器的所以哨兵都要以每秒一次的频率确认主服务的确进入下线状态,当有足够数量的哨兵也认为下线了,标记为客观下线 —— 哨兵和其他哨兵协商客观下线的主节点主节点状态,如果处于SDOWN状态,则投票自动选出新节点。

    • 每个哨兵以每秒一次的频率,向他所知的主节点,从节点,其他哨兵发送一个PING命令。

    • 如果某个实例距离最后一次有效回复PING命令时间超过down-after-milliseconds所指定的时间,这个实例被哨兵标记为主观下线。

    • 一旦一个主服务器被标记为主观下线,监视这个主服务器的所有哨兵都要以每秒一次的频率确认主服务器的确进入了主管下线状态。

    • 如果一个主服务器被标记为主管下线,并且有足够数量的哨兵(至少是配置文件制定的数量)在指定时间范围内也同意这一判断,那么该主服务器被标记为客观下线

    • 一般情况下,每个哨兵以每10秒一次的频率向他已知的所有主服务器和从服务器发送INFO命令。当一个主服务被标记为客观下线时,哨兵向下线主服务器个所有从服务器发送INFO命令,从每10秒一次改为每秒1次。

    • 哨兵和其他哨兵协商客观下线的主节点状态,如果处于SDOWN状态,则投票自动选出新的主节点,将剩余节点指向新的主节点进行数据复制。

    • 当没有足够数量的哨兵同一主服务器下线时,主服务器的客观下线状态就会被移除。当主服务器重新向哨兵的PING命令返回有效回复时,主服务器的主观下线状态就会被移除。




1. Redis是什么?

  • 高性能键值对的内存数据库,用作数据库、缓存、消息中间件等。是一种非关系型数据库。
  • 数据存在内存中,读写速度非常快,支持并发10W QPS。
  • 单进程单线程,线程安全,采用IO多路复用机制
  • 丰富的数据,字符串(strings),散列(hahes), 列表(lists), 集合(sets), 有序集合(sorted sets)。
  • 支持数据持久化。可以将内存中的数据保存在磁盘中,重启时加载。
  • 主从复制,哨兵,高可用
  • 用作分布式锁
  • 用作消息中间件,支持发布订阅

2. 五种数据类型及其应用场景

  • String,类比于Java中的Map

    • redis的string类型可以包含任何数据,如图片,序列化对象。
    • 应用场景:计数:redis是单线程的,不用考虑并发造成计数不准确问题。
      • 限制次数:如登录次数校验,错误超过三次5分钟内就不让登录了,每次登录。key自增一次,并设置该key的过期时间为5分钟后,每次登录检查一下该key值来进行限制登录。
  • Hash,类比于 Java里面的 Map>

    • string的key和value的映射表,Hash特别适合存储对象。
  • List,类比于Java中的Map>

    • String类型列表,按照插入顺序排序, 有序可重复。
    • 实现:双向链表,支持反向查找和遍历。
    • 应用场景: Twitter的关注列表,粉丝列表。
      • 栈,队列,有限集合,消息队列
  • Set,

    • String类型集合,无序不重复。
    • 实现:HashTable
    • 应用场景:自动去重,判断某个成员是否在一个set集合中。
      • 交集,方便求出多个用户的共同好友,共感兴趣领域。
  • ZSet

    • String类型集合,有序不重复。
    • 实现:HashMap和跳跃表(skipList)保证数据的存储和有序,HashMap存放成员到score的映射,跳表也放成员和score。
    • 应用场景:额外提供一个double类型的优先级(score)参数,为成员排序,并且是插入排序,即自动排序。

2.1 redis五种数据结构的六底层数据结构

2.1.1 简单动态字符串

  • Redis是用C写的,但Redis中的字符串不是C语言中的字符串(以空字符’\0’结尾的字符数组), 构建了名为简单动态字符串(simple dynamic string, SDS)的抽象类型,将SDS作为Redis的默认字符串表示。
struct sdshdr{
     //记录buf数组中已使用字节的数量
     //等于 SDS 保存字符串的长度
     int len;
     //记录 buf 数组中未使用字节的数量
     int free;
     //字节数组,用于保存字符串
     char buf[];
}

用SDS保存字符串“Redis”具体如下:
Redis_第14张图片

  • 为什么使用SDS,有什么好处?
    • 多一个len属性,常数时间获取字符串长度
    • 杜绝缓冲区溢出。C 语言中使用 strcat 函数来进行两个字符串的拼接,一旦没有分配足够长度的内存空间,就会造成缓冲区溢出。SDS 数据类型,在进行字符修改的时候,会首先根据记录的 len 属性检查内存空间是否满足需求,如果不满足,会进行相应的空间扩展,然后在进行修改操作,所以不会出现缓冲区溢出。
    • 减少修改字符串的内存重新分配次数。对于修改字符串SDS实现了空间预分配和惰性空间释放两种策略:
      • 空间预分配:对字符串进行空间扩展的时候,扩展的内存比实际需要的多,这样可以减少连续执行字符串增长操作所需的内存重分配次数。
      • 惰性空间释放:对字符串进行缩短操作时,程序不立即使用内存重新分配来回收缩短后多余的字节,而是使用 free 属性将这些字节的数量记录下来,等待后续使用。
      • 二进制安全: C字符串以空字符作为字符串结束的标识,而对于一些二进制文件(如图片等),内容可能包括空字符串,因此C字符串无法正确存取;所有 SDS 的API 都是以处理二进制的方式来处理 buf 里面的元素,不是以空字符串来判断是否结束,而是以 len 属性表示的长度来判断字符串是否结束。
      • 兼容部分 C 字符串函数
        Redis_第15张图片
        SDS 除了保存数据库中的字符串值以外,SDS 还可以作为缓冲区(buffer):包括 AOF 模块中的AOF缓冲区以及客户端状态中的输入缓冲区。后面在介绍Redis的持久化时会进行介绍。

问题:char[] char数组,怎么处理二进制数据,并用len判断结束?

2.1.2 链表

Redis自己构建,链表特性:

  • 双端
  • 无环
  • len属性
  • void*指针保存节点值,可以保存各种不同类型
    Redis_第16张图片

2.1.3 字典

Redis自己构建
Redis_第17张图片

  • 渐近式 rehash
    • 扩容和收缩操作不是一次性、集中式完成的,而是分多次、渐进式完成的。所以Redis采用渐进式 rehash,这样在进行渐进式rehash期间,字典的删除查找更新等操作可能会在两个哈希表上进行,第一个哈希表没有找到,就会去第二个哈希表上进行查找。但是进行 增加操作,一定是在新的哈希表上进行的。

2.1.4 跳跃表 skiplist

Redis_第18张图片
Redis_第19张图片

2.1.5 整数集合 intset

保存整数值的集合抽象数据类型,它可以保存类型为int16_t、int32_t 或者int64_t 的整数值,并且保证集合中不会出现重复元素。

  • 升级
    当我们新增的元素类型比原集合元素类型的长度要大时,需要对整数集合进行升级,才能将新元素放入整数集合中。
    • 根据新元素类型,扩展整数集合底层数组的大小,并为新元素分配空间。
    • 将底层数组现有的所有元素都转成与新元素相同类型的元素,并将转换后的元素放到正确的位置,放置过程中,维持整个元素顺序都是有序的。
    • 将新元素添加到整数集合中(保证有序)。

升级能极大地节省内存。

2.1.6 压缩列表

是Redis为了节省内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构,一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值

压缩列表的原理:压缩列表并不是对数据利用某种算法进行压缩,而是将数据按照一定规则编码在一块连续的内存区域,目的是节省内存。

压缩列表的每个节点构成如下:
在这里插入图片描述

总结

  • 大多数情况下,Redis使用简单字符串SDS作为字符串的表示。
  • Redis链表可以保存各种不同类型的值,除了用作列表键,还在发布与订阅、慢查询、监视器等方面发挥作用
  • Redis的字典底层使用哈希表实现,每个字典通常有两个哈希表,一个平时使用,另一个用于rehash时使用,使用链地址法解决哈希冲突。
  • 跳跃表通常是有序集合的底层实现之一,表中的节点按照分值大小进行排序。
  • 整数集合是集合键的底层实现之一,底层由数组构成,升级特性能尽可能的节省内存。
  • 压缩列表是Redis为节省内存而开发的顺序型数据结构,通常作为列表键和哈希键的底层实现之一。

简单字符串、链表、字典、跳跃表、整数集合、压缩列表等数据结构就是Redis底层的一些数据结构

redis的五大数据类型实现原理

  • 列表(list)

    • 数据量比较小时,压缩列表,内存连续,允许存储的数据大小不一样,相比数据大小一样的数组节省空间,且可以存储多种数据类型。(因为大小不一样,无法通过寻址,时间复杂度O(1)下随机访问,list中的value值不需要随机查找,而是通过key查找)
    • 数据个数大于512,双向链表。
  • 字典(hash)

    • 数据较小时,压缩列表
    • 键值对个数大于512个,散列表 。动态扩容(装载因子大于1),缩容(装载因子小于0.1)。渐进式扩容,缩容,分批搬迁,避免一次性大量数据搬迁,导致服务暂停。
  • 集合(set)

    • 数据量较小时,且都是整数,有序数组。
    • 元素个数大于512个,散列表。
  • 有序集合(sortedset)

    • 数据带上一个得分。
    • 数据较小时,压缩列表
    • 数据个数大于126,且单个数据小于64 ,hashMap + 跳表。快速增,删,查,改。快速按照得分值,得分区间获取数据。

字典的键保存元素的值,字典的值保存元素的分值;跳跃表节点的Object属性保存元素的值,跳跃表节点的score属性保存元素的分值。

这两种数据结构会通过指针来共享相同元素的成员和分值,避免造成内存的浪费。

说明:其实有序集合单独用字典或跳表都可以实现。比如单独用字典:O(1)时间查找成员分值,但字典是以无序的方式保存集合元素,所以每次进行范围操作时,都需要排序;单独使用跳跃表,能进行范围查找,但查找操作由原来的O(1)变为O(logN)。

- 五大数据结构应用场景总结:
- string: 二进制安全(不以空格做结尾,以长度作结尾) ,存储图片,视频。由于Redis高性能读写,string类型的value也可以是数字,用作计数器,分布式统计在线人数,秒杀等。
- hash, 单点登录存放用户信息。
- list, 简单的消息队列
- set, 底层map, 查找快,不允许重复,全局去重,如判断用户是否登录,是否注册。交集,并集,差集,计算共同爱好,自己独有的喜好。
- zset, 有序,范围查找,排行榜,TopN等操作。

内存回收与内存共享

内存回收

  • Redis自己构建了一个内存回收机制,redisObject 结构中的 refcount 属性实现。创建一个新对象,属性 refcount 初始化为1对象被一个新程序使用,属性 refcount 加 1。对象不再被一个程序使用,属性 refcount 减 1。当对象的引用计数值变为 0 时,对象所占用的内存就会被释放。
  • 不能克服循环引用。
  • 解决:当内存使用达到最大值时,redis使用的清楚策略。

内存共享
refcount 属性除了能实现内存回收以外,还能用于内存共享。

  • 1、将数据库键的值指针指向一个现有值的对象

  • 2、将被共享的值对象引用refcount 加 1

    注意:Redis的共享对象目前只支持整数值的字符串对象。之所以如此,实际上是对内存和CPU(时间)的平衡:共享对象虽然会降低内存消耗,但是判断两个对象是否相等却需要消耗额外的时间。对于整数值,判断操作复杂度为O(1);对于普通字符串,判断复杂度为O(n);而对于哈希、列表、集合和有序集合,判断的复杂度为O(n^2)。

虽然共享对象只能是整数值的字符串对象,但是5种类型都可能使用共享对象(如哈希、列表等的元素可以使用)。?

总结:
redisObject 结构:
type :对象的类型,五大数据类型
encoding
ptr
refcount

3.缓存问题

3.1 缓存和数据库一致性问题

因为读多写少,而数据库资源又很宝贵,没有办法一味的添加从库,一味增加分库分表来解决,不断分库分表,数据迁移风险高。

添加缓存解决读压力。但又带来缓存与数据库一致性问题。此时要看自己达到的目标是什么?不同的方案解决的问题是不一样的。

你的业务和目标是怎样的呢?

  • 要求最终一致性?还是强一致性?
  • 对缓存要求多高?延迟1ms行不行?延迟1min行不行?
  • 当前是单库单表?多库多表?
  • 缓存数据结构是什么?是否存在多表合并?

3.2 缓存雪崩,击穿,穿透问题

4. Redis为什么这里快

5. Redis和Memcached的区别

6. 持久化

7. 主从复制

8. 哨兵

你可能感兴趣的:(redis)