Redis详解(二)

3 篇文章 0 订阅
2 篇文章 0 订阅

Redis的单线程模型

Redis基于Reactor模式来设计开发了自己的一套高效的时间处理模型。

Redis内部使用文件事件处理器file event handler,这个文件事件处理器是单线程的,所以Redis才叫做单线程的模型。它采用IO多路复用机制同时监听多个socket,将产生事件的socket压入内存队列中,事件分派器根据socket上的事件类型来选择对应的事件处理器进行处理。

文件事件处理器的结构包含4个部分

  • 多个socket
  • IO多路复用程序
  • 文件事件分派器
  • 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)

多个socket可能会并发产生不同的操作,每个操作对应不同的文件事件,但是IO多路复用程序会监听多个socket,会将产生事件的socket放入队列中排队,事件分派器每次从队列中取出一个socket,根据socket的事件类型交给对应的事件处理器进行处理。主要是以空间换时间
在这里插入图片描述
因此,Redis快的原因主要是基于非阻塞的IO多路复用机制。单线程避免了多线程的频繁上下文切换,预防了多线程可能产生竞争问题。Redis单机并发 10w+。

Redis数据过期

Redis通过一个叫做过期字典(可以看做是hash表)来保存数据过期的时间。键是指向Redis数据库的某个key,值是一个long long类型的整数,这个整数保存了key所指向的数据库键的过期时间

typedef struct redisDb {
    ···
    dict *dict; // 数据库键空间,保存着数据库中所有键值对
    dict *expires; // 过期字典,保存着键的过期时间
    ···
} redisDb;
删除策略

常用的过期数据删除策略有两个:

  1. 定期删除:每隔一段时间随机抽取一批key执行删除过期的key操作。对内存友好。
    假设Redis里放了10w个key,都设置了过期时间,你每隔几百毫秒,就检查10w个key,那Redis基本就死了,CPU负载很高的,消耗在你的检查过期key上了。注意,这里可不是每隔100ms就遍历所有的设置过期时间的key,那样就是一场性能上的灾难。实际上Redis是每隔100ms随机抽取一些key来检查和删除的。但是问题是,定期删除可能会导致很多过期key到了时间并没有被删除掉。这时候就是惰性删除了。

  2. 惰性删除:只会在取出key的时候才对数据进行过期检查。对象CPU友好,但可能造成太多的key没有被删除。
    在你获取某个key的时候,Redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会返回任何东西。

Redis采用的是定期删除+惰性删除,但还是存在漏掉了很多过期key的情况。会导致Out of memory了。为了解决这个问题,Redis引进了Redis内存淘汰机制

内存淘汰机制

Q:MySQL里有2000W数据,Redis中只存20W的数据,如何保证Redis中的数据都是热点数据?

Redis提供6种数据淘汰策略:

  1. volatile-lru(least recently used):从已设置过期时间的键空间中挑选最近最少使用的数据淘汰。
  2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰。
  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰。
  4. allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
  5. allkeys-random:内存不足以容纳新写入数据时,在键空间中随机移除某个key。
  6. no-eviction:禁止驱逐数据,也就是当内存不足以容纳新写入数据时,新写入操作会报错。(不会使用)

4.0版本后增加以下两种:

  1. volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰。
  2. allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key。

一般就用 volatile-lru:就行了,淘汰最近最少使用的key

手写LRU

继承LinkedHashMap可以简易实现LRU

public class LRUCacheLinkedHashMap<K, V> extends LinkedHashMap<K, V> {

    private final int capacity;

    LRUCacheLinkedHashMap(int capacity) {
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > capacity;
    }

    public static void main(String[] args) {
        LRUCacheLinkedHashMap<String, String> cache = new LRUCacheLinkedHashMap<>(5);

        cache.put("1", "a");
        cache.put("2", "b");
        cache.put("3", "c");
        cache.put("4", "d");
        cache.put("5", "e");

        printCache(cache);

        System.out.println("插入第6个元素后的顺序:");
        cache.put("6", "f");
        printCache(cache);

        System.out.println("访问第3个元素后的顺序:");
        cache.get("3");
        printCache(cache);
    }


    private static void printCache(LRUCacheLinkedHashMap<String, String> map) {
        map.forEach((key, value) -> {
            System.out.print(String.format("%s ", key));
        });
        System.out.println();
    }
}
持久化

Redis相对比于Memcached支持持久化,而且支持两种不同的持久化操作,快照(Redis DataBase,RDB)和只追加文件(Append-Only File,AOF)

快照持久化(RDB)

RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。默认的持久化方式。这种方式就是将内存中数据以快照的方式写入到二进制文件中,默认的文件的名字dump.rdb

快照文件默认存放在安装的根目录下,dir ./
在这里插入图片描述
可以修改配置文件redis.config dir D:/redis/rdb

触发方式有三种:

  1. save命令触发
    该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止,执行完成时候如果存在老的RDB文件,就把新的RDB文件替换掉旧的RDB文件。

  2. bgsave触发方式
    执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体操作是Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。基本上Redis内存所有的RDB操作都是采用bgsave命令。

  3. 自动触发
    在配置文件redis.conf中配置

    save 900 1    #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
    
    save 300 10   #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
    
    save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
    

    RDB的一些配置参数:

    • save:这里是用来配置触发Redis的RDB持久化条件,不需要持久化,那么你可以注释掉所有的save行来停用保存功能。

    • stop-writes-on-bgsave-error:默认值为yes。当RDB后台保存数据失败,Redis会拒绝新的写入。

    • rdbcompression:默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储

    • dbfilename:设置快照的文件名,默认是dump.db

    • rdbchecksum:默认值是yes。在存储快照后,我们还可以让Redis使用CRC64算法来进行数据校验,但是这样会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。

    • dir:设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。

RDB优缺点

优点:

  1. RDB文件紧凑,全量备份,适合用于备份和灾难恢复
  2. 生成RDB文件的时候,Redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作
  3. RDB在恢复大数据集时的速度比AOF的恢复速度快

缺点:

RDB进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据

AOF持久化

全量备份总是耗时的,Redis提供了一种更加高效的方式-AOF。原理是Redis会将每一个收到的写命令都通过write函数追加到文件中。相当于对写命令进行日志记录。
在这里插入图片描述
触发方式
默认情况下Redis没有开启AOF,可以修改为yes进行开启。

appendonly no

开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof

AOF可以配置三种持久化方式

# appendfsync always # 每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec # 默认,每秒钟同步一次,显示地将多个写命令同步到硬盘
# appendfsync no   # 让操作系统决定何时进行同步
AOF优缺点

优点

  1. AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过后台线程执行一个fsync操作,最多丢失1秒的数据
  2. AOF日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容器破损。
  3. AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。

缺点

AOF日志文件通常比RDB快照数据文件更大

RDB和AOF的选择

Redis 支持同时开启开启两种持久化方式,我们可以综合使用 AOF 和 RDB 两种持久化机制,用 AOF 来保证数据不丢失,作为数据恢复的第一选择;用 RDB 来做不同程度的冷备,在 AOF 文件都丢失或损坏不可用的时候,还可以使用 RDB 来进行快速的数据恢复。

注意事项
Redis修改配置后未生效(windows)

运行redis-sever.exe需要指定配置文件
在这里插入图片描述

待整理知识点
- 缓存雪崩/缓存击穿/缓存穿透
- 缓存和数据库一致性问题
- Redis集群-主从架构/哨兵模式
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值