欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

程序员文章站 2022-05-18 17:16:44
...

Redis学习笔记

基础篇

Redis数据类型

官方命令大全网址:http://www.redis.cn/commands.html
Redis中存储的数据是通过key-value格式存储数据的,其中value可以定义五种数据类型:

  • String(字符类型)
  • Hash(散列类型)
  • List(列表类型)
  • Set(集合类型)
  • SortedSet(有序集合类型,简称zset)
    注意:在Redis中的命令语句中,命令是忽略大小写的,而key是不忽略大小写的

key的设计技巧

JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

string类型

常用命令

赋值
  • 语法:SET key value
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
取值
  • 语法:GET key
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
取值并赋值
  • 语法:GETSET key value
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
数值增减

注意事项

  1. 当value为整数数据时,才能使用以下命令操作数值的增减
  2. 数值增减都是【原子】操作
  3. redis中的每一个单独的命令都是原子性操作。当多个命令一起执行的时候,就不能保证原子性,不过我们可以使用事物和lua脚本来保证这一点
递增数字
  • 语法:INCR key
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
增加指定的整数
  • 语法:INCRBY key increment
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
递减数值
  • 语法:DECR key
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
减少指定的整数
  • 语法:DECRBY key decrement
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
仅当不存在时赋值

使用该命令可以实现分布式锁的功能

  • 语法:SETNX key value
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

其他命令

向尾部追加值
  • 语法:APPEND key value
  • 说明:如果键不存在则将该键的值设置为value,即相当于 SET key value,返回值是追加后字符串的总长度
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
获取字符串长度
  • 语法:STRLEN key
  • 说明:返回键值的长度,如果键不存在则返回0
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
同时设置/获取多个键值
  • 语法:MSET key value [key value …]MGET key [key …]
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

应用场景之自增主键

  • 需求:商品编号、订单号采用INCR命令生成
  • 设计:key命名要有一定的设计
  • 实现:定义商品编号key:items:id
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

hash类型

hash类型介绍

hash类型也叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其他类型。如下:
JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

常用命令

赋值

HSET命令不区分插入和更新操作,当执行插入操作时,返回值1;当执行更新操作时,返回0

设置一个字段值
  • 语法:HSET key field value
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
设置多个字段值
  • 语法:HMSET key field value [field value …]
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
当字段不存在时赋值
  • 语法:HSETNX key field value
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
取值
获取一个字段值
  • 语法:HGET key field
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
获取多个字段值
  • 语法:HMGET key field [field …]
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
获取所有字段值
  • 语法:HGETALL key
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
删除字段

可以删除一个或多个字段,返回值是被删除的字段个数

  • 语法:HDEL key field [filed …]
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
增加数字
  • 语法:HINCRBY key field increment
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

其他命令

  • 判断字段是否存在:HEXISTS key field
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
  • 只获取字段名或字段值:HKEYS keyHVALS key
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
  • 获取字段数量:HLEN key
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
  • 获取所有字段:HGETALL key
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

string类型和hash类型的区别

hash类型适合存储那些对象信息,特别是对象属性经常发生【增删改】操作的数据。string类型也可以存储对象数据,将java对象转成json字符串进行存储,这种存储适合【查询】操作

应用之存储商品信息

  • 商品信息字段:商品id、商品名称、商品描述、商品库存、商品好评
  • 定义商品信息的key:商品ID为1001的信息在 Redis 中的key为:【items:1001】
  • 存储商品信息:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
  • 获取商品信息:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

list类型

ArrayList和LinkedList的区别

  • ArrayList使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,所以比较慢。
  • LinkedList使用双向链表方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快。然后通过下标查询时需要从头开始索引,所以比较慢,但是如果查询前几个元素或后几个元素速度比较快。
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

list类型介绍

  • Redis的列表类型可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。
  • 列表类型内部是使用双向实现的,所以上列表两端添加元素的时间复杂度为O(1),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的。

常用命令

JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

LPUSH、RPUSH
  • 语法:LPUSH key value [value …]RPUSH key value [value …]
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
LRANGE
  • 语法:LRANGE key start stop
  • 说明:获取列表中的某一片段,将返回 start—stop之间的所有元素(包括两端的元素),索引从 0 开始,索引可以是负数,如 -1 代表最后边的一个元素
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
LPOP、RPOP
  • 语法:LPOP keyRPOP key
  • 说明:从列表两端【L-左端,R-右端】弹出元素。从左端弹出一个元素,分两步:一是将列表左边的元素从列表中移除、第二步是返回移除的元素值
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
LLEN
  • 语法:LLEN key
  • 说明:获取列表中元素的个数
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

其他命令

LREM
  • 语法:LREM key count value
  • 说明:删除列表中指定个数的值。
- 当 count>0 时,LREM会从列表左边开始删除;
- 当 count<0 时,LREM会从列表右边开始删除;
- 当 count=0 时,LREM删除所有值为value的元素
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
LINDEX
  • 语法:LINDEX key index
  • 说明:获取指定索引的元素值
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
LSET
  • 语法:LSET key index value
  • 说明:设置指定索引的元素值
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
LTRIM
  • 语法:LTRM key start stop
  • 说明:只保留列表指定片段,指定返回和LRANGE一致
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
LINSERT
  • 语法:LINSERT key BEFORE|AFTER pivot value
  • 说明:向列表中插入元素。该命令首先会在列表中从左到右查找pivot的元素,然后根据第二个参数是BEFROE还是AFTER来决定将value插入到该元素的前面还是后面
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
RPOPLPUSH
  • 语法:RPOPLPUSH source destination
  • 说明:将元素从一个列表转移到另一个列表中
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

应用之商品评论列表

  • 需求:
* 用户针对某一商品发布评论,一个商品会被不同的用户进行评论,存储商品评论时,要按时间顺序排序
* 用户在前端页面查询该商品的评论,需要按照时间降序排序
  • 分析:使用list存储商品评论信息,KEY是该商品的ID,VALUE是商品评论信息列表
  • 实现:商品编号为1001的商品评论key:【items:comment:1001】
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

set类型

set类型介绍

  • set类型即集合类型,其中的数据是不重复且没有顺序
  • 集合类型和列表类型的对比
集合类型 列表类型
存储内容 至多2^32-1个字符串 至多2^32-1个字符串
有序性
唯一性
  • 集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,由于集合类型的Redis内部使用值为空的散列表实现,所以这些操作的时间复杂度都是O(1)
  • Redis还提供了多个集合之间的交集、并集、差集的运算

常用命令

SADD、SREM
  • 语法:SADD key member [member …]SREM key member [member …]
  • 说明:添加元素、删除元素
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
SMEMBERS
  • 语法:SMEMBERS key
  • 说明:获得集合中的所有元素
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
SISMEMBER
  • 语法:SISMEMBER key member
  • 说明:判断元素是否在集合中
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

集合运算命令

SDIFF
  • 语法:SDIFF key [key …]
  • 说明:集合的差集运算 A-B——属于A并且不属于B的元素构成的集合
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
SINTER
  • 语法:SINTER key [key …]
  • 说明:集合的交集运算A∩B——属于A并且属于B的元素构成的集合
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
SUNION
  • 语法:SUNION key [key …]
  • 说明:集合的并集元素A∪B:属于A或者属于B的元素构成的集合
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

其他命令

SCARD
  • 语法:SCARD key
  • 说明:获得集合中元素的个数
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
SPOP
  • 语法:SPOP key
  • 说明:从集合中弹出一个元素。注意——由于集合是无序的,所以SPOP命令会从集合中随机选择一个元素弹出
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

zset类型

zet介绍

  • 在set集合类型的基础上,有序集合类型为集合中的每个元素都关联一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在集合中,还能够获得分数最高或最低的前N个元素、获取指定分数范围内的元素与分数有关的操作。
  • 在某些方面有序集合和列表类型有些相似:
* 二者都是有序的
* 二者都可以获得某一范围的元素
  • 但是,二者有着很大的区别:
* 列表类型是通过链表实现的,获取靠近两端的数据速度极快、而元素增多后,访问中间数据的速度会变慢
* 有序集合类型使用散列表实现,所以即时读取位于中间部分的数据也很快
* 列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现)
* 有序集合要比列表类型更耗内存

常用命令

ZADD
  • 语法:ZADD key score member [ score member …]
  • 说明:添加元素。向有序集合中加入一个元素和该元素的分数,如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
ZRANGE、ZREVRANGE
  • 语法:ZRANGE key start stop [WITHSCORES]ZREVRANGE key start stop [WITHSCORES]
  • 说明:获得排名在某个范围的元素列表
- ZRANGE:按照元素分数从小到大的顺序返回索引从 start 到 stop 之间的所有元素(包含两端的元素)
- ZREVRANGE:按照元素分数从大到小的顺序返回索引从 start 到 stop 之间的所有元素(包含两端的元素)
  • 如果需要获得元素的分数,可以在命令的尾部加上WITHSCORES参数:
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
ZSCORE
  • 语法:ZSCORE key member
  • 说明:获取元素的分数
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
ZREM
  • 语法:ZREM key member [member …]
  • 说明:删除元素。移除有序集合key中的一个或多个成员,不存在的成员将被忽略;当key存在但不是有序集合类型时,返回一个错误
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

其他命令

ZRANGEBYSCORE
  • 语法:ZRANGEBYSCORE key min max [WITHSCORES]
  • 说明:获得指定分数返回的元素
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
ZINCRBY
  • 语法:ZINCRBY key increment member
  • 说明:增加某个元素的分数,返回值是更改后的分数
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
ZCARD
  • 语法:ZCARD key
  • 说明:获得集合中元素的数量
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
ZCOUNT
  • 语法:ZCOUNT key min max
  • 说明:获得指定分数范围内的元素个数
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
ZREMRANGEBYRANK
  • 语法:ZREMRANGEBYRANK key start stop
  • 说明:按照排名范围删除元素
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
ZREMRANGEBYSCORE
  • 语法:ZREMRANGEBYSCORE key start stop
  • 说明:按照分数范围删除元素
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)
ZRANK、ZREVRANK
  • 语法:ZRANK key memberZREVRANK key member
  • 说明:获取元素的排名
- ZRANK:从小到大
- ZREVRANK:从大到小
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

应用之商品销售排行榜

  • 需求:根据商品销量对商品进行排序显示
  • 设计:定义商品销量排行榜(sorted set 集合),key为 items:sellsort,分数为商品销售量
    写入商品销售量
  • 商品编号1001的销量是9,商品编号1002的销量是10:
  • 商品编号1001的销量加1:
  • 商品销量前10名:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

通用命令

KEYS

  • 语法:KEYS pattern
  • 说明:返回满足给定pattern的所有key
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

DEL

  • 语法:DEL key
  • 说明:根据key删除某一条记录
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

EXISTS

  • 语法:EXISTS key
  • 说明:确认一个key是否存在
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

EXPIRE(重点)

  • 语法:
    EXPIRE key seconds:设置key的生存时间(单位:秒)key在多少秒后会自动删除
    TTL key:查看key剩余的生存时间
    PERSIST key:清除生存时间
    PEXPIRE key milliseconds:生存时间设置单位为:毫秒
  • 说明:Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,即:到期后数据销毁
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

RENAME

  • 语法:RENAME oldkey newkey
  • 说明:重命名key
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

TYPE

  • 语法:TYPE key
  • 说明:显示指定key的数据类型
  • 示例:
    JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

总结适用场景

  • String
    • 序列化字符串数据缓存
    • 采用INCR命令生成商品编号、订单号
    • 共享用户Session,
    • 基于setnx实现分布式锁
  • List
    • 基于列表的存储结构实现 粉丝列表、商品评论列表
    • 基于Redis 的lrange命令实现简单的高性能分页
    • 简单的消息队列
  • Hash
    • 存储商品信息
    • 秒杀仓库
  • Set
    • 基于Redis进行全局的Set集合去重
    • 基于Redis的集合运算(交集、差集),实现共同好友、可能认识的人
  • zSet
    • 商品销量排行榜,热搜【前面是名称,后面是热度值】
    • 有权重的消息队列

Redis 的补充数据类型

BitMap

  • BitMap 就是通过一个 bit 位来表示某个元素对应的值或者状态,其中的 key 就是对应元素本身,实际上底层也是通过对字符串的操作来实现。Redis 从 2.2 版本之后新增了setbit、getbit、bitcount 等几个bitmap 相关命令。虽然是新命令,但是本身都是对字符串的操作,我们先来看看语法:
SETBIT key offset value
# 其中 offset 必须是数字,value 只能是 0 或者 1
  • 测试:
127.0.0.1:6379> setbit k1 5 1
(integer) 0
127.0.0.1:6379> getbit k1 5
(integer) 1
127.0.0.1:6379> getbit k1 4
(integer) 0
127.0.0.1:6379> bitcount k1
(integer) 1
127.0.0.1:6379> setbit k1 3 1
(integer) 0
127.0.0.1:6379> bitcount k1
(integer) 2
127.0.0.1:6379> setbit "200522:active" 67 1
(integer) 0
127.0.0.1:6379> setbit "200522:active" 78 1
(integer) 0
127.0.0.1:6379> 

通过 bitcount可以很快速的统计,比传统的关系型数据库效率高很多,比如:

  • 统计年活跃用户数量:用户的ID作为offset,当用户在一年内访问过网站,就将对应offset的bit值设置为“1”;通过bitcount 来统计一年内访问过网站的用户数量。
  • 统计三天内活跃用户数量:时间字符串作为key,比如 “200522:active“ ;用户的ID就可以作为offset,当用户访问过网站,就将对应offset的bit值设置为“1”;统计三天的活跃用户,通过bitop or 获取一周内访问过的用户数量。
  • 连续三天访问的用户数量:bitop and
  • 三天内没有访问的用户数量:bitop not
  • 统计在线人数:设置在线key:“online:active”,当用户登录时,通过setbit设置。
  • bitmap 的优势,以统计活跃用户为例,每个用户id占用空间为1bit,消耗内存非常少,存储1亿用户量只需要12.5M。
  • bitmap 还可以做布隆过滤器:确认访问值是否存在,只要在布隆过滤器里值是0,后面的服务就别访问了

HyperLogLog(2.8)

  • 基于bitmap 计数
  • 基于概率基数计数 0.87
  • 这个数据结构的命令有三个:PFADD、PFCOUNT、PFMERGE,内部编码主要分稀疏型和密集型。
  • 用途:记录网站IP注册数,每日访问的IP数,页面实时 UV、在线用户人数。
  • 局限性:只能统计数量,没有办法看具体信息。
127.0.0.1:6379> pfadd h1 b
(integer) 1
127.0.0.1:6379> pfadd h1 a
(integer) 1
127.0.0.1:6379> pfcount h1				# 统计数据
(integer) 2
127.0.0.1:6379> pfadd h1 c
(integer) 1
127.0.0.1:6379> pfadd h2 a
(integer) 1
127.0.0.1:6379> pfadd h3 d
(integer) 1
127.0.0.1:6379> pfmerge h3 h1 h2		# 将h1、h2中的数据合并到h3
OK
127.0.0.1:6379> pfcount h3
(integer) 4
127.0.0.1:6379> 

Geospatial

  • 底层数据结构 Zset
  • 命令有:GEOADD、GEODIST、GEOHASH、GEOPOP、GEOPADUIS、GEORADIUSBYMEMBER
  • 可以用来保存地理位置,并作位置距离计算或者根据半径计算位置等。有没有想过用Redis来实现附近的人?或者计算最优地图路径?Geo本身不是一种数据结构,它本质上是借助于 Sorted Set(zset)
  • 把某个具体的位置信息(经度,纬度,名称)添加到指定的key中,数据将会用一个sorted set存储,以便稍后能使用GEORADIUSGEORADIUSBYMEMBER命令来根据半径来查询位置信息。
# GEOADD key 经度 维度 名称
127.0.0.1:6379> geoadd cities 116.404269 39.91582 "beijing" 121.478799 31.235456 "shanghai"
(integer) 2
127.0.0.1:6379> zrange cities 0 -1
1) "shanghai"
2) "beijing"
127.0.0.1:6379> zrange cities 0 -1 withscores
1) "shanghai"
2) "4054803475356102"
3) "beijing"
4) "4069885555377153"
127.0.0.1:6379> geodist cities beijing shanghai km		# 计算两点之间的距离,单位km
"1068.5677"
127.0.0.1:6379> geopos cities beijing shanghai		# 显示经纬度
1) 1) "116.40426903963088989"
   2) "39.91581928642635546"
2) 1) "121.47879928350448608"
   2) "31.23545629441388627"
127.0.0.1:6379> geoadd cities 120.165036 30.278973 hangzhou
(integer) 1
127.0.0.1:6379> georadius cities 120 30 500 km		# 寻找经纬度120 30距离500km范围的城市
1) "hangzhou"
2) "shanghai"
127.0.0.1:6379> georadiusbymember cities shanghai 200 km	# 找出以上海经纬度为中心点,距离200km范围内的城市
1) "hangzhou"
2) "shanghai"
127.0.0.1:6379> zrange cities 0 -1
1) "hangzhou"
2) "shanghai"
3) "beijing"
127.0.0.1:6379> 

Redis持久化

Redis是一个内存数据库,为了保证数据的持久性,主要提供了两种持久化方案

  • RDB方式(默认)
  • AOF方式
  • 在4.0版本及以后,提供了 混合持久化模式,5.0默认开启,它是AOF的一个补充。

RDB方式

  • RDB是 Redis 默认采用的持久化方式。
  • RDB是通过快照(snapshotting)完成的,当符合一定条件时,Redis会自动将内存中的数据进行快照并持久化到硬盘。

触发快照的时机

  • 符合自定义配置的快照规则
  • 执行save或者bgsave命令
  • 执行flushall命令
  • 执行主从复制操作

特别说明
Redis启动后会读取RDB快照文件,将数据从硬盘载入到内存
根据数据量大小与结构和服务器性能不同,这个快照条件也需要不同。通常将记录一千万个字符串类型键、大小为1GB的快照文件载入到内存中需要花费20 ~ 30秒钟。

设置快照规则

1. RDB持久化条件

  • 格式:SAVE seconds changes
  • 示例:可以配置多个条件(每行配置一个条件),每个条件之间是 “或” 的关系
save 900 1	:表示15分钟内至少1个键被更改则进行快照
save 300 10	:表示5分钟内至少10个键被更改则进行快照
save 60 1000:表示1分钟内至少1000个键被更改则进行快照

2. 配置dir指定rdb快照文件的位置

# Note that you must specify a directory here, not a file name.
dir ./

3. 配置dbfilename指定rdb快照文件的名称

# The filename where to dump the DB
dbfilename dump.rdb

RDB快照的实现原理

  • 快照过程
* Redis调用系统中的 fork 函数复制一份当前进程的副本(子进程)
* 父进程继续接收并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件
* 当子进程写入完所有数据后会用该临时文件替换旧的RDB文件,至此,一次快照操作完成

JavaEE 企业级分布式高级架构师(五)Redis学习笔记(2)

  • 注意事项:
* Redis在进行快照的过程中不会修改 RDB 文件,只有快照结束后才会将旧的文件替换成新的,也就是说任何时候 RDB 文件都是完整的。
* 这就使得我们可以通过定时备份 RDB 文件来实现 Redis 数据库备份,RDB 文件是经过压缩的二进制文件,占用的空间会小于内存中的数据,更加利于传输。

RDB优缺点

  • 缺点:使用 RDB 方式实现持久化,一旦 Redis 异常退出,就会丢失最后一次快照以后更改的所有数据。这个时候我们就需要根据具体的应用场景,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受的范围。如果数据相对来说比较重要,希望将损失降到最小,则可以使用 AOF 方式进行持久化
  • 优点:RDB 可以最大化 Redis 性能,父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无需执行任何磁盘 I/O 操作。同时这个也是一个缺点,乳沟数据集比较大的时候,fork 可能比较耗时,造成服务器在一段时间内停止处理客户端的请求

AOF方式

AOF介绍

  • 默认情况下 Redis 没有开启 AOF(append only file)方式的持久化
  • 开启 AOF 持久化之后,每执行一条会更改 Redis 中的数据的命令,Redis就会将该命令写入磁盘中的 AOF 文件,这一过程显然会降低 Redis 的性能,但大部分情况下这个影响是能够接受的,另外使用较快的硬盘可以提供 AOF 的性能
  • redis.conf配置修改
# 可以通过修改redis.conf配置文件中的appendonly参数开启
appendonly yes
# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的
dir ./
# 默认的文件名是appendonly.aof,可以通过appendfilename参数修改
appendfilename appendonly.aof

同步磁盘数据

  • Redis每次更改数据的时候,aof 机制都会将命令记录到 aof 文件,但是实际上由于操作系统的缓存机制,数据并没有实时写入到硬盘,而是进入硬盘缓存,再通过硬盘缓存机制去刷新到文件中保存
  • 参数说明
# 每次执行写入都会进行同步,这个是最安全但是效率比较低的方式
appendfsync always
# 每一秒执行(默认)
appendfsync everysec
# 不主动进行同步操作,由系统区执行,这个是最快但是最不安全的方式
appendfsync no

AOF重写原理(优化AOF文件)

  • Redis 可以再 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写。重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合
  • 整个重写操作时绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并对新 AOF 文件进行追加操作。
  • AOF文件有序地保存了对数据库执行的所有写入操作,这些写入操作以Redis协议的格式保存,因此,AOF 文件的内容非常容易被人读懂,对文件进行分析(parse)也很轻松
  • 参数说明:
# 表示当前AOF文件大小超过一次aof文件大小的百分之多少的时候会进行重写。如果之前没有重写过,已启动时aof文件大小为准
auto-aof-rewrite-percentage 100
# 限制允许重写最小aof文件大小,也就是文件大小小于64MB的时候,不需要进行优化
auto-aof-rewrite-size 64mb

AOF文件损坏后如何修复

  • 问题描述:服务器可能在程序正在对 AOF 文件进行写入时停机,如果停机造成了 AOF 文件出错(corrupt),那么 Redis 在重启时会拒绝载入 AOF 文件,从而确保数据的一致性不会被破坏
  • 当发生这种情况时,可以用以下方法类修复出错的 AOF 文件:
- 为现有的 AOF 文件创建一个备份
- 使用 Redis 附带的 redis-check-aof 程序,对原来的 AOF 文件进行修复:
	redis-check-aof --fix readonly.aof
- 重启 Redis 服务器,等服务器载入修复后的 AOF 文件,并进行数据恢复

如何选择RDB和AOF

  • 一般来说,如果对数据的安全性要求非常高的话,应该同时使用两种持久化功能
  • 如果可以承受数分钟以内的数据丢失,那么可以只使用 RDB 持久化
  • 有很多用户都只使用 AOF 持久化,但并不推荐这种方式 :因为定时生成 RDB 快照非常便于进行数据库备份,并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快
# 禁止RDB方式
save ""
  • 两种持久化策略可以同时使用,也可以使用其中一种。如果同时使用的话,那么Redis重启时,会优先使用 AOF 文件来还原数据。

混合持久化方式

  • Redis 4.0之后新增的方式,混合方式是结合了RDB和AOF的优点,在写入的时候,先把当前的数据已RDB形式写入文件的开头,再将后续的操作命令以AOF的格式存入文件,这样既保证了Redis重启时的速度,又能降低数据丢失的风险。
  • RDB和AOF持久化各有利弊,RDB可能会导致一定时间内的数据丢失,而AOF由于文件大,则会影响Redis的启动速度,为了能同时拥有RDB和AOF的优点,Redis 4.0之后新增了混合持久化方式,因此我们必须要进行持久化操作时,应该选择混合持久化方式。
  • 查询是否开启了混合持久化可以使用的命令:
127.0.0.1:6379> config get aof-use-rdb-preamble
1) "aof-use-rdb-preamble"
2) "yes"
  • 其中 yes 表示已经开启混合持久化,no 表示关闭,Redis 5.0 默认值是 yes。如果是其他版本的 Redis,首先需要检查一下,是否开启了混合持久化,如果关闭情况下,可以通过以下两种方式开启:

命令行方式

127.0.0.1:6379> config set aof-use-rdb-preamble yes
  • 命令行设置的缺点是重启 Redis 服务后,设置的配置就会失效。

修改Redis配置文件方式

  • 在 Redis 的根路径下找到 redis.conf 文件,把配置文件中的 aof-use-rdb-preamble no 改为 aof-use-rdb-preamble yes
  • 配置完成后,需要重启 Redis 服务器,配置才能生效,但修改配置文件的方式,在每次重启Redis服务器之后,配置信息不回丢失。
  • 需要注意的是,在非必须进行持久化的业务中,可以关闭持久化,这样可以有效提升 Redis 的运行速度,不会出现间歇性卡顿的困扰。
  • 混合持久化:RDB+指令。
  • 如果 AOF 文件过大时,会重写:把当前数据以 RDB 格式保存,后续指令用 AOF。