Redis核心考案 | 系统性学习 | 无知的我费曼笔记(图文排版无水印)

本文介绍了Redis的基础知识,如数据类型、使用场景、快速性能、与Memcached对比,以及持久化策略。重点讲解了Redis为何选择内存存储、不使用map/guava缓存的原因,以及如何解决缓存穿透和双写问题。
摘要由CSDN通过智能技术生成

无知的我正在复盘Redis。。。
笔记特点是

  • 重新整理了涉及资料的一些语言描述、排版而使用了自己更容易理解的描述。。
  • 提升了总结归纳性
  • 同样是回答了一些常见关键问题。。

Redis

Redis 是什么

是什么

  • 目前最受欢迎的NoSQL数据库之一
  • 使用ANSI C编写的开源、包含多种数据结构、支持网络、基于内存、可选持久性的键值对存储数据库

特点

  • 基于内存运行,所以性能高效(每秒可以处理超过10万次读写操作)
  • 支持分布式,理论上可以无限扩展
  • key-value 存储系统。
    • key 是字符串
    • value 可以是 字符串、列表、集合、散列表、有序集合等等
  • 开源地使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存持久化的日志型、key-value 数据库;提供多种语言的API

Redis 数据类型

数据类型 导读

image-20220503191843417

  • 基本数据结构 都是基于 String 的
  • bitMap、HyperLog 的底层是基于 String 的
  • GEO 是基于 zset 的

数据类型 String

是什么

  • 是 redis 最基础的数据结构
  • 键都是字符串类型
  • 其他集中基本数据结构都是在 String 的基础上构建的
  • 值实际上可以是字符串(编码方式为编为字节码;占用内存更多)、数字(编码方式是直接编为二进制)、二进制
    • 字符串包括 简单的、复杂的(如 JSON、XML)
    • 数字包括 整数、浮点数
    • 二进制包括 图片、音频、视频
  • 值最大不能超过512MB

如图

image-20220504110227213

使用场景

缓存功能

  • MySQL作为存储层绝大部分的数据都是从Redis这个缓存层获取的。
  • 缓存能够 加速读写、降低后端压力。这是因为Redis具有支撑高并发的特性

计数

  • 能够 快速计数、查询缓存
  • 可以 让数据异步落地到其他数据源

共享Session

  • 为了解决“分布式服务器负载均衡而使得用户每次访问Session都需要重新登陆”问题
  • 具体实现是 将用户的Session做集中管理,在保证Redis高可用、扩展性下,用户每次更新或者查询登录的信息都可以从 Redis 中集中获得

限速

  • 限制用户访问接口的频率

数据类型 List

是什么

  • 就是链表
  • 存储多个有序的字符串(如 a、b、c 从左到右组成了一个有序的列表)
  • 列表中的每个字符串被称为元素

image-20220518130300757

特点

  • 列表中的元素是有序的
  • 列表中的元素是可重复的

使用场景

消息队列

Redis的lpush + brpop命令组合可以实现阻塞队列

  • 生产者客户端使用lrpush从列表左侧插入元素
  • 多个消费者客户端使用brpop命令阻塞式的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡、高可用性

文章列表

实现其他数据结构

  • lpush + lpop = Stack
  • lpush + rpop = Queue
  • lpush + ltrim = Capped Colleciton(有限集合)
  • lpush + brpop = Message Queue(消息队列)

数据类型 has

是什么

类似于 JDK1.8 前的HashMap,内部实现也差不多(数组 + 链表)

使用场景

比较适宜存放对象类型的数据

举例说明——使用场景

image-20220518131513115

使用String类型,需要三条语句

缺陷是 键数过多,占用内存多,用户信息过于分散,不用于生产环境

对象序列化存入redis

序列化、反序列化有一定的开销。更新属性时需要userInfo全取出来进行反序列化,更新后再序列化到redis

使用hash类型

缺陷是 要控制内部编码格式,不适当的格式会消耗更多的内存

数据类型 set

是什么

类似于java中Hashset。是一种无序集合、没有先后顺序

image-20220518131946715

使用场景

提供了判断某个成员是否在set集合内的接口中(这个是list不能提供的)

可以基于set轻易实现交集、并集、差集的操作

应用在 标签

应用在 生成随机数进行抽奖活动、社交图谱等等

数据结构 Zset

是什么

保留了集合不能有重复成员的特性,不同的是 可以排序有序集、不像列表一样使用索引下标作为排序依据,而是为每个元素设置一个分数(score)作用排序的依据

特点是

元素不能重复,但是score能重复

提供了获取指定分数、元素范围查询、计算成员排名等功能

使用场景

排行版系统

数据结构 bitmap

是什么

“big”字符串由三个字符组成

分别对应ASCII码为 98、105、103

对应的二进制分别是01100010、01101001、011001111

本身不是一种数据结构,而是字符串。但可以对字符串的位进行操作

Redis使用bitmaps和使用字符串的方法不同。

可以把Bitmaps想象成一个以位为单位的数组,数组的每个单位只能存储0、1。数组的下标在Bitmaps中叫做偏移量

使用场景

需要保存状态信息(如是否签到、是否登录)并需要对其进行进一步分析的场景

构建布隆过滤器

Redis 使用原因

这是因为实现了高性能、高并发

image-20220518133208244 image-20220518133223834

image-20220518133242038

Redis 与Memcached

MemcachedRedis
数据类型单一。所有的值都是简单的字符串数据类型丰富
速度慢速度快
不可持久化数据可持久化数据

都是非关系型内存键值数据库

Redis 应用场景

计数器

对String进行自增自减运算

缓存

将热点数据放到内存中

会话缓存

统一存储多台应用服务器的会话信息

全页缓存(FPC)

除了基本的会话token之外,Redis还提供了很简便的FPC平台来加速加载曾访问过的页面

查找表

DNS记录很适合使用Redis来存储

消息队列

这是因为List是一个双向链表。可以通过lpush、rpop写入和读取信息

但是推荐使用RabbitMQ、Kafka等消息中间件

分布式锁实现——在分布式场景下,无法使用在单机环境下的锁来对多个节点上的进程进行同步

使用Redis自带的SETNX命令实现分布式锁

使用官方提供的 RedLock 分布式锁实现

Redis 快的原因

  • 完全基于内存。类似于HashMap的优势是查找和操作的时间复杂度都是O(1)
  • 数据结构简单,数据操作简单
  • 采用单线程。避免了不必要的上下文切换、竞争条件;不存在多线程、多进程导致的切换而消耗CPU;不存在加锁释放锁操作;不会出现死锁
  • 使用多路I/O复用模型,非阻塞IO
  • 使用的底层模型是Redis自己构建的 VM 机制。避免了系统调用系统函数会浪费一定时间去移动和请求的问题。

Redis 不用map/guava作缓存的原因

缓存分为本地的、分布式的缓存

Java自带的 map、guava 实现的是本地缓存

  • 生命周期随着 JVM 的销毁而结束
  • 缓存不具有一致性(主要原因)。比如 在多实例情况下,每个实例都需要各自保存一份数据
  • 适应场景是 快速、轻量

Redis、Memcached 是分布式缓存

  • 缓存具有一致性。比如 在多实例情况下,每个实例共用一份缓存数据
  • 虽然缺点是需要保持它们服务的高可用性、整个程序架构较为复杂

Redis 持久化机制

Redis 持久化是什么 防止当突然宕机时,Redis的数据因保存在内存而导致全部消失

Redis 持久化机制方式

  • RDB 快照
  • AOF 日志

Redis 数据库双写问题

为了确保数据是一致的方法

首先排除两种方法

  • 先更新缓存,再更新DB
  • 先更新DB,再更新缓存
  • 原因是 难以确保更新的内容是一致的,这是难以察觉的

原因是 存在并发问题导致更新出了脏数据。如图

image-20220503224821336

先删除缓存,再更新DB

image-20220503225107951

引出问题 并发问题导致的数据不一致问题

这是因为当进程A想要删除缓存而更新数据库、B同时想要查询数据时

  1. 进程A删除缓存的速度比进程B进度慢
  2. 进程B判断到缓存为空
  3. 去数据库查询
  4. 自动将数据库查询到的数据更新到原本的空缓存中
  5. 此时进程A更新数据库(没有更新缓存)
  6. =>这就意味者缓存中的是旧数据(就是脏数据)

解决办法是 使用“延时双删”策略

进程A在最后更新数据库时,等待其他进程执行完成后(比如先休眠进程A一秒),重新再次更新缓存

Redis 缓存穿透问题

定义及结构图

当用户发送访问 缓存中没有的数据 的请求时,这个请求会继续向数据库进行访问,如果有大量的这种请求,会导致数据库在短时间内因承受大量请求而崩掉。如下图

image-20220503230537933

解决方案

两种常规方案
  1. 在接口层添加校验。
  2. 在第一次接收到 访问缓存没有的、数据库也没有的数据 的请求时,将key-value对写成key-null来返回,那么在下一个接收到同样的请求时,可以直接从缓存将设定好的数据再次返回。(但是还要考虑到用户反复用同一个id暴力攻击的情况,解决办法是 将这种数据的缓存有效期设置短一些)
布隆过滤器

是什么

  • 本质上还是在接口层添加校验
  • 只不过校验的方式是通过一个二进制数组、Hash 算法组成的
  • 作用是 判断一个元素是否存在一个集合中
image-20220503232704992

实现机制

  • 将 所有可能存在的数据 哈希到 一个足够大的bitmap中
  • 在bitmap的值中 1 代表存在;0代表不存在
  • 一个一定不存在的数据一定会被bitmap拦截
  • //从而避免对底层存储系统的查询压力过大
image-20220503232922190

校验机制 举例说明

  • A、B、C数据形成了一个集合 并且 哈希到 了一个bitmap中
  • 当用户查询D、F数据时,将它们哈希到 bitmap 中,如果哈希到的bitmap的值为0,则返回false
  • 如下图
image-20220503233303555

引出问题 当访问哈希的bitmap值为1,无法确定其存在哪个集合中

解决办法是 作两次哈希。如下图

image-20220503233704772
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值