从零单排学Redis【白银】

前言

只有光头才能变强html

今天继续来学习Redis,上一篇从零单排学Redis【青铜】已经将Redis经常使用的数据结构过了一遍了。若是还没看的同窗能够先去看一遍再回来~git

这篇主要讲的内容有:程序员

  • Redis服务器的数据库
  • Redis对过时键的处理
  • Redis持久化策略(RDB和AOF)

本文力求简单讲清每一个知识点,但愿你们看完能有所收获github

1、Redis服务器中的数据库

咱们应该都用过MySQL,MySQL咱们能够在里边建立好几个库:redis

MySQL建立几个数据库

一样地,Redis服务器中也有数据库这么一个概念。若是不指定具体的数量,默认会有16个数据库。sql

经过SELECT命令能够切换到0~15的数据库

上面的命令咱们也能够发现:当切换到15号数据库,存进15号库的数据,再切换到0号数据库时,是获取不到的!数据库

  • 这说明,数据库与数据库之间的数据是隔离的。

1.1Redis数据库的原理

Redis服务器用redisServer结构体来表示,其中redisDb是一个数组,用来保存全部的数据库,dbnum表明数据库的数量(这个能够配置,默认是16)数组

struct redisServer{  

    //redisDb数组,表示服务器中全部的数据库
    redisDb *db;  
    
    //服务器中数据库的数量
    int dbnum;  
  
};

咱们知道Redis是C/S结构,Redis客户端经过redisClient结构体来表示:缓存

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

Redis客户端链接Redis服务端时的示例图:安全

Redis客户端链接Redis服务端时的示例图

Redis中对每一个数据库用redisDb结构体来表示:

typedef struct redisDb { 
    int id;         // 数据库ID标识
    dict *dict;     // 键空间,存放着全部的键值对              
    dict *expires;  // 过时哈希表,保存着键的过时时间                          
    dict *watched_keys; // 被watch命令监控的key和相应client    
    long long avg_ttl;  // 数据库内全部键的平均TTL(生存时间)     
} redisDb;

从代码上咱们能够发现最重要的应该是dict *dict,它用来存放着全部的键值对。对于dict数据结构(哈希表)咱们在上一篇也已经详细说了。通常咱们将存储全部键值对的dict称为键空间

键空间示意图:

键空间示意图

Redis的数据库就是使用字典(哈希表)来做为底层实现的,对数据库的增删改查都是构建在字典(哈希表)的操做之上的

例如:

redis > GET message

"hello world"

查找键的示意图

1.2键的过时时间

Redis是基于内存,内存是比较昂贵的,容量确定比不上硬盘的。就咱们如今一台普通的机子,可能就8G内存,但硬盘随随便便都1T了。

由于咱们的内存是有限的。因此咱们会干掉不经常使用的数据,保留经常使用的数据。这就须要咱们设置一下键的过时(生存)时间了。

  • 设置键的生存时间能够经过EXPIRE或者PEXPIRE命令。
  • 设置键的过时时间能够经过EXPIREAT或者PEXPIREAT命令。

其实EXPIREPEXPIREEXPIREAT这三个命令都是经过PEXPIREAT命令来实现的。

咱们在redisDb结构体中还发现了dict *expires;属性,存放全部键过时的时间。

举个例子基本就能够理解了:

redis > PEXPIREAT message 1391234400000
(integer) 1

设置了message键的过时时间为1391234400000

新增一个过时时间的键

既然有设置过时(生存)时间的命令,那确定也有移除过时时间,查看剩余生存时间的命令了:

  • PERSIST(移除过时时间)
  • TTL(Time To Live)返回剩余生存时间,以秒为单位
  • PTTL以毫秒为单位返回键的剩余生存时间

1.2.1过时策略

上面咱们已经可以了解到:过时键是保存在哈希表中了。那这些过时键到了过时的时间,就会立马被删除掉吗??

要回答上面的问题,须要咱们了解一下删除策略的知识,删除策略可分为三种

  • 定时删除(对内存友好,对CPU不友好)
    • 到时间点上就把全部过时的键删除了。
  • 惰性删除(对CPU极度友好,对内存极度不友好)
    • 每次从键空间取键的时候,判断一下该键是否过时了,若是过时了就删除。
  • 按期删除(折中)
    • 每隔一段时间去删除过时键,限制删除的执行时长和频率。

Redis采用的是惰性删除+按期删除两种策略,因此说,在Redis里边若是过时键到了过时的时间了,未必被立马删除的!

1.2.2内存淘汰机制

若是按期删除漏掉了不少过时key,也没及时去查(没走惰性删除),大量过时key堆积在内存里,致使redis内存块耗尽了,咋整?

咱们能够设置内存最大使用量,当内存使用量超出时,会施行数据淘汰策略

Redis的内存淘汰机制有如下几种:

内存淘汰机制

通常场景:

使用 Redis 缓存数据时,为了提升缓存命中率,须要保证缓存数据都是热点数据。能够将内存最大使用量设置为热点数据占用的内存量,而后启用allkeys-lru淘汰策略,将最近最少使用的数据淘汰

2、Redis持久化

Redis是基于内存的,若是不想办法将数据保存在硬盘上,一旦Redis重启(退出/故障),内存的数据将会所有丢失。

  • 咱们确定不想Redis里头的数据因为某些故障所有丢失(致使全部请求都走MySQL),即使发生了故障也但愿能够将Redis原有的数据恢复过来,这就是持久化的做用。

Redis提供了两种不一样的持久化方法来说数据存储到硬盘里边:

  • RDB(基于快照),将某一时刻的全部数据保存到一个RDB文件中。
  • AOF(append-only-file),当Redis服务器执行写命令的时候,将执行的写命令保存到AOF文件中。

2.1RDB(快照持久化)

RDB持久化能够手动执行,也能够根据服务器配置按期执行。RDB持久化所生成的RDB文件是一个通过压缩的二进制文件,Redis能够经过这个文件还原数据库的数据。

RDB文件还原数据

有两个命令能够生成RDB文件:

  • SAVE阻塞Redis服务器进程,服务器不能接收任何请求,直到RDB文件建立完毕为止。
  • BGSAVE建立出一个子进程,由子进程来负责建立RDB文件,服务器进程能够继续接收请求。

Redis服务器在启动的时候,若是发现有RDB文件,就会自动载入RDB文件(不须要人工干预)

  • 服务器在载入RDB文件期间,会处于阻塞状态,直到载入工做完成。

除了手动调用SAVE或者BGSAVE命令生成RDB文件以外,咱们可使用配置的方式来按期执行:

在默认的配置下,若是如下的条件被触发,就会执行BGSAVE命令

save 900 1              #在900秒(15分钟)以后,至少有1个key发生变化,
    save 300 10            #在300秒(5分钟)以后,至少有10个key发生变化
    save 60 10000        #在60秒(1分钟)以后,至少有10000个key发生变化

原理大概就是这样子的(结合上面的配置来看):

struct redisServer{
    // 修改计数器
    long long dirty;

    // 上一次执行保存的时间
    time_t lastsave;

    // 参数的配置
    struct saveparam *saveparams;
};

遍历参数数组,判断修改次数和时间是否符合,若是符合则调用besave()来生成RDB文件

Redis服务器的状态

总结:经过手动调用SAVE或者BGSAVE命令或者配置条件触发,将数据库某一时刻的数据快照,生成RDB文件实现持久化。

2.2AOF(文件追加)

上面已经介绍了RDB持久化是经过将某一时刻数据库的数据“快照”来实现的,下面咱们来看看AOF是怎么实现的。

  • AOF是经过保存Redis服务器所执行的写命令来记录数据库的数据的。

AOF原理图

好比说咱们对空白的数据库执行如下写命令:

redis> SET meg "hello"
OK

redis> SADD fruits "apple" "banana" "cherry"
(integer) 3

redis> RPUSH numbers 128 256 512
(integer) 3

Redis会产生如下内容的AOF文件:

AOF文件

这些都是以Redis的命令请求协议格式保存的。Redis协议规范(RESP)参考资料:

AOF持久化功能的实现能够分为3个步骤:

  • 命令追加:命令写入aof_buf缓冲区
  • 文件写入:调用flushAppendOnlyFile函数,考虑是否要将aof_buf缓冲区写入AOF文件中
  • 文件同步:考虑是否将内存缓冲区的数据真正写入到硬盘

AOF持久化步骤

flushAppendOnlyFile函数的行为由服务器配置的appendfsyn选项来决定的:

appendfsync always     # 每次有数据修改发生时都会写入AOF文件。
    appendfsync everysec   # 每秒钟同步一次,该策略为AOF的默认策略。
    appendfsync no         # 从不一样步。高效可是数据不会被持久化。

从字面上应该就更好理解了,这里我就不细说了...

下面来看一下AOF是如何载入与数据还原的:

  • 建立一个伪客户端(本地)来执行AOF的命令,直到AOF命令被所有执行完毕。

redis伪客户端载入AOF文件

2.2.1AOF重写

从前面的示例看出,咱们写了三条命令,AOF文件就保存了三条命令。若是咱们的命令是这样子的:

redis > RPUSH list "Java" "3y"
(integer)2

redis > RPUSH list "Java3y"
integer(3)

redis > RPUSH list "yyy"
integer(4)

一样地,AOF也会保存3条命令。咱们会发现一个问题:上面的命令是能够合并起来成为1条命令的,并不须要3条。这样就能够让AOF文件的体积变得更小

AOF重写由Redis自行触发(参数配置),也能够用BGREWRITEAOF命令手动触发重写操做。

  • 要值得说明的是:AOF重写不须要对现有的AOF文件进行任何的读取、分析。AOF重写是经过读取服务器当前数据库的数据来实现的

好比说如今有一个Redis数据库的数据以下:

Redis数据库的数据

新的AOF文件的命令以下,没有一条是多余的

AOF重写后的命令

2.2.2AOF后台重写

Redis将AOF重写程序放到子进程里执行(BGREWRITEAOF命令),像BGSAVE命令同样fork出一个子进程来完成重写AOF的操做,从而不会影响到主进程。

AOF后台重写是不会阻塞主进程接收请求的,新的写命令请求可能会致使当前数据库和重写后的AOF文件的数据不一致

为了解决数据不一致的问题,Redis服务器设置了一个AOF重写缓冲区,这个缓存区会在服务器建立出子进程以后使用

AOF后台重写过程

2.3RDB和AOF对过时键的策略

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

  • 执行SAVE或者BGSAVE命令建立出的RDB文件,程序会对数据库中的过时键检查,已过时的键不会保存在RDB文件中
  • 载入RDB文件时,程序一样会对RDB文件中的键进行检查,过时的键会被忽略

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

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

复制模式:

  • 主服务器来控制从服务器统一删除过时键(保证主从服务器数据的一致性)

2.4RDB和AOF用哪一个?

RDB和AOF并不互斥,它俩能够同时使用

  • RDB的优势:载入时恢复数据快、文件体积小。
  • RDB的缺点:会必定程度上丢失数据(由于系统一旦在定时持久化以前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。)
  • AOF的优势:丢失数据少(默认配置只丢失一秒的数据)。
  • AOF的缺点:恢复数据相对较慢,文件体积大

若是Redis服务器同时开启了RDB和AOF持久化,服务器会优先使用AOF文件来还原数据(由于AOF更新频率比RDB更新频率要高,还原的数据更完善)

可能涉及到RDB和AOF的配置:

redis持久化,两种方式
一、rdb快照方式
二、aof日志方式

----------rdb快照------------
save 900 1
save 300 10
save 60 10000

stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/rdb/

-----------Aof的配置-----------
appendonly no # 是否打开 aof日志功能

appendfsync always #每个命令都当即同步到aof,安全速度慢
appendfsync everysec
appendfsync no 写入工做交给操做系统,由操做系统判断缓冲区大小,统一写入到aof  同步频率低,速度快


no-appendfsync-on-rewrite yes 正在导出rdb快照的时候不要写aof
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb 


./bin/redis-benchmark -n 20000

官网文档:

3、最后

如今临近双十一买阿里云服务器就特别省钱!以前我买学生机也要9.8块钱一个月,如今最低价只须要8.3一个月!

不管是Nginx/Elasticsearch/Redis这些技术都是在Linux下完美运行的,若是仍是程序员新手,买一个学习Linux基础命令,学习搭建环境也是不错的选择。

若是有要买服务器的同窗可经过个人连接直接享受最低价https://m.aliyun.com/act/team1111/#/share?params=N.FF7yxCciiM.pfn5xpli


若是你们有更好的理解方式或者文章有错误的地方还请你们不吝在评论区留言,你们互相学习交流~~~

参考资料:

  • 《Redis设计与实现》
  • 《Redis实战》

一个坚持原创的Java技术公众号:Java3y,欢迎你们关注

3y全部的原创文章:

相关文章
相关标签/搜索