redis_详解


坚持学习,坚持写博客,努力向大佬前进! QAQ ………

redis 概述


在web应用发展初期,web站点访问量不高,与用户交予较少,因此关系型数据库受到广泛的应用。但是随着互联网的发展,web站点的访问量提升,使用关系型数据库(基于磁盘的读写操作)在性能上出现了I/O上的瓶颈。在一瞬间大量请求同时命中数据库时,复杂的I/O操作使得数据库很难承受这样高速读写的压力,此时必须有一个更高效的中间件,来缓解数据库的压力。

redis 是什么


为解决数据库在高并发情况下的压力,非关系型数据(NoSQL)也就进入我们的视野。Redis是现在最受欢迎的NoSQL数据库之一

redis 的自有数据结构


  • String
    • 它是一个二进制安全的字符串,不仅能够存储字符串、还能存储图片、视频等。单个最大512M
  • Hash
  • 该类型是由field和关联的value组成的map。其中,field和value都是字符串类型的。
  • List
    • 插入顺序排序的字符串元素集合,基于双向链表
  • Set
    • 无序集合,数据唯一,可用于去重
  • Zset
    • 有序集合类型,每个元素都会关联一个double类型的分数权值,通过这个权值来为集合中的成员进行从小到大的排序。
        // keyName 用作主键id
        // orderNum1 用作就是分数id 用于排序
        zadd keyName orderNum1  v1 orderNum1 v2 orderNum1 v3
      
      更多请查看redis中文网站 - 数据类型

redis 的使用场景


  • 缓存系统热点数据
    • 如:系统中频繁被请求的静态数据
  • 计数器
    • 如:基于redis的主键生成策略、分布式计数。
  • 排行榜,消息队列
    • 如:计算日排行、月排行等,可以通过zset类型添加。
    • 消息队列(订阅发布模式):
        发布:
        publish channel message
        例如:publish msg1 发送内容
        订阅:
        subscribe channel [.....]
        例如:subscribe msg1 
      
  • 分布式锁,共享session
    • 分布式锁:通过setnx实现
      setnx(key,value,time,'时间类型【时/分/秒/毫秒/微秒等】')

redis 的持久化


Redis持久化分为RDB持久化和AOF持久化

RDB持久化

RDB 其实就是在指定时间间隔中将内存中的数据集快照写入磁盘。这是redis 默认开启的持久化方式。

RDB 持久化过程
当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作:

  • Redis 调用forks. 同时拥有父进程和子进程。
  • 子进程将数据集写入到一个临时 RDB 文件中。
  • 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。

RDB的优点

  • rdb 是一个非常紧凑的单一文件,,非常适用于数据集的备份(比如每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据),这样即使出现问题也可以根据某时刻的备份恢复到之前的版本。
  • 由于rdb生成的文件时单一的,这也很方便用于同步数据(比如,集群情况下,某台redis主机宕机,可以通过rdb文件恢复)
  • rdb 同步时是从父线程fork一个子线程来完成这个操作,这意味着父线程不需要做其他额外的I/O操作,最大的发挥redis的性能。
  • 和AOF相比,在大数据集的保存上,RDB 有明显的性能优势。

*RDB的缺点

  • 由于RDB 保存的数据是某个时刻的快照,这意味着在保存快照操作开始到下次保存快照之间新增的数据集,是有可能丢失的,比如在下次保存之前意外断电或宕机。
  • 由于RDB是保存某一时刻的全量数据,即使是从父线程 fork 一个子线程,也难免消耗性能。

AOF持久化

AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.
如何开启AOF
在配置文件中打开AOF方式

appendonly yes

持久化策略

  • 每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全
  • 每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。
  • 从不 fsync :将数据交给操作系统来处理。更快,也更不安全的选择。
  • 推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。

*AOF持久化过程

  • Redis 执行 fork() ,同时拥有父进程和子进程。
  • 子进程开始将新 AOF 文件的内容写入到临时文件。
  • 对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。
  • 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。
  • Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。

AOF的优点

  • AOF 文件是一个只进行追加的日志文件,不需要写入seek,,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题.
  • Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写,整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
  • AOF 文件有序地保存了对数据库执行的所有写入操作
  • AOF 提供了多种同步策略:无fsync,每秒fsync,每次写的时候fsync。默认使用每秒同步。这样即使出现异常(如断电、宕机),也只丢失一秒的数据。
  • 通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.

*AOF的缺点

  • 相同的数据集,AOF文件的体积要比RDB更大。
  • 即使选择每秒同步,AOF的速度还是比RDB慢

在RDB和AOF同时开启的情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据

redis 在sprinBoot中的使用


依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置

spring.redis.cluster.max-redirects=3
spring.redis.cluster.nodes=127.0.0.1:6590,127.0.0.1:6591
spring.redis.password=Pass_123
spring.redis.timeout=5000

代码中的使用

@Resource
private StringRedisTemplate stringRedisTemplate;

ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
// 分布式锁
ops.setIfAbsent(msgId,msgId,1800L,TimeUnit.SECONDS)

redis 常见面试题

  1. redis为什么可以快速执行?
    • 绝大多数请求都是基于内存操作的
    • 采用单线程,避免不必要的上下文切换,不需要各种锁的性能消耗。
    • 非阻塞IO-IO多路复用(多网络连接,复用一个线程)
    • Redis采用自己实现的事件分离器
  2. 缓存雪崩
    • 缓存雪崩是指在某台缓存主机宕机时,所有请求直接访问数据库,导致数据库宕机的请况。
    • 解决方案:
      • 确保redis的高可用:多台 redis 主机同时工作,互为主备,及搭建 redis 集群。
      • 限流降级:只允许一个线程同时访问数据库(分布式锁),当一个线程访问时,其他线程等待。
      • 数据预热:在正式上线之前,先对可能大量访问的数据进行预先访问,使其存在缓存中,同时设置超时时间,使缓存失效时间尽量分布均匀
  3. 缓存穿透
    • 是直接访问一个 redis 中不存在的key,缓存没有命中,直接访问数据库。在这种请求下大量访问类似的key,也会出现数据库宕机的请况。
    • 解决方案:
      • 布隆过滤器:将数据库可以查询的key 以hash的类型缓存到 bitmap中,当一个一定不存在的key访问时,会被直接拦截
      • 缓存空对象:如果key值在缓存和数据库都没有命中的情况下,直接缓存一个空对象(需要设置超时时间),下次访问这个key就不会命中数据库。
  4. 缓存击穿
    • 指某个热点key,在高并发的情况下突然失效,并发直接请求数据库,导致数据库宕机的请求
    • 解决方案:
      • 互斥锁(mutex key):在出现热点key 失效时,通过redis的setnx(key,value,timeout)方法,实现单一线程进行读写操作。
        //设置一个缓存(存在超时时间)表示,目前有一个线程正在读写操作
        if (redis.setnx(key_mutex, 1, 3 * 60) == 1) {
        value = db.get(key);
        redis.set(key, value, expire_secs);
        redis.del(key_mutex);//热点key设置完成后,将之前key_mutex删除,防止下次击穿时,无法进行读取db
        } else { //这个时候代表同时期的线程进入等待,等待结束再次进行读取操作
        sleep(50);
        get(key); //重试
        } 
        
        ```java
    • 永远不过期:
      物理不过期:不设置超时时间,也就不存在过期问题。但是会出现数据不自动更新,无法保证一致性。
      异步不过期:设置超时时间,同时添加异步线程,发现热点key快过期的时候,重新设置热点key,但是会出现异步线程更新key时,其他线程访问的还是老数据。
      ```
  5. 过期机制
    • 在内存充足的情况下,redis 对已经过期的 key 进行清理
    • 常见的过期机制有三种:
      • 定时过期:设置对每个设置过期时间的 key 创建一个定时器,再key一到过期时间,就会立即删除。该策略对内存友好,但是对CPU消耗极大,影响redis的响应时间和吞吐量。
      • 惰性过期:访问 key 的时候,采取校验是否过期,如果过期则删除。该策略对CPU友好,但是对内存极不友好。
      • 定期过期:每隔一段时间,检查数据库 expires字典中的 key ,是否过期,过期则删除。
    • redis采用惰性过期和定期过期,优化内存和CPU的性能,使其达到平衡。
  6. 淘汰机制
    • 是指再redis 缓存内存不足时的,如何处理新入库的数据的解决方案。
    • 淘汰机制分6种:
      • noeviction:不删除策略,达到最大内存限制时,如果需要更多内存,直接返回错误信息。
      • allkeys-lru:所有key通用,优先删除最近最少使用的(less recently used,LRU)key.
      • allkeys-random:所有key通用,随机删除一部分key.
      • volatile-random:只限于设置了expire的部分,删除一部分expire的key.
      • volatile-ttl:只限于设置了expire的部分,优先删除剩余时间(time to live,TTL)短的key.
      • volatile-lru:只限于设置过期时间的数据集,优先删除最近最少使用的数据淘汰
        • 如何配置 淘汰策略:redis.conf 中maxmemory 设置最大内存,超出时,自动触发 maxmemory-policy 中配置的策略

文章作者: zhouxh-z
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 zhouxh-z !
 上一篇
Markdown 语法整理 Markdown 语法整理
Markdown 基本语法 最近写博文的时候,经常需要 google Markdown的语法,就觉得很麻烦,所以自己来整理一下,md常用的语法。 标题 最多支持6级标题,字体逐渐表小 # date ## date ### date ####
2020-10-21
下一篇 
Java集合_详解(HashMap源码剖析) Java集合_详解(HashMap源码剖析)
有生以来,第一次写自己的博客,希望自个坚持学习,坚持写博客,努力向大佬前进! QAQ ……… 废话不多说,直接开撸! 集合框架图 如上图所见,集合主要分为两类,Collection 和 Map,其中Collection又分为List、Set
2020-10-10
  目录