Redis

Redis


1、Redis使用

1)为什么要用redis?

redis是现在最受欢迎的NoSQL数据库之一,相比于传统的关系型数据库,具备高性能高并发、支持丰富的数据类型、基于内存的、也是单线程的,响应速度很快。

高性能:比如一个请求过来,在数据库中查询需要100ms,且这个数据在一段时间都不会变了,如果我们把它放到缓存中,通过key查出一个value,2ms就可以搞定,能大大提高性能。

高并发:如果有大量的请求同时去访问数据库,数据库很容易就会宕机,这个时候如果能把数据放到缓存中,那么redis一秒几万十几万的并发量是可以支撑下来的。

2)单线程的redis为什么这么快?

  • 纯内存操作
  • 单线程操作,避免了频繁的上下文切换
  • 采用了非阻塞I/O多路复用机制

3)reids 为什么是单线程的?

redis是基于一个文件事件处理器,这个文件事件处理器是单线程的,所以redis才叫单线程模型。

文件事件处理器:

  • 多个socket
  • IO多路复用程序
  • 文件事件分配器
  • 事件处理器

过程:I/O多路复用程序会监听多个socket,会将socket产生的事件放入队列中排队,每次以一个套接字的方式向文件时间分派器传送套接字。当上一个套接字产生的事件被处理完毕之后,I/O多路复用程序才会继续向文件事件分派器传送下一个套接字。



2、丰富的数据类型

1)String 字符串类型

是redis中最基本的数据类型,一个key对应一个value。

底层数据结构:简单动态字符串(SDS)

/*  
* 保存字符串对象的结构  
*/  
struct sdshdr {  
    
  // buf 中已占用空间的长度  
  int len;  
 
  // buf 中剩余可用空间的长度  
  int free;  
 
  // 数据空间  
  char buf[];  
};
  • SDS具有常数复杂度获取字符串长度
  • 杜绝了 缓存区的溢出
  • 减少了修改字符串长度时所需的 内存重分配次数
  • 二进制安全能存储各种类型的文件

实战场景:

  • 缓存: 做简单的 KV 缓存。经典使用场景,把常用信息,字符串,图片或者视频等信息放到redis中,redis作为缓存层,mysql做持久化层,降低mysql的读写压力。

  • 计数器:redis是单线程模型,一个命令执行完才会执行下一个,同时数据可以一步落地到其他的数据源。


2)Hash

是一个Mapmap,指值本身又是一种键值对结构,如 value={{field1,value1},......fieldN,valueN}}

底层数据结构:压缩表+哈希表

实战场景:

  • 更适合对象的存储 可以用来存储用户信息

  • 这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。


3)list

List 说白了就是链表(redis 使用双端链表实现的 List),是有序的value可以重复,可以通过下标取出对应的value值,左右两边都能进行插入和删除数据。

底层数据结构:双向循环链表+压缩表

实战场景:

  • 简单队列
  • 微博关注人时间轴列表

4)set

集合类型也是用来保存多个字符串的元素,但和列表不同的是集合中

  1. 不允许有重复的元素
  2. 2.集合中的元素是无序的,不能通过索引下标获取元素
  3. 3.支持集合间的操作,可以取多个集合取交集、并集、差集。

底层数据结构:哈希表+整数列表

实战场景:

  • 共同关注、共同爱好
  • 推荐好友

5)zset

有序集合和集合有着必然的联系,保留了集合不能有重复成员的特性,区别是,有序集合中的元素是可以排序的,它给每个元素设置一个分数,作为排序的依据。

底层数据结构:压缩表+跳表

实战场景:

  • 排行榜, 榜单可以按照用户关注数,更新时间,字数等打分,做排行。

6) Geospatial(地理位置) 两人之间的距离

7) Hyperloglog 基数统计

8) Bitmap 位存储、活跃不活跃、登录不登录、打卡不打卡



3、持久化方式

1) RDB(快照方式snapshotting)(全量持久化)

RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘, 实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

三种触发机制:

  • save触发方式(该服务会阻塞当前的Redis服务器,执行save命令期间,Redis不能处理其他命令,直到Rdb过程完成为止)
  • bgsave触发方式 ( redis后在后台异步进行快照操作,快照同时还可以响应客户端请求 )
  • 自动触发( 满足保存条件时将会把数据持久化到硬盘 )
    • save 900 1:表示 900 秒内如果至少有 1 个 key 值变化,则把数据持久化到硬盘;

优点:

  • 性能比较高
  • 生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作
  • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快

缺点:

  • 不能保证数据的完整性
  • 系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。

2) AOF(append-only-file)(增量持久化)

AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。

三种触发机制:

  • 每修改同步always:同步持久化 每次发生数据变更会被立即记录到磁盘 性能较差但数据完整性比较好

  • 每秒同步everysec:异步操作,每秒记录 如果一秒内宕机,有数据丢失

  • 不同步no:从不同步

优点:

  • 该机制可以带来更高的数据安全性,即数据持久性 (一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据 )
  • 写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容

缺点:

  • AOF文件通常要大于RDB文件,在数据恢复时要慢
  • 运行效率慢


4、过期回收策略和内存淘汰机制

1)过期回收策略(删除超过过期时间的Redis数据)

  • 定时过期:设置过期时间的key会创建一个定时器,到过期时间就会立即清除。

    • 优点: 可以立即清除过期的数据,对内存友好
    • 缺点: 占用大量的CPU资源处理过期的数据,影响Redis缓存的响应时间和吞吐量。
  • 惰性过期: 只有当访问一个key时,才会判断该key是否已过期,过期则清除。

    • 优点: 可以最大节省CPU资源。
    • 缺点: 可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
  • 定期过期:每隔一定的时间,会扫描一定数量的expires字典中的key,并清除其中已过期的key。

    • 前两者的一个折中方案

2)淘汰机制(当内存使用到达最大内存(maxmemory)上限时触发内存淘汰策略)

1、allkeys-lru:当内存大小不能容纳新写入数据时,在键空间中移除最近最少使用的键(key)。(默认)

2、allkeys-random:当内存大小不能容纳新写入数据时,在键空间中,随机移除某个键(key)。

3、volatile-lru: 当内存大小不能容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的键(key)。

4、volatile-random:当内存大小不能容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个键(key)。
5、volatile-ttl:当内存大小不能容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的键(key)优先移除。

6、noeviction:当内存大小不能容纳新写入数据时,新写入数据会报错。



5、主从复制机制

1)同步

全量同步:一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份

  • 从服务器连接主服务器

  • 主服务器生成RDB文件并使用缓冲区记录此后执行的所有写命令

  • 主服务器把快照文件发送给所有的从服务器

  • 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照

  • 主服务器发送完快照之后开始向服务器发送缓冲区中的写命令

  • 从服务器完成快照的载入之后,开始接收命令请求,并执行来自主服务器缓冲区的命令

增量同步: 主服务器发生的写操作同步到从服务器的过程

  • 主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。

主从同步策略:

主从刚刚连接的时候,就行全量同步,全同步结束后,进行增量同步。当然,如果有需要,slave在任何时候都可以发起全量同步。redis策略是,无论如何,首先会尝试进行增量同步,如果不成功,要求从机进行全量同步。


2)作用

  • 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。

  • 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务
    的冗余。

  • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。


3)哨兵

自动选举老大的模式, 如果故障了根据投票数自动将从库转换为主库



6、事务

  • 一组命令的集合! 一个事务中的所有命令都会被序列化,在事务执行过程的中,会按
    照顺序执行!
  • Redis事务没有没有隔离级别的概念!
  • Redis单条命令式保存原子性的,但是事务不保证原子性!
  • 事务中任意命令执行失败,其余的命令依然被执行。


7、常见四大问题

1) 缓存穿透

原因:查询没有的数据 ,redis中查不到,数据库中也查不到,本次查询失败。

解决方案 :

  • 布隆过滤器
    • 组成: 一个很长的二进制向量和一堆散列函数
    • 实现原理: 当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。
    • 实现原理:
    • 优点: 空间效率和查询时间都远远超过一般的算法
    • 缺点: 有一定的误识别率和删除困难
  • 缓存空对象
    • 当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源

2) 缓存击穿

原因: 热点数据过期, 当某个热点key在过期的瞬间,有大量的请求并发访问,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导致数据库瞬间压力过大

解决方案:

  • 设置热点数据永不过期, 不设置过期时间,热点key就不会产生过期问题
  • 加互斥锁,每个key同一时间只有一个线程去查询后端数据

3) 缓存雪崩

原因: 海量数据, 在某一时间段,缓存集中过期失效,Redis宕机!

解决方案:

  • redis 高可用,搭建集群,一台redis挂掉,其他redis顶上来
  • 限流降级, 在缓存失效后,通过加锁或者对来来控制读数据库写缓存的线程数量
  • 数据预热
    • 在正式部署前,把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。
    • 在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

4) 一致性问题

原因:数据库和缓存数据不一致

解决方案:

  • 延时双删的解决办法
    • 线程1删除缓存,然后去更新数据库
    • 线程2来读缓存,发现缓存已经删除,所以直接从数据库中读取,这个时候,由于线程1还没有更新完成,所以读到的是旧值,然后把旧值写入缓存
    • 线程1 根据估算时间, sleep 由于Sleep 的时间大于线程2 读数据+写缓存的时间,所以缓存再次被删除
    • 如果还有其他线程来读取缓存的话,就会再次从数据库中读取到最新值
  • 更新数据库产生的binlog订阅(使用canal)
    • 先更新数据库,成功后往消息队列发消息,消费到消息后再删除缓存,借助消息队列的重试机制来实现,达到最终一致性的效果。


posted @ 2021-07-15 22:38  随性0528  阅读(70)  评论(0)    收藏  举报