参考:Redis设计与实现
事先声明:本文为对该书的总结。并参考 https://segmentfault.com/a/1190000016951866 作者:java3y

数据库相关

Redis服务器的服务器状态由redis.h/redisServer结构来表示,其中的redisDb数组代表当前服务器中的所有数据库

struct redisServer{  //redisDb数组,表示服务器中所有的数据库redisDb *db;  //服务器中数据库的数量int dbnum;  };

其中的dbnum代表当前服务器的数据库数量,这个数量默认为16。我们可以在redis.conf配置文件中看到它,并且可以进行修改

当redis客户端连接redis服务器的时候默认选择的是0号数据库,客户端可以通过select命令进行切换

Redis的客户端用redisClient结构来表示,其中的db指针指向了redisServer中db数组中的一个元素,代表当前客户端正在使用的数据库。

typedef struct redisClient{  //客户端当前所选数据库redisDb *db;  }redisClient;

如图所示:

select命令的实现原理就是将redisClient中的db指针指向了redisServer中db数组的不同位置。

我们知道redis是一种key-value数据库,所有数据都由一个键和一个value组成,键总是一个字符串对象,value可以是不多的对象类型,字符串、列表、哈希表、集合、有序集合。这些对象类型根据存储的内容不同对应着底层不同的数据结构实现。

在数据库结构redis.h/redisDb中的dict属性中保存着该数据库所有的键值对,expires保存着所有键的过期时间。
他们两个都是字典类型(相当于java中的map 里面存着键值对

typedef struct redisDb { // ....int id;         // 数据库ID标识dict *dict;     // 键空间,存放着所有的键值对              dict *expires;  // 过期哈希表,保存着键的过期时间                          long long avg_ttl;  // 数据库内所有键的平均TTL(生存时间)     // ......
} redisDb;

可以从图中看到dist字典里存着一些键 这些都是字符串对象类型,他们分别指向不同类型的值对象。
比如

  • alphabat对应的值为列表类型对象
  • book对应的值为哈希类型对象
  • message对应的值也为一个字符串类型对象

他们对应的命令为

rpush alphabet "a" "b" "c"
hmset book name ... author... publisher...
set message "hello world"

当我们对相应的键进行 增删改查时 就是对字典中的键空间进行操作
提示: 我们可以用TYPE命令返回一个键对应值的对象类型

键的过期策略相关

客户端可以用通过expire命令或者pexpire给键设置生存时间

可以看到5秒生存时间后 wmh这个键过期了

expire命令默认的时间单位为秒
pexpire命令默认的时间单位为毫秒

客户端还可以用expireat 和pexpireat 这两个命令用来给键设置过期时间(UNIX时间戳)
如果当前时间到了指定的时间戳 那么这个键就会过期


expireat命令默认的时间戳单位为秒
pexpireat命令默认的时间戳单位为毫秒

虽然我们有4个命令来决定键啥时候过期
但其中的expire、pexpire、expireat 都是由pexpireat命令来实现的。

比如 expire命令会先被转换为pexpire (将秒转换为毫秒)
接着将pexpire转换为pexpireat(将生存时间加上当前时间戳)

ttl 命令将返回该键剩余生存时间以秒为单位, 对应的 pttl 命令 单位为毫秒

redisDb数据库结构中的expires字典中保存着当前数据库中的键和它的过期时间戳
如果当前键在expires字典中对应的时间戳值小于当前时间戳说明键已过期

过期键删除策略

当键过期redis该什么时候将他删除呢?

通常有3种过期删除策略

  • 定时删除:设置键的同时,开启一个定时器监视键的过期时间,到期时将他删除
    优势:对内存友好,可以保证键过期了就会被删除,不会占用太多内存
    劣势:对cpu不友好,在键很多的情况下,定时器会占用大量的CPU时间,导致不能专注于接收请求,对服 务器的吞吐量和响应时间有很大影响。另外redis中创建定时器需用到时间事件(无序链表),查找一个事件的时间复杂度为O(n),并不能高效大量处理时间事件。时间事件我现在还没学。。

  • 惰性删除:只有当获取键的时候才会检查键是否过期然后将它删除,否则放任不管
    优势:对CPU相当友好,根本不会浪费CPU时间来对无关过期键的检测,只有在获取的时候才会判断一下有没有过期然后将它删除。
    劣势:对内存相当不友好,会保留很多过期键在内存中,因为不可能每次都访问到过期键,太多过期键占用内存会导致内存泄漏。垃圾数据占用内存。

  • 定期删除:每隔一段时间对数据库进行一次检查,删除过期的键,删除的时间和多少由对应的算法决定
    本策略是上面两种策略的中和但也有难点
    优势:每隔一段时间执行对过期键的删除,并限制执行时长和频率来减少对CPU的影响
    定期删除键,对内存也不会有太大的浪费
    劣势:对执行时长和间隔清理的时间难以把控,执行太久或频率太高,会占用大量CPU的时间
    执行时间太短或频率太低,又会浪费内存

Redis采用的过期策略

Redis通过配合惰性删除和定期删除这两种策略,在合理使用CPU时间和避免浪费内存之间取得了平衡

  • 惰性删除策略的实现

    db.c/expireIfNeeded函数实现
    Redis在执行命令之前都会调用expireIfNeeded函数检查该键。
    如果过期,expireIfNeeded删除该键,没过期则啥都不做。
    这个函数保证了命令不会接触到过期键,相当于一个过滤器。
    当命令发现该键对应的值为null时,可能刚被expireIfNeeded删除,也可能真的不存在
    它只需要返回一个空回复就好。

  • 定期删除策略的实现

    redis.c/activeExpireCycle函数实现
    该函数执行时会在规定的时间内,分多次遍历服务器的各个数据库,从数据库中的expires字典中随机检查
    一部分键的过期时间并删除过期键

虽然采用了这两种策略但还是不可避免的会出现过期而没有及时删除的键。还是有一定情况下内存会不足,并且我们需要保证redis有限的内存中保存的都是热点数据,这时会采用 redis 内存淘汰机制。

我们可以设置内存最大使用量,当内存使用量超出时,会施行数据淘汰策略
Redis的内存淘汰机制有以下几种:

Redis采用allkeys-lru淘汰策略,将最近最少使用的数据淘汰

AOF、RDB对过期键的处理

RDB持久化对过期键的策略:

  • 执行SAVE或者BGSAVE命令创建出的RDB文件,程序会对数据库中的过期键检查,已过期的键不会保存在RDB文件中。
  • 载入RDB文件时,程序同样会对RDB文件中的键进行检查,过期的键会被忽略。从服务器无论过期不过期都会载入到数据库

AOF持久化对过期键的策略:

  • 如果数据库的键已过期,但还没被惰性/定期删除,AOF文件不会因为这个过期键产生任何影响(也就说会保留),当过期的键被删除了以后,会追加一条DEL命令来显示记录该键被删除了
  • 重写AOF文件时,程序会对RDB文件中的键进行检查,过期的键会被忽略。

复制模式:

  • 主服务器在删除过期建后会显示的向所有从服务器发送DEL命令,告知从服务删除
  • 从服务器就算发现建已过期也不会删除,还是会继续将过期值返回给客户端,只会等待主服务器发来的DEL命令才会i删除

由主服务器来控制从服务器统一删除键保证数据一致性。

Redis学习篇之数据库实现和过期策略相关相关推荐

  1. Redis学习笔记(四):过期策略、持久化、事件

    图片来自<Redis设计与实现> Redis数据库 redis.server中redisServer.dbnum用于初始化生成多少个数据库,默认16个,Select命令选择数据库 redi ...

  2. Redis运维和开发学习笔记(7) 内存管理和过期策略

    Redis运维和开发学习笔记(7) 内存管理和过期策略 文章目录 Redis运维和开发学习笔记(7) 内存管理和过期策略 内存回收策略 惰性删除 定时任务删除 maxmemory 过期策略allkey ...

  3. Redis学习之单机数据库(二)

    1. 数据库 数据结构 /* Redis database representation. There are multiple databases identified* by integers f ...

  4. Redis 学习笔记-NoSQL数据库 常用五大数据类型 Redis配置文件介绍 Redis的发布和订阅 Redis_事务_锁机制_秒杀 Redis应用问题解决 分布式锁

    1.NoSQL数据库 1.1 NoSQL数据库概述 NoSQL(NosQL = Not Only sQL ),意即"不仅仅是sQL",泛指非关系型的数据库.NoSQL不依赖业务逻辑 ...

  5. Redis学习篇3_事务及其监控(锁)、Jedis、SpringBoot整合Redis、RedisTemplate的json序列化、RedisUtil工具类

    目录 事务及其监控(锁) Jedis SpringBoot整合Redis RedisTemplate 默认RedisTemplate来源 关于中文序列化问题 RedisUtil工具类 一.事务及其监控 ...

  6. redis学习篇(十)-----高级特性之持久化处理

    2019独角兽企业重金招聘Python工程师标准>>> redis是基于内存的数据库.基于内存的数据库速度虽然快,但是却有一个严重的弊端:当服务器突然宕机或者断电时,内存里的数据都会 ...

  7. 项目知识学习篇———PostgreSQL数据库

    PostgreSQL学习手册(数据表) http://www.cnblogs.com/stephen-liu74/archive/2011/12/16/2290803.html 一.表的定义 二.系统 ...

  8. 【redis学习篇】哨兵架构详解

    一.哨兵架构概要 sentinel哨兵是特殊的redis服务,不提供读写服务,主要用来监控redis实例节点. sentinel实时监视主从集群,能实时知道哪个节点是主节点,哪些是从节点,哨兵架构下c ...

  9. redis学习篇(九)-----高级特性之事务处理

    2019独角兽企业重金招聘Python工程师标准>>> redis目前对事务的处理比较简单,只能保证一个客户端连接发起事务中的命令可以连续的执行,而中间不被插入其他客户端连接的命令. ...

最新文章

  1. Vue封装一个简单轻量的上传文件组件
  2. 关于JetBrains CLion 激活 (CLion License Activation)的解决办法,带hosts详细修改
  3. 设计模式:模板方法模式(Template Method)
  4. html怎么让方块自动旋转,如何使用纯CSS实现一个圆环旋转错觉的动画效果(附源码)...
  5. Cesium中级教程6 - 3D Models 三维模型
  6. RTOS原理与实现13(完):内核裁剪与移植
  7. LPSTR、LPWSTR、LPCSTR、LPCWSTR、LPTSTR、LPCTSTR的区分与转化
  8. 《Python CookBook2》 第一章 文本 - 测试一个对象是否是类字符串 字符串对齐
  9. Map集合-根据宠物昵称查找宠物
  10. 《DSP using MATLAB》Problem 7.25
  11. Socket网络编程进阶与实战
  12. 自定义填充图案插件 cad_CAD软件中如何自定义CAD填充图案?
  13. 阳光房行业现状调研及趋势分析报告
  14. 将iTunes降级到12.6版本
  15. 代币标准--ERC721协议源码解析
  16. CLion2020调整字体大小
  17. cadence 通孔焊盘_通孔焊盘内走线不报错
  18. 被骗进一个很隐蔽的外包公司,入职一个月才发现,已经有了社保记录,简历污了,以后面试有影响吗?...
  19. Java编程那些事儿74——java.lang包介绍1
  20. Windows10中如何使用ADSL固定IP地址连接因特网

热门文章

  1. python 编译成exe vmp加密_Python vmp包_程序模块 - PyPI - Python中文网
  2. Python+requests 有道翻译接口 翻译中英文
  3. 总结下利用Python赚钱的方法,过年在家太闲,小赚看一笔,1000-5000不等
  4. antlr4例子步骤
  5. H5:画布Canvas基础知识讲解(三)之文字、阴影、颜色渐变
  6. Collectors详解
  7. 解决递归求阶乘问题时间复杂度计算
  8. 流媒体网络协议 -- DASH
  9. yii2 php 生成小程序分享海报的两种方法
  10. SDL_Surface表面