暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【数据库往事】Redis : 谁是NoSQL性能小霸王?

海内云联 2022-07-26
461

今天我们继续看肝帝的硬核文章(凤梨已开摆)。


Redis

Redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value(nosql)数据库

本文目录:

  1. redis的特点
  2. redis持久化方案
  3. redis事务
  4. 数据删除与淘汰策略
  5. redis分布式架构
  6. redis常见面试题

1.redis的特点

  1. 高性能(基于内存)最高可以支持10万/s级别的读写。

  2. 丰富的数据结构。(相应操作可以访问官网redis.cn查看)

    1. string
    2. hash
    3. list
    4. set
    5. zset
    6. bitmap,hyperloglog,geospatial(不常用)
  3. 单线程,避免了线程上下文切换带来的性能损耗,使用了IO多路复用机制,保证了在单线程情况下也能发挥较高的性能。

    • io多路复用指的是使用一个线程监听多个网络连接,防止io时阻塞导致redis性能下降,多路复用一般有select、poll、epoll三种模式,redis一般使用epoll模式

    • epoll:将用户的socket对应的fd(文件描述符)注册进epoll,红黑树管理文件描述符,双向链表存放就绪的描述符,内核态与用户态共享这部分内存,回调函数会把就绪的描述符放在就绪链表,然后通知用户态哪一个socket准备好了。(emmm这一块描述是我融合了不下于三篇CSDN的结果,想详细了解的话可以多看一看CSDN上的回答,用户态和内核态是操作系统的基础知识,不了解的可以先弄明白它们两个)

    • 多路复用机制:

  4. 持久化,支持RDB和AOF两种持久化方案,保证了在服务器宕机之后数据不会丢失。

  5. 稳定性,主从架构,哨兵,集群等架构保证了redis的高可用性。

  6. 丰富的机制,支持事务,过期时间,可以利用发布订阅者模式实现消息队列,可以实现分布式锁等等

下面让我们来围绕redis的特点来展开对redis的介绍

2.redis持久化方案

2.1 RDB(redis DataBase)

RDB的基本原理就是redis会根据配置在特定的时间对数据进行快照存储,RDB的特点就是文件格式紧凑,方便数据传输和数据恢复,在保存.rdb文件时redis会fork出一个子进程,由该进程完成具体持久化的工作,RDB的优点就在于在恢复打的数据集时速度更快,但是由于它并不是每时每刻都在记录,所以在发生宕机时会有一部分数据丢失。

//RDB配置
save [seconds] [changes]    

优点:

  1. 对性能影响最小。可以fork子进程进行数据快照存储
  2. 可以保存多个时间点的快照,作为灾难恢复的备份
  3. 数据恢复速度较快

缺点:

  1. 会丢数据
  2. 在数据较大时备份比较消耗性能

2.2 AOF(Append Of File)

AOF会记录每一次的写操作,类似于mysql中的bitlog,redis重启时会重放这些命令来恢复数据,redis还能对AOF进行重写,是的AOF不会过大

appendonly  yes     //开启(默认关闭)
appendfsync no   //不进行fsync,将flush文件的时机交给OS决定,速度最快
appendfsync always   //每写入一条日志就进行一次fsync操作,数据安全性最高,但速度最慢
appendfsync everysec //折中的做法,交由后台线程每秒fsync一次
auto-aof-rewrite-percentage 100  //rewrite间隔
auto-aof-rewrite-min-size 64mb   //达到这个大小时触发

优点:

  1. 数据安全,不会丢数据
  2. AOF文件易读,可修改

缺点:

  1. AOF文件通常比RDB文件更大
  2. 性能消耗比RDB大

最好在从机上把RDB和AOF都开启

3.redis事务

redis事务本质上就一次性执行一个客户端的多个命令,期间不会执行其他客户端的命令,所以redis的事务只满足一次性、顺序性和排他性。

redis的事务不保证原子性,没有隔离级别,无法回滚。

multi   //开启事务,redis会将后续的命令逐个放入队列中,然后使用EXEC命令来原子化执行这个命令队列
exec //执行事务中的所有操作命令
discard //取消事务,放弃执行事务块中的所有命令
watch //监视一个或多个key,如果事务在执行前,这个key(或多个key)被其他命令修改,则事务被中断,不会执行事务中的任何命令
unwatch //取消WATCH对所有key的监视

事务失败处理:

  1. 语法错误(编译器错误),直接导致事务中所有命令都不执行。
  2. 运行时错误(将一个string类型数据当成list类型操作),跳过该命令,执行其他命令。

4.数据删除与淘汰策略

4.1 删除策略

redis的数据删除指的是对针对过期之后的定时数据的删除

定时数据会被分配一块独立的存储空间,Hash结构,field是内存地址,value是过期时间,保存了所有key的过期描述,在最终进行过期处理的时候,对该空间的数据进行检测, 当时间到期之后通过field找到内存该地址处的数据,然后进行相关操作

  1. 定时删除

原理:创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作。

特点:快速释放不必要的内存占用,节约内存,cup压力较大,影响redis的性能。

  1. 惰性删除

原理:数据过期时不做任何操作,等访问的时候检查过期时间,如果过期的话就删除。

特点:cup压力小,但是内存压力大,过期数据长期占用内存。

  1. 定期删除

原理:是周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度

特点:内存定期随机清理,每秒花费固定的CPU资源维护内存,随机抽查,重点抽查。

  • Redis启动服务器初始化时,读取配置server.hz的值,默认为10

  • 每秒钟执行server.hz次serverCron()-------->databasesCron()--------->activeExpireCycle()

  • activeExpireCycle()对每个expires[*]逐一进行检测,每次执行耗时:250ms/server.hz(redis将存储空间分成很多快,每一块就是一个expires)

  • 对某个expires[*]检测时,随机挑选W个key检测

    • 如果key超时,删除key
    • 如果一轮中删除的key的数量>W*25%,循环该过程
    • 如果一轮中删除的key的数量≤W**25%,检查下一个expires[*],0-15循环
  • W取值=ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性值

  • 参数current_db用于记录activeExpireCycle() 进入哪个expires[*] 执行

  • 如果activeExpireCycle()执行时间到期,下次从current_db继续向下执行

4.2 淘汰策略(逐出策略)

相关配置:

maxmemory ?mb      //最大可用内存
maxmemory-samples count  //每次选取待删除数据的个数,采用随机获取数据的方式作为待检测删除数据
maxmemory-policy policy  //删除策略

相关的删除策略一共有三类八种

//第一类:检测易失数据(可能会过期的数据)
volatile-lru  //挑选最近最少使用的数据淘汰      least recently used
volatile-lfu  //挑选最近使用次数最少的数据淘汰   least frequently used
volatile-ttl  //挑选将要过期的数据淘汰
volatile-random  //任意选择数据淘汰
//第二类:检测所有数据
allkeys-lru   //挑选最近最少使用的数据淘汰
allkeLyRs-lfu  //挑选最近使用次数最少的数据淘汰
allkeys-random  //任意选择数据淘汰,相当于随机
//第三类:摆烂,不驱逐,引发OOM
no-enviction  

5.redis分布式架构

5.1 redis主从复制架构

redis的主从架构和哨兵机制主要为了实现redis的高可用,将数据复制多份分别存放在不同的机器上,比如下图所示的部署,主机器只负责接收写请求,执行写请求之后将数据同步至每一个从机上,从机只负责接收读请求。


redis的主从复制架构主要需要解决的问题就是主机与从机之间的数据同步问题

  • 建立连接阶段(即准备阶段)
  • 数据同步阶段
  • 命令传播阶段(反复同步)

5.2 redis哨兵机制

哨兵(sentinel) 是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master。哨兵也是一台redis服务器,只是不提供数据相关服务,通常哨兵的数量配置为单数

工作原理:

  • 监控
    • 获取各个sentinel的状态(是否在线)
    • 获取master的状态
    • 获取所有slave的状态(根据master中的slave信息)
  • 通知
    • sentinel在通知阶段要不断的去获取master/slave的信息,然后在各个sentinel之间进行共享
  • 故障转移
    • 淘汰不在线,响应慢,与原master断开连接长的slave
    • 每一个sentinel都向master发送信息,如果都收不到回复,就可以判断master宕机了
    • master宕机之后,sentinel会投票选出一个slave作为master
    • 向选举出的slave发送slaveof no one命令,向其他slave发送slaveof 新master端口

5.3 redis cluster集群

redis主从架构是为了实现高可用性,而集群是将多台机器连通起来,并提供统一的管理方式,使其对外呈现单机的服务效果,实现容量扩展

作用:
  • 分散单台服务器的访问压力,实现负载均衡
  • 分散单台服务器的存储压力,实现可扩展性
  • 降低单台服务器宕机带来的业务灾难
结构设计:
  1. 通过算法设计,计算出key应该保存的位置

  2. 将所有的存储空间计划切割成16384份,每台主机保存一部分

    注意:每份代表的是一个存储空间,不是一个key的保存空间

  3. 将key按照计算出的结果放到对应的存储空间

查找策略:
  • 各个数据库相互通信,保存各个库中槽的编号数据
  • 一次命中,直接返回
  • 一次未命中,告知具体位置

6.redis常见面试题

网上都说他们是常见面试题,但是我面试的时候面试官从来没问过。。。

6.1 缓存预热

用户访问数据之前,事先将用户可能经常访问的数据加载到redis中,可以在系统启动时加载或者定时加载。

6.2 缓存雪崩

同一时间大量定时数据失效,导致大量本应访问redis的请求直接访问数据库,导致数据库压力过大,严重时会直接导致数据库宕机,从而引发一系列连锁反应。

处理方法:

  • 请求加锁(慎用):对应并发量不高的业务,那我还用redis干嘛??哈哈哈哈
  • 设置随机的失效时间:在失效时间后加一个随机数,避免大量热点key同时失效。
  • lru和lfu切换

6.3 缓存击穿

可以理解为缓存雪崩的一个子集,它指的是某个热点key过期,导致大量请求直接访问数据库。

处理方法:

  • 预先设定,如双十一等活动,在预先知道访问量大的时间段的情况下,不要让对应热点key失效。
  • 慎用加锁,因为加锁会导致性能下降。

6.4 缓存穿透

大量数据库中不存在的请求或者恶意的攻击,导致数据库被频繁访问并且不生成缓存。

  • 布隆过滤器,将所有可能存在的数据映射到一个足够大的bitmap上,过滤访问不存在的数据的请求
  • cache null,缓存空数据,用户访问时直接返回空,但是这种缓存的过期时间不能太长,要保证产生相应数据时缓存能及时更新。
  • 过滤恶意请求

6.5 缓存降级

由于访问剧增导致服务出现问题时,优先保证核心业务能够正常运行,减少非核心业务对资源的使用。

  • 读降级

写请求增大时,只进行缓存的更新,然后将数据异步更新至数据库,保证最终一致性

  • 写降级

在数据库服务负载过高或出现故障时,可以只对缓存进行读取并将结果返回给用户,这种方式只适用于对数据实时性要求不高的场景下,保证在系统故障时用户也能访问数据,但是数据相对有延迟。

6.6 缓存更新

  • 定时更新
  • 过期更新
  • 读请求更新
  • 写请求更新

欢迎关注!让我们一起变得更强!

文章转载自海内云联,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论