谁来跟我聊7毛钱的分布式缓存——redis

在这里插入图片描述

一、Nosql简介

1.1 什么是Nosql?

Nosql不是具体的某一种数据库,而是一个概念,泛指非关系型数据库。主要的特点:支持高并发的读写、支持海量数据的读写、高可扩展、速度快。

1.2 Nosql与sql的区别?

在这里插入图片描述

二、redis概述

redis是当前比较热门的Nosql系统之一。它是一个开源的、使用C语言编写的key-value键值对存储系统(区别于MySQL的二维表格形式存储)。和Memcache类似,但很大程度补偿了Memcache的不足,Redis数据都是缓存在计算机内存中,不同的是,Memcache只能将数据缓存到内存中,无法自动定期写入硬盘,这就表示,一断电或重启,内存清空,数据丢失。

2.1 redis的特点

  1. 高效性
    Redis读取的速度是110000次/s,写的速度是81000次/s。因为数据存在内存中。单线程操作,避免了频繁的上下文切换。采用了非阻塞I/O多路复用机制。纯ANSI C编写。
  2. 原子性
    Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
  3. 支持多种数据结构
    (1)string(字符串)
    (2)list(列表)
    (3)hash(哈希)
    (4)set(集合)
    (5)zset(有序集合)
  4. 稳定性
    持久化,主从复制
  5. 其他特性
    支持过期时间,支持事务,消息订阅。

三、redis的数据类型

3.1 string

String是最简单的类型,一个key对应一个value,string类型是二进制安全的。

常用命令:

命令描述实例
SET key value设置指定 key 的值SET hello world
GET key获取指定 key 的值GET hello
MGET key1 [key2…]获取所有(一个或多个)给定 key 的值MGET hello world
MSET key value [key value …]同时设置一个或多个 key-value 对。MSET hello world hahaha wawawa
INCR key将 key 中储存的数字值增一INCR hahaha
INCRBY key increment将 key 所储存的值加上给定的增量值(increment)INCRBY hahaha 2

3.2 hash

Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)
在这里插入图片描述

3.3 list

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。
在这里插入图片描述

3.4 set

Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据,Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

在这里插入图片描述

3.5 zset

Redis有序集合和集合一样也是string类型元素的集合,且不允许重复的成员,它用来保存需要排序的数据,例如排行榜,一个班的语文成绩,一个公司的员工工资,一个论坛的帖子等。有序集合中,每个元素都带有score(权重),以此来对元素进行排序,它有三个元素:key、member和score。以语文成绩为例,key是考试名称(期中考试、期末考试等),member是学生名字,score是成绩。

在这里插入图片描述

3.6 对key的操作

在这里插入图片描述

四、redis的持久化

前面提过redis相对于memcache的优势之一就是可持久化,因为两个数据库都是基于内存的数据库,一旦进程退出,数据就会丢失,所以我们需要把数据写到磁盘上,当redis重启后可以从磁盘中恢复数据。
redis提供了两种方案将内存中的数据保存到磁盘上。一种是RDB,原理是将redis数据库中的全部数据记录定时dump到磁盘上;一种是AOF,原理是将redis的操作日志以追加方式写入文件。

4.1 RDB

Redis会定期保存数据快照至一个rbd文件中,并在启动时自动加载rdb文件,恢复之前保存的数据。可以在配置文件中配置Redis进行快照保存的时机:

save [seconds] [changes] //若在seconds发送了changes 次数数据修改,则进行一次RDB快照保存。

例:save 60 100
会让Redis每60秒检查一次数据变更情况,如果发生了100次或以上的数据变更,则进行RDB快照保存。可以配置多条save指令,让Redis执行多级的快照保存策略。Redis默认开启RDB快照。也可以通过SAVE或者BGSAVE命令手动触发RDB快照保存。SAVE 和 BGSAVE 两个命令都会调用 rdbSave 函数,但它们调用的方式各有不同:

  • SAVE 直接调用 rdbSave ,阻塞 Redis 主进程,直到保存完成为止。在主进程阻塞期间,服务器不能处理客户端的任何请求。
  • BGSAVE 则 fork 出一个子进程,子进程负责调用 rdbSave ,并在保存完成之后向主进程发送信号,通知保存已完成。 Redis 服务器在BGSAVE 执行期间仍然可以继续处理客户端的请求。

4.1.1RDB方案优点

  1. 每次快照会生成一个完整的数据快照文件,所以可以辅以其他手段保存多个时间点的快照(例如把每天0点的快照备份至其他存储媒介中),作为非常可靠的灾难恢复手段。
  2. 使用RDB文件进行数据恢复比使用AOF要快很多。

4.1.2 RDB方案缺点

1.快照是定期生成的,所以在Redis crash时或多或少会丢失一部分数据。
2. RDB 是内存数据的全量同步,数据量大会由于 I/O 而严重影响性能。

4.2 AOF

采用AOF持久方式时,Redis会把每一个写请求都记录在一个日志文件里。在Redis重启时,会把AOF文件中记录的所有写操作顺序执行一遍,确保数据恢复到最新。

开启AOF:

appendonly yes

AOF提供了三种fsync配置:always/everysec/no,通过配置项[appendfsync]指定:
1.appendfsync no:不进行fsync,将flush文件的时机交给OS决定,速度最快。
2.appendfsync always:每写入一条日志就进行一次fsync操作,数据安全性最高,但速度最慢。
3.appendfsync everysec:折中的做法,交由后台线程每秒fsync一次。

4.2.1 AOF rewrite

随着AOF不断地记录写操作日志,因为所有的写操作都会记录,所以必定会出现一些无用的日志。大量无用的日志会让AOF文件过大,也会让数据恢复的时间过长。不过Redis提供了AOF rewrite功能,可以重写AOF文件,只保留能够把数据恢复到最新状态的最小写操作集。

AOF rewrite可以通过BGREWRITEAOF命令触发,也可以配置Redis定期自动进行:

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

Redis在每次AOF rewrite时,会记录完成rewrite后的AOF日志大小,当AOF日志大小在该基础上增长了100%后,自动进行AOF rewrite。auto-aof-rewrite-min-size最开始的AOF文件必须要触发这个文件才触发,后面的每次重写就不会根据这个变量了。该变量仅初始化启动Redis有效。

4.2.2 AOF方案优点

1.最安全,在启用appendfsync为always时,任何已写入的数据都不会丢失,使用在启用appendfsync everysec也至多只会丢失1秒的数据
2.AOF文件在发生断电等问题时也不会损坏,即使出现了某条日志只写入了一半的情况,也可以使用redis-check-aof工具轻松修复
3.AOF文件易读,可修改,在进行某些错误的数据清除操作后,只要AOF文件没有rewrite,就可以把AOF文件备份出来,把错误的命令删除,然后恢复数据。

4.2.3 AOF方案缺点

1.AOF文件通常比RDB文件更大
2.性能消耗比RDB高
3.数据恢复速度比RDB慢

五、redis的事务

事务其实就是一组命令的集合。mysql的事务具有原子性、一致性、隔离性、永久性等特点。而在redis中事务具有以下特征:

  • redis事务没有隔离级别的概念
    redis是单线程的,批量操作会被放入队列缓存。
  • redis事务不保证原子性
    Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。

5.1 为什么Redis不支持事务回滚?

多数事务失败是由语法错误或者数据结构类型错误导致的,语法错误说明在命令入队前就进行检测的,而类型错误是在执行时检测的,Redis为提升性能而采用这种简单的事务,这是不同于关系型数据库的,特别要注意区分。Redis之所以保持这样简易的事务,完全是为了保证高并发下的核心问题——性能。

六、redis的过期策略

redis是键值对数据库,可以设置redis中缓存的key的过期时间。

6.1 定时过期(内存友好型)

每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。

6.2 惰性过期(CPU友好型)

只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。

6.3 定期过期(折中型)

每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

七、redis的淘汰策略

Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据。实际开发中最常用的就是使用LRU算法:移除最近最少使用的key。

八、redis的高可用

8.1 主从复制

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。

8.1.1 主从复制原理

1.当从数据库启动后,会向主数据库发送SYNC命令
2.主数据库接收到SYNC命令后开始在后台保存快照(RDB持久化),并将保存快照期间接收到的命令缓存再来
3.快照完成后,Redis(Master)将快照文件和所有缓存的命令发送给从数据库
4.Redis(Slave)接收到RDB和缓存命令时,会开始载入快照文件并执行接收到的缓存的命令
5.后续,每当主数据库接收到写命令时,就会将命令同步给从数据库。所以3和4只会在初始化的时候执行

8.2 哨兵模式

在这里插入图片描述
Redis 的 Sentinel 系统用于管理多个 Redis 服务器(instance), 该系统执行以下三个任务:

  • 监控:Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
  • 提醒:当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障转移: 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器(投票协议,过半选举), 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

参见博客:Redis学习三:Redis高可用之哨兵模式

8.3 redis集群

主从模式:进行读写分离,只有主节点可以写。若master宕机需要手动配置slave转为master.
哨兵模式:实现高可用,若master宕机可自动将slave转为master。但是不能动态扩充。
集群模式:实现动态扩充。
Redis Cluster是分布式架构,有多个节点,每个节点都负责进行数据读写操作,每个节点之间会进行通信。Redis Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。

在这里插入图片描述
1、所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽;
2、节点的fail是通过集群中超过半数的节点检测失效时才生效;
3、客户端与redis节点直连,不需要中间proxy层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可;
4、redis-cluster 把所有的物理节点映射到[0-16383]slot上(不一定是平均分配),cluster 负责维护node<->slot<->value;
5、Redis集群预分好16384个桶(Slot),当需要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) & 16384的值,决定将一个key放到哪个桶中;

在 Redis 的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。还有一个就是cluster,可以理解为是一个集群管理的插件。当我们的存取的 Key到达的时候,Redis 会根据 crc16的算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。
在这里插入图片描述

8.4 redis集群的优势

  • 缓存永不宕机:启动集群,永远让集群的一部分起作用。主节点失效了子节点能迅速改变角色成为主节点,整个集群的部分节点失败或者不可达的情况下能够继续处理命令;
  • 迅速恢复数据:持久化数据,能在宕机后迅速解决数据丢失的问题;
  • Redis可以使用所有机器的内存,变相扩展性能;
  • 使Redis的计算能力通过简单地增加服务器得到成倍提升,Redis的网络带宽也会随着计算机和网卡的增加而成倍增长;
  • Redis集群没有中心节点,不会因为某个节点成为整个集群的性能瓶颈;
  • 异步处理数据,实现快速读写;

九、redis常见问题

9.1 缓存穿透

当key对应的数据在数据源并不存在,从缓存中获取不到key对应的value,从而去数据源中去查询,频繁查询,有可能使数据源崩溃。

其实就是,数据源和缓存中都没有保存该Key,频繁查询数据源
解决缓存穿透的方案主要有两种:
  • 方案一:当查询不存在时,也将结果保存在缓存中。但是这可能会存在一种问题:大量没有查询结果的请求保存在缓存中,这时我们就可以将这些请求的key设置得更短一些;
  • 方案二:使用bloom filter。(推荐)

9.2 缓存击穿

在查询某个key时,该key在redis缓存中过期,大量并发查询该key,而此时还没来得及将数据回设到redis,此时并发请求可能会使数据源崩溃。

其实就是:查询key,key失效,大量并发查询该key,频繁查询数据源

解决方案:使用互斥锁。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db(查询数据库),而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,就是只让一个线程构建缓存,其他线程等待构建缓存的线程执行完,重新从缓存获取数据。

9.3 缓存雪崩

当服务器重启或者大量缓存在同一时间段同时失效,导致频繁查询数据源。

其实就是:大量缓存在同一时间段失效,导致频繁查询数据源。

解决方案:
(1)给缓存的失效时间加上一个随机值,避免集体失效。
(2)使用互斥锁,但是该方案吞吐量明显下降了
(3)双缓存。有两个缓存,缓存A和缓存B。缓存A的失效时间为20min,缓存B不设失效时间。自己做缓存预热操作。
①从缓存A中读数据,有则直接返回
②A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。
③更新线程同时更新缓存A和缓存B

9.4 redis单线程为什么还那么快?

1、Redis是纯内存数据库,一般都是简单的存取操作,线程占用的时间很多,时间的花费主要集中在IO上,所以读取速度快。
2、Redis使用的是非阻塞IO、IO多路复用,使用了单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件,减少了线程切换时上下文的切换和竞争。
3、Redis采用了单线程的模型,保证了每个操作的原子性,也减少了线程的上下文切换和竞争。
4、Redis避免了多线程的锁的消耗。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值