Redis

缓存知识考点

Redis_第1张图片

缓存中间件——Memcache和Redis的区别

Memcache:代码层次类似Hash
支持简单数据类型
不支持数据持久化存储
不支持主从
不支持分片
Redis
数据类型丰富
支持数据磁盘持久化存储
支持主从
支持分片
为什么Redis能这么快?
100000+QPS(QFS即query per second,每秒内查询次数)
完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高

Redis采用的是单进程、单线程的k-v结构的数据库,由C语言编写,它将数据储存在内存
中,读写均不会受到磁盘IO的影响,速度极快。
数据结构简单,对数据操作也简单
Redis不适用表,它的数据库不会预定义或者强制用户对Redis存储的不同数据进行关
联,因此性能相比关系型数据库要高出不止一个量级,其存储结构就是键值对,类似于
HashMap, HashMap的优势就是查找和操作时间复杂度都是O( 1 )的
采用单线程,单线程也能处理高并发请求,想多核也可以启动多实例
一般在面对高并发请求的时候,首先想到的是多个线程来处理,将IO线程和业务线程分
开,业务线程使用线程池,避免频繁创建和销毁线程,即便是一次请求阻塞了也不会影
响到其他请求。为什么Redis会反其道而为之,准确的来说,Redis的单线程结构是指其
主线程是单线程的,这里主线程包括IO事件处理以及IO对应相关请求的业务处理,此外
主线程还负责过期键的处理,负责协调集群协调等等。这些除了IO事件之外的逻辑,会
被封装成周期性的任务,由主线程周期性的处理,正因为采用单线程的处理,对于客户
端的所有读写请求,都由一个主线程串行处理,单线程只是在处理网络请求的时候只由
一个单线程来处理。一个正式的Redis Server在运行的时候肯定不止一个线程的。例如
Redis进行持久化的时候,会根据实际情况以子进程或子线程的方式执行。
使用多路I/O复用模型,非阻塞IO
Redis是跑在单线程中的,所有操作都是按照顺序线性执行的,但是由于读写操作,等待
用户输入或者输出都是阻塞的,所以IO操作在一般情况下往往不能直接返回,就是导致
某一文件的IO阻塞,就会导致某一文件的IO阻塞,导致进程无法对其他客户端提供服
务。而IO多路复用就是为了解决这个问题而出现的。
多路IO复用模型
FD:File Descriptor,文件描述符
在操作系统中,一个打开的文件通过唯一的描述符进行引用,该描述符是打开文件的元
数据到文件本身的映射。
传统的阻塞IO模型

多路IO复用模型

Select系统调用

Select在Linux系统中可同时监控多个文件描述符的可读可写的情况,当其中的某些文件
描述符可读或可写时,Select方法就会返回可读或可写的文件描述符个数。
Redis采用的IO多路复用函数:epoll/kqueue/evport/select?
这么多的多路复用函数,Redis采用哪个呢?
因地制宜

因为Redis要在多个平台上运行,同时为了最大化的提高执行效率和性能,会根据编译平
台的不同选择不同的IO多路复用函数作为子模块,提供给上层统一的接口。
优先选择时间复杂度为O( 1 )的IO多路复用函数作为底层实现
包括epoll或者kqueue等,上述的这些函数都使用了内核内部的结构,并且可服务几十万
的文件描述符,性能比select优秀。
以时间复杂度为O(n)的select作为保底
因为其在启动时会扫描全部监听的描述符。
基于react设计模型监听IO事件
实现文件事件处理器。文件事件处理器使用IO多路复用模块同时监听多个FD,当
select、read/write事件产生时,文件事件处理器就会回调FD绑定的
事件处理器,虽然整个文件处理器是在单线程上运行的,但是通过IO多路复用模块的引
用,实现了同时对多个FD的监控,提高了网络通信的性能,同时也保证整个Redis实现
服务的简单。

说说你用过的Redis的数据结构

供用户使用的数据类型

String:最基本的数据类型,二进制安全
String是最基本的数据类型,就是我们常用的k-v键值对,它的值最大能存储512M,其是
二进制安全的,意思是Redis的String可以包含任何数据,比如jpg图片或者序列化的对
象,这样我们的String就可以存储很多东西了。
接下来直接实操一下:
打开Redis Server

同时打开Redis-cli

保存一个键为name,值为redis的键值对,其类型为String类型

进行查看及修改

Redis的操作是原子性操作。原子性的意思就是一个事务是一个不可分割的最小工作单
位,事务中包括的诸多操作,要么都做,要么都不做,Redis的原子性使得我们不用考虑
并发问题,可以方便的利用原子性操作incr来实现简单的计数功能。例如有这样一个应用
场景,我们有一个web项目,想要记录用户每天访问该网站的次数,此时web应用只需要
通过凭借用户ID和代表当前时间的字符串作为key,每次用户访问的时候,对这个key执
行以下incr命令即可,如下即可实现记录访问次数的功能。

String之所以支持存储如此多的数据类型,离不开其底层的简单动态字符串sds

Hash:String元素组成的字典,适合用于存储对象
实操一下

我们经常将一些结构化的信息在客户端序列化后存储为一个字符串的值,成一般是json格
式,比如用户的姓名,年龄,性别等,此时使用Redis的Hash再合适不过了。
List:列表,按照String元素插入顺序排序
可以添加一个元素到列表的头或者尾。可以实现最新消息排行榜等功能(栈),越新插
入的消息,越能排前显示。

Set:String元素组成的无序集合,通过哈希表实现,不允许重复

利用Redis的Set结构可以存储一些集合性的数据,比如在微博应用中,将一个用户的所
有关注人存在一个集合中,将其所有粉丝存在一个集合,Redis提供了求交集、并集、差
集的操作指令,就可以非常方便的实现如同共同关注、共同喜好等功能,对上面的所有
结构操作,还可以使用不同的命令将结果返还给客户端存储到一个新的集合中,若想要
有序的话,就要使用到接下来的数据类型Sorted Set。
Sorted Set:通过分数来为集合中的成员进行从小到大的排序

Redis的Zset就是有序集合,和Set一样,也是String类型的集合,且不允许重复的成员,
那不同的是每个元素都会关联一个Double类型的参数,Redis正是根据这个分数对集合中
的成员对其进行从小到大的排序。ZSet的成员是唯一的,但分数即Score却可以重复。

用于计数的HyperLogLog,用于支持存储地理位置信息的Geo

从海量Key里面查询出某一固定前缀的Key

留意细节

摸清数据规模,即问清楚边界

使用Keys对线上的业务的影响

KEYS pattern:查找所有符合给定模式pattern的key
KEYS指令一次性返还所有匹配的key
键的数量过大会使服务卡顿
对于内存的消耗和Redis的服务器都是一个隐患。执行此命令的时候Redis会发生卡顿,
所需时间大概 80 多秒。

SCAN cursor[MATCH pattern][COUNT count]
SCAN每次执行只会返回少量元素,所以可以用于生产环境,而不会出现可能像KEYS命
令带来的可能会阻塞服务器的问题
基于游标(cursor)的迭代器,需要基于上一代的游标延续之前的迭代过程
以 0 作为游标开始一次新的迭代,直到命令返回游标 0 完成一次遍历
不保证每次执行都返回某个给定数量的元素,支持模糊查询
一次返回的数量不可控,只能是大概率符合count参数

开始迭代,返回前缀为k1的KEYS,并且希望一次返回 10 个,scan 0表示刚开始迭代。返
回的结果包括两部分,一部分是游标, 11534336 ;另一部分是KEYS,数量不一定是
10 ,这里返回了三个。执行该命令的时候,Redis并不会卡顿。
传入新的cursor,会返回后面的数据

我们发现,cursor的游标并不一定是递增的,也就是说有可能获取到重复KEY的问题,因
此需要在外部程序做一下去重,比如可以在JAVA程序写一个for循环,不断调用scan指
令,首先会获取到它的游标和结果,我们将结果存入到HashSet里面,那在下次循环中,
我们又给scan传入不同的cursor,又获取到不同的数据集,可以通过HashSet达到去重的
效果。由于分批次处理,scan整体花费的时间要比KEYS要长。
Java语言执行Redis使用Jedis。

如何通过Redis实现分布式锁?

分布式锁是控制分布式系统或不同系统之间共同访问共享资源间的一种锁的实现。如果

不同系统或同一系统的不同主机之间共享了某个资源时,往往需要互斥来防止彼此干

扰,进而保证一致性。

分布式锁需要解决的问题

互斥性

任意时刻只能有一个客户端获取锁,不能同时有两个客户端获取到锁。

安全性

锁只能被持有该锁的客户端删除,不能由其他客户端删除

死锁

获取锁的客户端因为某些原因而宕机,而未能释放锁,其他客户端再也不能获取到锁,

导致了死锁。此时需要一种机制来避免这种情况的发生。

容错

当一部分节点如Redis节点宕机的时候,客户端仍然能够获取锁并释放锁。
SETNX key value:如果key不存在,则创建并赋值

时间复杂度:O( 1 )

返回值:设置成功,返回 1 ;设置失败,返回 0

实操:

可以看到test并未被更改为task,正因为setnx有上述功能,并且操作是原子的,因此初期
被用来实现分布式锁。
如何解决SETNX长期有效的问题
EXPIRE key seconds

设置key的生存时间,当key过期时(生存时间为 0 ),会被自动删除

可以发现,两秒过期后,task执行成功。
缺点:原子性得不到满足

SET key value[EX seconds][PX milliseconds][NX|XX]

EX second:设置键的过期时间为second秒
PX millisecond:设置键的过期时间为millisecond毫秒
NX:只在键不存在时,才对键进行设置操作
XX:只在键已经存在时,才对键进行设置操作
SET操作成功完成时,返回OK,否则返回nil

可以看到, 10 秒之后可以执行成功。

大量的key同时过期的注意事项

集中过期,由于清除大量的key很耗时,会出现短暂的卡顿现象
解决方案
:在设置key的过期时间的时候,给每个key加上随机值,分散一些。

如何使用Redis做异步队列

使用List作为队列,RPUSH生产消息,LPOP消费消息

缺点:没有等待队列里有值就直接消费

弥补:可以通过在应用层引入Sleep机制去调试LPOP重试

BLPOP key [key…] timrout:阻塞直到队列有消息或者超时

缺点:只能提供一个消费者消费

那能否实现生产一次并让多个消费者消费呢?答案是肯定的

pub/sub:主题订阅者模式
发送者(pub)发送消息,订阅者(sub)接收消息
订阅者可以订阅任意数量的频道

缺点:消息的发布是无状态的,无法保证可达

要解决此问题,要使用专门的消息队列,如Kafuka等。

Redis如何做持久化?

RDB(快照)持久化:保存某个时间点的全量数据快照

进入redis的配置信息

RDB策略

save 900 1 表示 900 秒之内,如果有一条是写入指令,就触发产生一次快照,可以理解为
一次备份,
save 300 10 表示 300 秒以内,如果有 10 条写入,就会产生快照,但是如果变动数是 0 ,
还未达到 10 ,就会等 900 秒过后才会备份
save 60 10000 表示在 60 秒内如果有 10000 次写入,我们就进行备份
那为什么要配置这么多条规则呢?因为redis在不同的时段内读写请求是不均衡的,为了
平衡性能与数据安全,我们可以根据实际情况进行合理配置。
另外一条比较重要的指令

当设置成yes的时候,表示当备份进程出错,主进程就停止接收新的写入操作,这样是为
了保证持久化数据一致性的问题。

设置成yes,表示将文件压缩成rdb文件后再进行备份。此处建议设置为no,因为redis属
于CPU密集型服务器,再开启压缩,会带来更多的CPU消耗,相比硬盘成本,CPU更值
钱,

执行save “”,可以禁用rdb配置。

当前使用的持久化方式是rdb方式的。redis系统会定期将里面的全量数据保存,生成
dump.rdb文件,存放在这个地方。

打开dump.rdb文件查看

接下来看看RDB的创建和载入

SAVE:阻塞Redis的服务器进程,直到RDB文件被创建完毕
SAVE很少被使用,因为SAVE操作是在主线程中保存快照的,由于redis使用一个主线程
来处理所有请求的,这种方式会阻塞所有client请求。
BGSAVE:Fork出一个子进程来创建RDB文件,不阻塞服务器进程

自动化触发RDB持久化的方式

根据redis.com配置里的SAVE m n 定时触发(用的是BGSAVE)
主从复制时,主节点自动触发
执行Debug Reload
执行Shutdown且没有开启AOF持久化
BGSAVE的原理

系统调用fork():创建进程,实现了Copy-on-Write

RDB持久化缺点

内存数据的全量同步,数据量大会由于I/O而严重影响性能

可能会因为Redis挂掉而丢失从当前至最近一次快照期间的数据

如果应用不能丢失任何修改的话,可以采用另一种持久化方式——AOF

AOF(Append-Only-File)持久化:保存写状态

记录下除了查询以外的所有变更数据库状态的指令

与RDB持久化不同,是通过保存Redis服务器所执行的写状态来记录数据库的。RDB持久
化相当于备份数据库状态,而AOF持久化是备份数据库接收到的指令,所有被写入AOF
的指令都是以Redis协议格式来保存的。
以append的形式追加保存到AOF文件中(增量)
AOF持久化默认是关闭的,可以通过修改redis的配置文件进行修改。

将其改为yes即可生效

默认的配置策略。

日志重写解决了AOF文件大小不断增大的问题,原理如下:

调用fork(),创建一个子进程
子进程把新的AOF写到一个临时文件里,不依赖原来的AOF
主进程持续将新额变动同时写到内存和原来的AOF里
主进程获取子进程重写AOF的完成信号,往新的AOF同步增量变动
使用新的AOF文件替换掉旧的AOF文件

Redis数据的恢复

RDB和AOF文件共存情况下的恢复流程

RDB和AOF的优缺点

RDB优点:全量数据快照,文件小,恢复快

RDB缺点:无法保存最近一次快照之后的数据

AOF优点:可读性高,适合保存增量数据,数据不易丢失

AOF缺点:文件体积大,恢复时间长

Redis 4.0之后推出了RDB-AOF混合持久化方式
RDB-AOF混合持久化方式

BGSAVE做镜像全量持久化,AOF做增量持久化

使用Pipeline的好处

Pipeline和Linux的管道类似
Redis基于请求/响应模型,单个请求处理需要一一应答
Pipeline批量执行指令,节省多次IO往返的时间
有顺序依赖的指令建议分批发送

Redis的同步机制

Redis的集群原理

如何从海量数据中快速找到所需?

分片:按照某种规则去划分数据,分散存储在多个节点上

######常规的按照哈希划分无法实现节点的动态增减

一致性哈希算法:对2^32取模,将哈希值空间组织成虚拟的圆环

将数据key使用相同的函数Hash计算出哈希值

######通常将虚拟节点设置为 32

你可能感兴趣的:(Redis,java,redis)