这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战
前言
本文主要介绍Redis的使用场景、事务机制和持久化机制
往期推荐:
一. Redis 常见的使用场景
1.1 用于缓存热点数据
缓存是Redis 使用最频繁的场景,由于Redis读写性能的优异,慢慢成为服务端缓存组件的首选
缓存的处理逻辑:
简单来说,就是读取数据的时候,先读Redis,若是Redis不存在数据,则读数据库,并将数据放入缓存中;但不管是先写MySQL数据库,再删除Redis缓存;还是先删除缓存,再写库,都有可能出现数据不一致的情况
1.2 用于限时业务
由于Redis可以为Key设置过期时间,所以很多有时限的东西都可以用Redis来实现
比如经典的短信验证码,用户提交验证码后,查询key是否已经过期了,过期了这个验证码就算失效了
1.3 作为业务的计数器
Redes的单线程模型保证了在Redis中操作数据是线程安全的,还有命令可以实现原子性的递增和递减
可以运用于高并发的秒杀活动、分布式序列号的生成、具体业务还体现在比如限制一个手机号发多少条短信、一个接口一分钟限制多少请求、一个接口一天限制调用多少次等等
再比如也可以给文章统计访问量、点赞数量
1.4 分布式锁
主要利用Redis 的 setnx 命令实现,如果不存在则成功设置缓存同时返回1,否则返回0,在Java中,一般用 Redission 来实现分布式锁
分布式锁常用于秒杀等场景
1.5 点赞,收藏,好友、关注等相互关系的存储
Redis 利用集合的一些命令,比如求交集、并集、差集等
在应用中,每个用户关注的人存在一个集合中,就很容易实现求两个人的共同好友功能
二. Redis中的事务
2.1 事务相关的命令
MULTI 、 EXEC 、 DISCARD 和 WATCH 是 Redis 事务相关的命令。事务可以一次执行多个命令, 并且带有以下两个重要的保证:
- 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
MULTI:开启一个事务,返回OK,此时用户可以按顺序输入命令,这些命令会被放入一个队列,而不会立即执行
EXEC:提交事务队列,事务中的命令会被按顺序执行
DISCARD:放弃事务
WATCH:使用乐观锁监控key,在同一个客户端中,假如一个key在事务开启前被WATCH标记,事务开启后如果这个值被其他客户端修改,事务将执行失败
例:
> MULTI
OK
> SET NUMBER 1 -- 设置一个Key初始值是1
QUEUED
> INCR NUMBER -- 自增这个key
QUEUED
> EXEC -- 执行事务
OK
2
2.2 为什么Redis中的事务不支持回滚?
Redis官方认为,在使用Redis的过程中,Redis只会因为错误的语法而失败,或是命令用在了错误类型的键上面如果你本来想通过 INCR 命令将键的值加上 1 , 却不小心加上了 2 , 又或者对错误类型的键执行了 INCR , 回滚是没有办法处理这些情况的;因此,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中
此外,因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速
三. Redis的持久化机制
3.1 为什么需要持久化
Redis 是一个基于内存的数据库,假如服务器宕机了,内存中的数据将全部丢失。假如这些缓存由系统直接从数据库重新拉取恢复,不仅会给数据库带来大量的压力,而且会造成系统响应缓慢。所以,对于Redis来说,实现数据的持久化从而避免给数据库加压
3.2 RDB
RDB 就是 Redis DataBase 的缩写,中文名为快照/内存快照,RDB持久化是把当前进程数据生成快照保存到磁盘上的过程,由于是某一时刻的快照,那么快照中的值要早于或者等于内存中的值
RDB允许用户手动触发,也允许配置为自动触发
save:阻塞当前Redis服务器,直到RDB过程完成为止,对于内存 比较大的实例会造成长时间阻塞,线上环境不建议使用
bgsave:Redis进程执行fork操作创建子进程,RDB持久化过程由子 进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短
> save
OK
> bgsave
Background saving started
3.2.1 RDB持久化的配置
在生产环境,不可能由开发人员时不时的上线操作备份,所以会设置成周期执行
可以通过配置redis.conf来开启RDB
# 周期性执行条件的设置格式为
save <seconds> <changes>
# 例子
save 900 1 #如果900秒内有1条Key信息发生变化,则进行快照,可以根据实际压力调整
# 以下设置方式为关闭RDB快照功能
save ""
# ------- 分割线---------
#其他配置
# 文件名称
dbfilename dump.rdb
# 文件保存路径
dir /data/redis/data/
# 如果持久化出错,主进程是否停止写入
stop-writes-on-bgsave-error yes
# 是否压缩
rdbcompression yes
# 导入时是否检查
rdbchecksum yes
3.2.2 RDB优缺点
优点
- RDB文件是某个时间节点的快照,默认使用LZF算法进行压缩,压缩后的文件体积远远小于内存大小,适用于备份、全量复制等场景;
- Redis加载RDB文件恢复数据要远远快于AOF方式;
缺点
- RDB方式实时性不够,无法做到秒级的持久化;
- 每次调用bgsave都需要fork子进程,fork子进程属于重量级操作,频繁执行成本较高;
- RDB文件是二进制的,没有可读性,AOF文件在了解其结构的情况下可以手动修改或者补全;
- 版本兼容RDB文件问题;
针对RDB不适合实时持久化的问题,Redis提供了AOF持久化方式来解决
3.3 AOF 持久化
AOF(Append Only File)是指将所有对数据库进行过写入的命令(及其参数)记录到 AOF 文件,以此达到记录数据库状态的目的
3.3.1 AOF持久化配置
默认情况下,Redis是没有开启AOF的,可以通过配置redis.conf文件来开启AOF持久化
# appendonly参数开启AOF持久化
appendonly yes
# AOF持久化的文件名,默认是appendonly.aof
appendfilename "appendonly.aof"
# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的
dir ./
# 同步策略
appendfsync always
3.3.2 回写策略
对于 appendfsync,Redis提供了三个参数:
appendfsync always每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度appendfsync everysec每秒钟同步一次,显示地将多个写命令同步到硬盘appendfsync no让操作系统决定何时进行同步
为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec 选项 ,让 Redis 每秒同步一次 AOF 文件,Redis 性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis 还会优雅的放慢自己的速度以便适应硬盘的最大写入速度
3.3.3 AOF重写
随着AOF记录的命令越来越多,文件也会越来越大,为了解决这个问题,Redis通过重写来为AOF文件“减重”
Redis通过创建一个新的AOF文件来替换现有的AOF,新旧两个AOF文件保存的数据相同,但新AOF文件没有了冗余命令
重写执行的过程:
- AOF重写过程是由后台进程BGREWRITEAOF 来完成的,主线程fork出后台的BGREWRITEAOF 子进程,fork会把主线程的内存拷贝一份给BGREWRITEAOF 子进程
- 在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令
- 当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧两个 AOF 文件所保存的数据库状态一致
- 服务器用新的 AOF 文件替换旧的 AOF 文件,AOF重写完成