Redis必备知识

Redis是一个内存型数据库,支持持久化、支持丰富的数据类型,使用单线程的多路IO复用模型(Redis6.0 引入了多线程IO),常用作分布式缓存。

Redis 基于 Reactor 模式开发了自己的网络事件处理器:这个处理器被称为文件事件处理器(file event handler)。文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并根据 套接字目前执行的任务来为套接字关联不同的事件处理器。

当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关 闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。

在这里插入图片描述

虽然文件事件处理器以单线程方式运行,但通过使用 I/O 多路复用程序来监听多个套接字,文件事件处理器既实现了高性能的网络通信模型,又可以很好地与 Redis 服务器中其他同样以单线程方式运行的模块进行对接,这保持了 Redis 内部单线程设计的简单性。

Redis的几种数据结构

String:在这里插入图片描述

在这里插入图片描述

简单动态字符串,不光可以保存文本数据还可以保存二进制数据,可以常数级获取字符串长度,Redis的String是字符串安全的,不会造成缓冲区溢出(当SDS API对SDS进行修改时,会先检查SDS的空间是否满足修改所需的要求,不满足会先扩展)。

一般用在:

计数场景:比如文章转发次数,用户访问次数和点赞量等等

分布式锁:setnx命令如果key不存在设值并返回1,如果key存在返回0,多个具有相同定时任务的机器,业务上只希望其中的一台执行定时任务,可以在定时任务的时间点触发时,多个服务竞争一个分布式锁,获得锁的执行任务,执行完通过del删除key释放锁。如果担心del失败,则可以通过expire给锁设置一个合理的自动过期时间,但是expire也可能失败。这时可以用JedisCommands接口提供的String set(String key, String value, String nxxx, String expx, long time)方法,这个方法可以将setnx和expire原子性地执行。

存储对象:可以将对象转换为string字符串存储在redis中,比如用户信息,商品信息。

缓存数据的处理流程
  1. 查询缓存,若存在直接返回,否则进入2
  2. 查询数据库,若存在,将数据写入缓存,并返回,否则进入3
  3. 返回空数据或者抛异常。

为什么要使用Redis做缓存?

  1. 高性能角度:缓存速度更快
  2. 高并发角度:可以将数据库中的一部分数据放在redis中,提高系统整体并发量。
list:在这里插入图片描述

用双向链表实现。常用场景为消息队列(但通常用更专业的RabbitMQ等),lrange可以分页查看队列的数据故可以用作排行榜。注意定时计算的排行榜适合用list,但是实时的的可以用zset。

hash

redis的字典类型由hash表实现,一个hash表里面可以用多个hash表节点,而每个hash表节点中就保存了字典中的一个键值对。

在这里插入图片描述

used记录hash表已有节点,sizemask = size - 1,这个值和key值一起决定某个entry放到哪个索引上去。通过拉链法解决hash冲突。

在这里插入图片描述

redis字典的结构如下:

在这里插入图片描述

可以看出一个字典里有两个hash表,字典只使用ht[0]哈希表,ht[1]hash表只会在对ht[0]rehash时使用。rehash可以使hash表的负载因子维持在一个合理的范围之内。redis采用渐进式rehash的方式,将rehash的计算工作均摊到对字典的每个添加、删除、查找和更新操作上。

应用场景:

购物车:

用户id 为key,商品id为field,商品数量为value

在这里插入图片描述

存储对象

商品的价格、销量、关注数、评价数等可能经常发生变化的属性,就适合存储在hash类型里。

在这里插入图片描述

set

也是基于字典实现。特点是元素无序且不重复。

使用场景:

存储好友/关注的人/粉丝的集合

​ a. sinter命令可以获得A和B两个用户的共同好友

b. sismember命令可以判断A是否是B的好友

c. scard命令可以获取好友数量

随机展示

通常,app首页的展示区域有限,但是又不能总是展示固定的内容,一种做法是先确定一批需要展示的内容,再从中随机获取。

set类型适合存放所有需要展示的内容,而srandmember命令则可以从中随机获取几个。

黑名单/白名单

经常有业务出于安全性方面的考虑,需要设置用户黑名单、ip黑名单、设备黑名单等,set类型适合存储这些黑名单数据,sismember命令可用于判断用户、ip、设备是否处于黑名单之中。

sorted set

和set相比,sorted set增加了一个权重,使得集合中的元素能够按照score进行排序。

由字典和跳表实现。跳表是排序的关键。

跳表是多层次的链表,平均查找和插入时间复杂度都是O(logn)。每一层链表中的元素是前一层链表中元素的子集。一开始算法在稀疏的层次进行搜索,直至需要查找的元素在该层两个相邻元素之间,这时跳到下一个层次。相比红黑树,查找区间内元素效率更高。

在这里插入图片描述

使用场景

需要对权重进行排序的场景。比如直播系统中礼物排行榜等。

bitmap

非常节省空间。

使用场景

适合需要保存状态信息(比如是否签到、是否登录…)并需要进一步对这些信息进行分析的场景。记录点赞的内容、统计活跃用户、用户在线状态等

Redis为什么不适用多线程?为什么6.0之后引入了多线程?

  1. 单线程编程维护简单
  2. 性能的瓶颈不在CPU,主要在内存和网络
  3. 多线程会存在线程死锁,上下文切换等问题,甚至会影响性能。
  4. 6.0之后主要是为了提高网络IO读写性能。

Redis是如何判断数据是否过期?

通过过期字典,key指向Redis数据库中的的某个健值,值是一个longlong的整数,这个值保存了key所指向的数据库的过期时间。

在这里插入图片描述

Redis的过期删除策略和内存淘汰策略

  1. 惰性删除 取key时才做过期检查 (cpu友好,内存不友好)
  2. 定期删除 每隔一段时间抽取一批key执行过期删除操作。
  3. redis是定期+惰性结合方式。
  4. 尽管由过期删除策略,但还是有可有key留在内存中,所以有内存淘汰策略。主要有:
    1. volatie-lru
    2. volatie-random
    3. volatile-ttl
    4. allkeys-lru
    5. allkeys-random
    6. volatile-lfu
    7. allkeys-lfu

Redis持久化机制

RDB

通过创建快照来保存redis在某个时间点上的副本

AOF(实时性更好)

开启AFO持久化后,每执行一条会更改Redis数据中的命令,Redis就会将该命令写在硬盘的AOF文件。Redis配置文件中可以配置三种不同的AFO持久化方式:

appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度

appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘

appendfsync no #让操作系统决定何时进行同步

为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec 选项。

Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。

如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。

缓存穿透

访问redis中不存在的数据,请求直接打到数据库,利用这个特点疯狂地访问请求不存在的数据,给数据库造成巨大的压力。可以利用参数校验,布隆过滤器,缓存无效key(无效key注意设置过期时间)来解决。

缓存雪崩

大面积key同一时间失效。可以使用redis集群,避免单机出现问题导致整个缓存服务都无法使用,合理地设置失效时间。

如何保证缓存和数据库的一致性

旁路缓存模式

写:先更新DB,然后直接删除cache

读:从cache中读取数据,读取到就直接返回,读不到则从数据库中读取数据返回,再把数据放到cache中。

可以先删除cache,再更新DB吗?

不行,比如请求1先写删除cache,请求2执行读数据,读到的是老数据且把数据更新到cache,请求1把DB数据更新造成数据不一致。

先更新DB后删除cache就没有问题了吗?

理论上还是有问题,不过概率非常小。缓存的写入速度比数据库的写入速度快很多。

请求1从DB读数据A->请求2写更新数据 A 到数据库并把删除cache中的A数据->请求1将数据A写入cache。

旁路缓存的缺陷

缺陷1:首次请求数据一定不在 cache 的问题

解决办法:可以将热点数据可以提前放入cache 中。

缺陷2:写操作比较频繁的话导致cache中的数据会被频繁被删除,这样会影响缓存命中率 。

解决办法:

  • 数据库和缓存数据强一致场景 :更新DB的时候同样更新cache,不过我们需要加一个锁/分布式锁来保证更新cache的时候不存在线程安全问题。
  • 可以短暂地允许数据库和缓存数据不一致的场景 :更新DB的时候同样更新cache,但是给缓存加一个比较短的过期时间,这样的话就可以保证即使数据不一致的话影响也比较小。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值