事物
基础
Redis 事物本质 : 一组命令的集合! 一个事物中的所有命令都会被序列化,在事物执行过程中,会安照顺序执行
一次性 顺序性 排他性
------- 队列 set set set 执行 ---------
1. Redis 事物没有隔离级别的概念
2. 所有的命令在事物中,并不执行! 当发起执行(exec)命令时才执行
3. Redis 单条命令保证原子性(要么完整的被执行,要么完全不执行),但是事物不保证
过程
# 开启事物
multi
# 命令入队 .....
# 执行事物
exec
# 取消事物 队列中的所有命令都不会执行
discard
命令出错
# 编译时异常 命令有语法错误 执行事物会报错
队列中的所有命令都不会执行
# 运行时异常 逻辑错误 出错的命令报异常
队列中的其他命令都会执行
锁
悲观锁
无论什么时候都会出问题, 都上锁
乐观锁
无论什么时候都不会出问题, 都不上锁
实现
# 监听 在事物中添加 当监听的 key 的值改变时,事物会失败
watch key
# 放弃监视
UNwatch key
Jedis
maven
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
</dependencies>
Java
// 获取Redis对象
Jedis jedis = new Jedis("192.168.182.132",6379);
//所有方法和命令一样
jedis.XXX
//事物
Transaction multi = jedis.multi;
multi.set("name","fang");
multi.exec;
//关闭连接
jedis.close;
SpringBoot集成
配置
maven
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
源码
RedisAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
// redisTemplate --> Redis 操作模板, 放<Object, Object>
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
// StringRedisTemplate --> Redis 操作模板, 专用于 String 存放
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
RedisProperties.class
/*
* 配置类
*/
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
// 默认 0 号数据库
private int database = 0;
//
private String url;
//
private String host = "localhost";
//
private String password;
// 默认 6379 端口
private int port = 6379;
//
private boolean ssl;
//
private Duration timeout;
使用
@Test
void test1(){
// 操作字符串
redisTemplate.opsForValue().set("fang","男");
String fang = (String) redisTemplate.opsForValue().get("fang");
// redisTemplate.opsForXXX -- 找到对应数据类型即可
redisTemplate.opsForList();
redisTemplate.opsForHash();
redisTemplate.opsForSet();
// .........
}
常用
@Test
void test(){
// 删除
redisTemplate.delete("fang");
// 事物
redisTemplate.discard();
redisTemplate.exec();
redisTemplate.multi();
// 过期时间
redisTemplate.expireAt("fang",new Date(new Date().getTime() + 100));
// 移动
redisTemplate.move("fang",5);
}
Redis.conf
杂
# units are case insensitive so 1GB 1Gb 1gB are all the same.
# 大小写不敏感
# 可以包涵其他配置文件
include /path/to/local.conf
# 绑定 ip , 默认为 127.0.0.1 只允许本机访问
bind 127.0.0.1
# 是否受保护
protected-mode yes
# 端口
port 6379
# 是否为守护进程 默认是No 一般改为 yes
daemonize no
# 配置文件 如果以后台方式运行 需指定一个 pid 文件
pidfile /var/run/redis_6379.pid
# 日志
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice
# 生成的文件名
logfile ""
# 默认的数据库数量
databases 16
# 是否总是显示 logo
always-show-logo yes
持久化
# 900s 内 如果有 1 个 key 进行修改 进行持久化操作
save 900 1
# 300 内 如果有 10 个 key 进行修改 进行持久化操作
save 300 10
# 60 内 如果有 10000 个 key 进行修改 进行持久化操作
save 60 10000
# 持久化失败 是否继续工作
stop-writes-on-bgsave-error yes
# 是否压缩 rdb 文件
rdbcompression yes
#是否校验 rdb 文件
rdbchecksum yes
# rdb 文件文件名
dbfilename dump.rdb
# 文件保存目录 ./ --> 当前目录
dir ./
安全
# 设置密码
requirepass ......(写密码)
主从
客户端
# 默认的客户端连接数
maxclients 10000
内存
# 内存容量
maxmemory <bytes>
# 内存超过策略
maxmemory-policy noeviction
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)
2、allkeys-lru : 删除lru算法的key
3、volatile-random:随机删除即将过期key
4、allkeys-random:随机删除
5、volatile-ttl : 删除即将过期的
6、noeviction : 永不过期,返回错误
AOF(APPEND ONLY)
# 默认关闭
appendonly no
# 持久化文件名字
appendfilename "appendonly.aof"
# 同步
appendfsync always # 每次修改都会 sync
appendfsync everysec # 每 1 秒执行 sync
appendfsync no # 不执行 sync 由操作系统进行 sync
持久化
RDB
RDB是Redis用来进行持久化的一种方式,是把当前内存中的数据集快照写入磁盘,也就是 Snapshot 快照(数据库中所有键值对数据)。恢复时是将快照文件直接读到内存里。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-POmQ1PkE-1604127485157)(…\img\Redis-RDB.jpg)]
触发
自动触发
# 配置文件中
save
save 900 1:表示900 秒内如果至少有 1 个 key 的值变化,则保存
save 300 10:表示300 秒内如果至少有 10 个 key 的值变化,则保存
save 60 10000:表示60 秒内如果至少有 10000 个 key 的值变化,则保存
stop-writes-on-bgsave-error
默认值为yes。当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。
rdbcompression
默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。
rdbchecksum
默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验
dbfilename
设置快照的文件名,默认是 dump.rdb
dir
设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。默认是和当前配置文件保存在同一目录。
手动触发
save
该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。
bgsave
执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体操作是Redis进程执行fork操作
创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。
恢复
将 dump.rdb 放到目录下
优缺点
优势
1.RDB是一个非常紧凑(compact)的文件,它保存了redis 在某个时间点上的数据集。这种文件非常
适合用于进行备份和灾难恢复。
2.生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任
何磁盘IO操作。
3.RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
劣势
1、RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子
进程,属于重量级操作,如果不采用压缩算法(内存中的数据被克隆了一份,大致2倍的膨胀性需要考
虑),频繁执行成本过高(影响性能)
2、RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本
Redis服务无法兼容新版RDB格式的问题(版本不兼容)
3、在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修
改(数据有丢失)
AOF
AOF 则是通过保存Redis服务器所执行的写命令来记录数据库状态。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YxKA9t4s-1604127485160)(…\img\Redis-AOF.png)]
触发
# 配置文件中
appendonly
默认值为no,也就是说redis 默认使用的是rdb方式持久化,如果想要开启 AOF 持久化方式,需要
将 appendonly 修改为 yes。
appendfilename
aof文件名,默认是"appendonly.aof"
appendfsync
aof持久化策略的配置
appendfsync always # 每次修改都会 sync 速度最慢
appendfsync everysec # 每 1 秒执行 sync
appendfsync no # 不执行 sync 由操作系统进行 sync 速度最快
no-appendfsync-on-rewrite
auto-aof-rewrite-percentage
默认100 当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写
auto-aof-rewrite-min-size
默认64mb 设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况
aof-load-truncated
优缺点
优点:
AOF 持久化的方法提供了多种的同步频率,即使使用默认的同步频率每秒同步一次,Redis 最多也就丢失 1 秒的数据而
已。
AOF 文件使用 Redis 命令追加的形式来构造,因此,即使 Redis 只能向 AOF 文件写入命令的片断,使用 redis-
check-aof 工具也很容易修正 AOF 文件。
AOF 文件的格式可读性较强,这也为使用者提供了更灵活的处理方式。例如,如果我们不小心错用了 FLUSHALL 命令,
在重写还没进行时,我们可以手工将最后的 FLUSHALL 命令去掉,然后再使用 AOF 来恢复数据。
缺点:
对于具有相同数据的的 Redis,AOF 文件通常会比 RDF 文件体积更大。
虽然 AOF 提供了多种同步的频率,默认情况下,每秒同步一次的频率也具有较高的性能。但在 Redis 的负载较高时,
RDB 比 AOF 具好更好的性能保证。
RDB 使用快照的形式来持久化整个 Redis 数据,而 AOF 只是将每次执行的命令追加到 AOF 文件中,因此从理论上说,
RDB 比 AOF 方式更健壮。官方文档也指出,AOF 的确也存在一些 BUG,这些 BUG 在 RDB 没有存在。
选择
如果可以忍受一小段时间内数据的丢失,毫无疑问使用 RDB 是最好的,定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快,而且使用 RDB 还可以避免 AOF 一些隐藏的 bug;否则就使用 AOF 重写。
但是一般情况下建议不要单独使用某一种持久化机制,而是应该两种一起用,在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。Redis后期官方可能都有将两种持久化方式整合为一种持久化模型。
RDB-AOF混合持久化
# 配置文件
aof-use-rdb-preamble
设置为yes表示开启,设置为no表示禁用。
当开启混合持久化时,主进程先fork出子进程将现有内存副本全量以RDB方式写入aof文件中,然后将缓冲区中的增量命令
以AOF方式写入aof文件中,写入完成后通知主进程更新相关信息,并将新的含有 RDB和AOF两种格式的aof文件替换旧的
aof文件。
简单来说:混合持久化方式产生的文件一部分是RDB格式,一部分是AOF格式。
使用 RDB 是最好的,定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快,而且使用 RDB 还可以避免 AOF 一些隐藏的 bug;否则就使用 AOF 重写。
但是一般情况下建议不要单独使用某一种持久化机制,而是应该两种一起用,在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。Redis后期官方可能都有将两种持久化方式整合为一种持久化模型。
## RDB-AOF混合持久化
```shell
# 配置文件
aof-use-rdb-preamble
设置为yes表示开启,设置为no表示禁用。
当开启混合持久化时,主进程先fork出子进程将现有内存副本全量以RDB方式写入aof文件中,然后将缓冲区中的增量命令
以AOF方式写入aof文件中,写入完成后通知主进程更新相关信息,并将新的含有 RDB和AOF两种格式的aof文件替换旧的
aof文件。
简单来说:混合持久化方式产生的文件一部分是RDB格式,一部分是AOF格式。