【Redis】内存数据库 Redis 基础

news2024/11/25 8:16:38

目录

    • 内存数据库Redis概念
    • Redis 安装
      • Redis的启动方式
      • Redis命令行客户端
    • Redis通用命令
    • Redis key结构
    • Redis value数据类型
      • String 和基础操作
      • Hash 和基础操作
      • List 和基础操作
      • Set 和基础操作
      • Sorted_set 和基础操作
    • Redis的Java客户端
      • Jedis客户端
      • SpringDataRedis客户端
        • 自定义RedisTemplate的序列化方式
        • StringRedisTemplate 统一使用 String 序列化器
    • Redis 持久化
      • RDB (Redis DataBase)
        • RDB启动方式——save指令
          • save指令相关配置
          • save指令工作原理
          • save配置自动执行
        • RDB启动方式——bgsave指令
          • bgsave指令相关配置
          • bgsave指令工作原理
        • RDB三种启动方式对比
        • RDB特殊启动形式
        • RDB优点与缺点
      • AOF (Append Only File)
        • AOF写数据三种策略 (appendfsync)
        • AOF相关配置
        • AOF工作流程
        • AOF重写
          • AOF重写作用
          • AOF重写规则
          • AOF重写方式
          • AOF重写流程
      • RDB (Redis DataBase) VS AOF (Append Only File)

内存数据库Redis概念

  Redis (Remote DIctinary Server) 是用C语言开发的一个开源的高性能键值对(key-value)数据库。特征:

  • 数据间没有必然的关联关系

  • 内部采用单线程机制进行工作

  • 高性能。官方提供测试数据,50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s。

  • 多数据类型支持:string(字符串类型)、list(列表类型)、hash(散列类型)、set(集合类型)、sorted_set(有序集合类型)

  • 持久化支持。可以进行数据灾难恢复

Redis 安装

# 解压缩:
tar -xzf redis-6.2.6.tar.gz
cd redis-6.2.6

# 运行编译命令
make && make install
# 默认的安装路径是在 `/usr/local/bin`目录下。

/usr/local/bin目录已经默认配置到环境变量,因此可以在任意目录下运行这些命令。

  • redis-cli:是redis提供的命令行客户端
  • redis-server:是redis的服务端启动脚本
  • redis-sentinel:是redis的哨兵启动脚本

Redis的启动方式

  • 默认启动
  • 指定配置启动
  • 开机自启

默认启动

# 在任意目录输入redis-server命令即可启动Redis
redis-server
# 这种启动属于`前台启动`,会阻塞整个会话窗口,窗口关闭或者按下`CTRL + C`则Redis停止。不推荐使用。

指定配置启动

# 让Redis以 后台 方式启动,则必须修改Redis配置文件,/usr/local/src/redis-6.2.6/redis.conf
cp redis.conf redis.conf.bck

vi redis.conf
# 修改redis.conf文件中的一些配置:
# 允许访问的地址,默认是127.0.0.1,会导致只能在本地访问。修改为0.0.0.0则可以在任意IP访问,生产环境不要设置为0.0.0.0
bind 0.0.0.0
# 守护进程,修改为yes后即可后台运行
daemonize yes 
# 密码,设置后访问Redis必须输入密码
requirepass 123456


# Redis的其它常见配置:
# 监听的端口
port 6379
# 工作目录,默认是当前目录,也就是运行redis-server时的命令,日志、持久化等文件会保存在这个目录
dir .
# 数据库数量,设置为1,代表只使用1个库,默认有16个库,编号0~15
databases 1
# 设置redis能够使用的最大内存
maxmemory 512mb
# 日志文件,默认为空,不记录日志,可以指定日志文件名
logfile "redis.log"
# 启动Redis
# 进入redis安装目录 
cd /usr/local/src/redis-6.2.6
# 启动
redis-server redis.conf

# 利用redis-cli来执行 shutdown 命令,即可停止 Redis 服务,
# 因为之前配置了密码,因此需要通过 -u 来指定密码
redis-cli -u 123456 shutdown

开机自启

# 新建一个系统服务文件:
vi /etc/systemd/system/redis.service
[Unit]
Description=redis-server
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/bin/redis-server /usr/local/src/redis-6.2.6/redis.conf
PrivateTmp=true

[Install]
WantedBy=multi-user.target
# 重载系统服务
systemctl daemon-reload

# 启动
systemctl start redis
# 停止
systemctl stop redis
# 重启
systemctl restart redis
# 查看状态
systemctl status redis

# 让redis开机自启
systemctl enable redis

Redis命令行客户端

Redis安装完成后就自带了命令行客户端:redis-cli

redis-cli [options] [commonds]

其中常见的options有:

  • -h 127.0.0.1:指定要连接的redis节点的IP地址,默认是127.0.0.1
  • -p 6379:指定要连接的redis节点的端口,默认是6379
  • -a 123321:指定redis的访问密码

其中的commonds就是Redis的操作命令,例如:

  • ping:与redis服务端做心跳测试,服务端正常会返回pong

不指定commonds时,会进入redis-cli的交互控制台。

Redis 默认有16个仓库,编号从0至15。通过配置文件可以设置仓库数量,但是不超过16,并且不能自定义仓库名称。通过 select 命令来选择数据库:

# 选择 0号库
select 0

Redis通用命令

通用指令是部分数据类型的,都可以使用的指令,常见的有:

  • keys:查看符合模板的所有 key
  • del:删除一个指定的 key
  • exists:判断 key 是否存在
  • expire:给一个 key 设置有效期,有效期到期时该 key 会被自动删除
  • ttl:查看一个 key 的剩余有效期

通过help [command] 可以查看一个命令的具体用法,例如:

# 查看keys命令的帮助信息:
127.0.0.1:6379> help keys

KEYS pattern
summary: Find all keys matching the given pattern
since: 1.0.0
group: generic

Redis key结构

Redis 没有类似 MySQL 中的Table的概念,如何区分不同类型的key呢?

例如,需要存储用户、商品信息到redis,有一个用户id是1,有一个商品id恰好也是1,此时如果使用id作为key,那就会冲突了。

可以通过给key添加前缀加以区分,不过这个前缀不是随便加的,有一定的规范:

Redis 的 key 允许有多个单词形成层级结构,多个单词之间用':'隔开,格式如下:

	项目名:业务名:类型:id

这个格式并非固定,也可以根据需求来删除或添加词条。这样就把不同类型的数据区分开了。从而避免了key的冲突问题。

例如项目名称叫 koma,有 user 和 product 两种不同类型的数据,可以这样定义key:

  • user相关的key:koma:user:1

  • product相关的key:koma:product:1

如果 Value 是一个Java对象,例如一个User对象,则可以将对象序列化为JSON字符串后存储:

KEYVALUE
koma:user:1{“id”:1, “name”: “Jack”, “age”: 21}
koma:product:1{“id”:1, “name”: “小米11”, “price”: 4999}

并且,在Redis的桌面客户端中,还会以相同前缀作为层级结构,让数据看起来层次分明,关系清晰。

Redis value数据类型

  数据类型指的是存储的数据的类型,也就是 value 部分的类型,key 部分永远都是字符串。

在这里插入图片描述

String 和基础操作

  String在 redis 内部存储默认就是一个字符串,当遇到增减类操作 incr , decr 时会转成数值型进行计算。value是字符串,根据字符串的格式不同,又可以分为3类:

  • string:普通字符串
  • int:整数类型,可以做自增、自减操作
  • float:浮点类型,可以做自增、自减操作

  底层都是字节数组形式存储,只不过是编码方式不同。字符串类型的最大空间不能超过512m。
  Redis 所有的操作都是原子性的,采用单线程处理所有业务,命令是一个一个执行的,因此无需考虑并发带来的数据影响。

# 信息添加
set key value

# 信息查询
get key

# 删除数据
del key

# 批量添加多个String类型的键值对
mset key1 value1 key2 value2 …

# 根据多个key获取多个String类型的value
mget key1 key2 …

# 获取数据字符个数(字符串长度)
strlen key

# 追加信息到原始信息后部(如果原始信息存在就追加,否则新建)
append key value

# 帮助命令:获取命令帮助文档,获取组中所有命令信息名称
help 命令名称
help @组名

在这里插入图片描述

# 设置数值数据增加指定范围的值
incr key # 让一个整型的key自增1
incrby key increment # 让一个整型的key自增并指定步长
incrbyfloat key increment # 让一个浮点类型的数字自增并指定步长
# 设置数值数据减少指定范围的值
decr key
decrby key increment

:按数值进行操作的数据,如果原始数据不能转成数值,或超过了 redis 数值上限范围,将会报错。9223372036854775807 ( java中long型数据最大值,Long.MAX_VALUE)

String 数据时效性设置
设置数据具有指定的声明周期

# psetex 和 setex 作用一样,都是设置过期时间,区别是:psetex 时间单位是毫秒,setex 是秒
setex key seconds value # 添加一个String类型的键值对,并且指定有效期
psetex key milliseconds value
# setex key1 10 123
# get key1 ————> "123"
# get key1 ————> (nil)

# setnx:添加一个String类型的键值对,前提是这个key不存在,否则不执行

String 类型的注意事项

  • 数据操作不成功的反馈与数据正常操作之间的差异
  • 表示运行结果是否成功:(integer) 0 ——>false 失败,(integer) 1 ——> true 成功
  • 表示运行结果值:(integer) 3 ——> 3 3个,(integer) 1 ——> 1 1个
  • 数据未获取到 (nil):等同于null
  • 数据最大存储量: 512MB
  • 数值计算最大范围:(java中的long的最大值)

Hash 和基础操作

  String 结构是将对象序列化为 JSON 字符串后存储,当需要修改对象某个字段时很不方便,Hash 结构可以将对象中的每个字段独立存储,可以针对单个字段做 CRUD。
在这里插入图片描述

# 添加/修改数据
hset key field value # 添加或者修改hash类型key的field的值

# 获取数据
hget key field # 获取一个hash类型key的field的值
hgetall key # 获取一个hash类型的key中的所有的field和value

# 删除数据
hdel key field1 [field2]

# 添加/修改多个数据
hmset key field1 value1 field2 calue2 #批量添加多个hash类型key的field的值

# 获取多个数据
hmget key field1 field2 … # 批量获取多个hash类型key的field的值
 
# 获取哈希表中字段的数量
hlen key

# 获取哈希表中是否存在指定的字段
hexists key field

# 获取哈希表中所有的字段名和字段值
hkeys key # 获取一个hash类型的key中的所有的field
hvals key

# 设置指定字段的数值数据增加指定范围的值
hincrby key field increment # 让一个hash类型key的字段值自增并指定步长
hincrbyfloat key field increment

# hsetnx:添加一个hash类型的key的field值,前提是这个field不存在,否则不执行
  • hash 类型下的 value 只能存储字符串,不允许存储其他类型数据,不存在嵌套现象。如果数据未获取到,对应的值为 (nil)。
  • 每个 hash 可以存储 232-1 个键值对。
  • hash 类型十分贴近对象的数据存储形式,并且可以灵活添加删除对象属性。但 hash 设计初中不是为了存储大量对象而设计的,切记不可滥用,更不可以将 hash 作为对象列表使用。
  • hgetall 操作可以获取全部属性,如果内部 field 过多,遍历整体数据效率就会很低,有可能成为数据访问瓶颈。
    在这里插入图片描述

List 和基础操作

  Redis 中的 List 类型与 Java 中的 LinkedList 类似,可以看做是一个双向链表结构。既可以支持正向检索和也可以支持反向检索。特征也与 LinkedList 类似:

  • 有序
  • 元素可以重复
  • 插入和删除快
  • 查询速度一般

  Redis 应用于具有操作线后顺序的数据控制。常用来存储一个有序数据,例如:朋友圈点赞列表,评论列表等。

# 添加/修改数据
lpush key value1 [value2]# 向列表左侧插入一个或多个元素
rpush key value1 [value2]# 向列表右侧插入一个或多个元素

# 获取数据
lrange key start stop # 返回一段角标范围内的所有元素
lindex key index
llen key

# 删除并移除数据
lpop key # 移除并返回列表左侧的第一个元素,没有则返回nil
rpop key # 移除并返回列表右侧的第一个元素

# 规定时间内获取并移除数据 (阻塞式获取,获取值如果还没有的时候可以等,如果有值就可以获取到)
blpop key1 [key2] timeout
brpop key1 [key2] timeout

# 移除指定数据
lrem key count value

在这里插入图片描述

  • list 中保存的数据都是string类型的,数据总容量是有限的,最多 232-1 个元素。(4294967295)
  • list具有索引的概念,但是操作数据时候通常以队列的形式进行入队出队操作,或以栈的形式进入栈出栈的操作。
  • 获取全部数据操作结束索引设置为-1。
  • list 可以对数据进行分页操作,通过第一页的信息来自list,第2页及更多的信息通过数据库的形式加载。

Set 和基础操作

  Redis 的 Set 结构与 Java 中的 HashSet 类似,可以看做是一个 value 为 null 的 HashMap。因为也是一个 Hash 表,因此具备与 HashSet 类似的特征:

  • 无序

  • 元素不可重复

  • 查找快

  • 支持交集、并集、差集等功能

set类型:与 Hash 存储结构完全相同,仅存储键,不存储值( nil ) , 并且值是不允许重复的。

# 添加数据
sadd key menber1 [member2] # 向set中添加一个或多个元素

# 获取全部数据
smembers key

# 删除数据
srem key member1 [member2] # 移除set中的指定元素

# 获取集合数据总量
scard key

# 判断集合中是否包含指定数据
sismember key member

# 随机获取集合中指定数量的数据
srandmember key [count]

# 随机获取集合中的某个数据并将该数据移出集合
spop key [count]

# 求两个集合的交、并、差集
sinter key1 [key2]
sunion key1 [key2]
sdiff key1 [key2]

# 求两个集合的交、并、差集并存储到指定集合中
sinterstore destination key1 [key2]
sunionstore destination key1 [key2]
sdiffstore destination key1 [key2]

# 将指定数据从原始集合移动到目标集合中
smove source destination member

Sorted_set 和基础操作

  Redis 的 SortedSet 是一个可排序的 set 集合,与 Java 中的 TreeSet 有些类似,但底层数据结构却差别很大。SortedSet 中的每一个元素都带有一个 score 属性,可以基于 score 属性对元素排序,底层的实现是一个跳表(SkipList)加 hash表。

SortedSet具备下列特性:

  • 可排序
  • 元素不重复
  • 查询速度快

  因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。
  根据排序有利于数据的有效显示,需要提供一种可以根据自身特征进行排序的方式。Sorted_set类型: 在set的存储结构基础上添加可排序字段。
在这里插入图片描述

# 添加数据,添加一个或多个元素到sorted set ,如果已经存在则更新其score值
zadd key score1 member1 [score2 member2]

# 获取全部数据(start与stop用于限定查询范围,作用于索引,表示开始和结束索引)
zrange key start stop [WITHSCORES] # 按照score排序后,获取指定排名范围内的元素
zrevrange key start stop [WITHSCORES]

# 删除数据,删除sorted set中的一个指定元素
zrem key member [member …]

# 按条件获取数据(min与max用于限定搜索查询的条件)
zrangebyscore key min max [WITHSCORES] [LIMIT] # 按照score排序后,获取指定score范围内的元素
zrevrangebyscore key max min [WITHSCORES]

# 条件删除
zremrangebyrank key start stop
zremrangebyscore key min max

# 获取集合数据总量
zcard key # 获取sorted set中的元素个数
zcount key min max # 统计score值在给定范围内的所有元素的个数

# 集合交、并操作(计算给定的一个或多个有序集的交集,其中给定 key 的数量必须以 numkeys 参数指定,并将该交集(结果集)储存到 destination)
zinterstore destination numkeys key [key …]
zunionstore destination numkeys key [key …]

# offset与count用于限定查询范围,作用于查询结果,表示开始位置和数据总量

# 获取数据对应的索引(排名)
zrank key member # 获取sorted set 中的指定元素的排名
zrevrank key member

# score 值获取与修改
zscore key member # 获取sorted set中的指定元素的score值
zincrby key increment member # 让sorted set中的指定元素自增,步长为指定的increment值

# zdiff、zinter、zunion:求差集、交集、并集

注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可,例如:

  • 升序获取Sorted set 中的指定元素的排名:ZRANK key member

  • 降序获取Sorted set 中的指定元素的排名:ZREVRANK key memeber

在这里插入图片描述

Redis的Java客户端

  • Jedis 和 Lettuce:这两个主要是提供了 Redis 命令对应的 API,方便我们操作 Redis,而 SpringDataRedis 又对这两种做了抽象和封装。
  • Redisson:是在 Redis 基础上实现了分布式的可伸缩的 Java 数据结构,例如 Map、Queue 等,而且支持跨进程的同步机制:Lock、Semaphore等待,比较适合用来实现特殊的功能需求。

Jedis客户端

Redis 官网 Java guide | Redis

引入依赖:

<!--jedis-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.7.0</version>
</dependency>
<!--单元测试-->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.7.0</version>
    <scope>test</scope>
</dependency>

建立连接:

private Jedis jedis;

@BeforeEach 		//用于表示应在当前类中的每个@Test方法之前执行注解方法,@BeforeEach注解的方法不得为静态方法
void setUp() {
    // 1.建立连接
    // jedis = new Jedis("192.168.150.101", 6379); 	//Jedis的直连方式
    jedis = JedisConnectionFactory.getJedis(); 		//Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此推荐使用Jedis连接池代替Jedis的直连方式。
    // 2.设置密码
    jedis.auth("123456");
    // 3.选择库
    jedis.select(0);
}

测试:

@Test
void testString() {
    // 存入数据
    String result = jedis.set("name", "Koma");
    System.out.println("result = " + result);
    // 获取数据
    String name = jedis.get("name");
    System.out.println("name = " + name);
}

@Test
void testHash() {
    // 插入hash数据
    jedis.hset("user:1", "name", "Koma");
    jedis.hset("user:1", "age", "25");

    // 获取
    Map<String, String> map = jedis.hgetAll("user:1");
    System.out.println(map);
}

释放资源:

@AfterEach
void tearDown() {
    if (jedis != null) {
        jedis.close();
    }
}

Jedis连接池:
  Jedis 本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此使用Jedis连接池代替Jedis的直连方式。

import redis.clients.jedis.*;

public class JedisConnectionFactory {
    private static JedisPool jedisPool;
    
    static {
        // 配置连接池
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(8);
        poolConfig.setMaxIdle(8);
        poolConfig.setMinIdle(0);
        poolConfig.setMaxWaitMillis(1000);
        // 创建连接池对象,参数:连接池配置、服务端ip、服务端端口、超时时间、密码
        jedisPool = new JedisPool(poolConfig, "192.168.150.101", 6379, 1000, "123456");
    }

    public static Jedis getJedis(){
        return jedisPool.getResource();
    }
}

SpringDataRedis客户端

  Spring官网 Spring Data Redis

  SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis。

  • 提供了对不同Redis客户端的整合(Lettuce 和 Jedis)
  • 提供了 RedisTemplate 统一 API 来操作 Redis
  • 支持 Redis 的发布订阅模型
  • 支持 Redis 哨兵和 Redis 集群
  • 支持基于 Lettuce 的响应式编程
  • 支持基于JDK、JSON、字符串、Spring 对象的数据序列化及反序列化
  • 支持基于Redis的 JDKCollection 实现

  SpringDataRedis 中提供了 RedisTemplate 工具类,其中封装了各种对 Redis 的操作。并且将不同数据类型的操作 API 封装到了不同的类型中:

API返回值类型说明
redisTemplate.opsForValue()ValueOperations操作String类型数据
redisTemplate.opsForHash()HashOperations操作Hash类型数据
redisTemplate.opsForList()ListOperations操作List类型数据
redisTemplate.opsForSet()SetOperations操作Set类型数据
redisTemplate.opsForZSet()ZSetOperations操作SortedSet类型数据
redisTemplate通用的命令

引入依赖:

 <dependencies>
        <!--redis依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--common-pool-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <!--Jackson依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </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>
        </dependency>
    </dependencies>

配置Redis:

spring:
  redis:
    host: 192.168.150.101
    port: 6379
    password: 123456
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: 100ms

注入RedisTemplate:

// SpringBoot的自动装配
@SpringBootTest
class RedisStringTests {

    @Autowired
    private RedisTemplate redisTemplate;
}

测试:

@SpringBootTest
class RedisStringTests {

    @Autowired
    private RedisTemplate edisTemplate;

    @Test
    void testString() {
        // 写入一条String数据
        redisTemplate.opsForValue().set("name", "Koma");
        // 获取string数据
        Object name = stringRedisTemplate.opsForValue().get("name"); 
        // RedisTemplate可以接收任意 Object 作为值写入 Redis;写入前会把 Object 序列化为字节形式,默认是采用 JDK 序列化,eg: \xAC\xED\x05t\x00\x06\xE6...
        System.out.println("name = " + name);
    }
}


RedisTemplate可以接收任意 Object 作为值写入 Redis;写入前会把 Object 序列化为字节形式,默认是采用 JDK 序列化。eg: \xAC\xED\x05t\x00\x06\xE6…

自定义RedisTemplate的序列化方式

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
        // 创建RedisTemplate对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置连接工厂
        template.setConnectionFactory(connectionFactory);
        // 创建JSON序列化工具
        GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // 设置Key的序列化
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        // 设置Value的序列化
        template.setValueSerializer(jsonRedisSerializer);       // 这里采用了JSON序列化来代替默认的JDK序列化方式。
        template.setHashValueSerializer(jsonRedisSerializer);   // 这里采用了JSON序列化来代替默认的JDK序列化方式。
        // 返回
        return template;
    }
}
//redis中记录为:
{
	"@class":"com.xx.xx.xx.User",
	"name":"koma",
	"age","25"
}

  整体可读性有了很大提升,并且能将 Java 对象自动的序列化为 JSON 字符串,并且查询时能自动把 JSON 反序列化为 Java 对象。不过,其中记录了序列化时对应的 class 名称,目的是为了查询时实现自动反序列化。这会带来额外的内存开销。

StringRedisTemplate 统一使用 String 序列化器

  为了节省内存空间,template.setHashValueSerializer(jsonRedisSerializer)可以不使用 JSON 序列化器来处理 value,而是统一使用 String 序列化器,要求只能存储 String 类型的 key 和 value。当需要存储 Java 对象时,手动完成对象的序列化和反序列化。
在这里插入图片描述
  存入和读取时的序列化及反序列化都是自己实现的,SpringDataRedis 就不会将 class 信息写入 Redis 了。这种用法比较普遍,因此 SpringDataRedis 就提供了 RedisTemplate 的子类:StringRedisTemplate,它的 key 和 value 的序列化方式默认就是 String 方式RedisSerializer.string()

直接使用,省去了自定义 RedisTemplate 的序列化方式的步骤:

@Autowired
private StringRedisTemplate stringRedisTemplate;
// JSON序列化工具
private static final ObjectMapper mapper = new ObjectMapper();

@Test
void testSaveUser() throws JsonProcessingException {
    // 创建对象
    User user = new User("Koma", 25);
    // 手动序列化
    String json = mapper.writeValueAsString(user);
    // 写入数据
    stringRedisTemplate.opsForValue().set("user:1", json);
    // 获取数据
    String jsonUser = stringRedisTemplate.opsForValue().get("user:1");
    // 手动反序列化
    User user1 = mapper.readValue(jsonUser, User.class);
    System.out.println("user1 = " + user1);
}

Redis 持久化

  防止数据的意外丢失,确保数据安全性,利用永久性存储介质将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持久化。

  持久化过程保存什么:

  • 将当前数据状态进行保存,快照形式,存储数据结果,存储格式简单,关注点在数据
  • 将数据的操作过程进行保存,日志形式,存储操作过程,存储格式复杂,关注点在数据的操作过程
    在这里插入图片描述

RDB (Redis DataBase)

  在进行 RDB 的时候,Redis 的主线程是不会做 IO 操作的,主线程会 fork 一个子线程来完成该操作;Redis 调用 fork,同时拥有父进程和子进程。子进程将数据集写入到一个临时 RDB 文件中。当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。
  这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益,因为是使用子进程进行写操作,而父进程依然可以接收来自客户端的请求。
在这里插入图片描述

RDB启动方式——save指令

# 手动执行一次保存操作
save

redis-cli -p 6379
127.0.0.1:6379> 
127.0.0.1:6379> save
OK
# 在redis安装目录的data目录生成dump.rdb文件
save指令相关配置
  • dbfilename dump.rdb
    说明:设置本地数据库文件名,默认值为 dump.rdb
    经验:通常设置为dump-端口号.rdb
  • dir
    说明:设置存储.rdb文件的路径。
    经验:通常设置成存储空间较大的目录中,目录名称data
  • rdbcompression yes
    说明:设置存储至本地数据库时是否压缩数据,默认为 yes,采用 LZF 压缩。
    经验:通常默认为开启状态,如果设置为no,可以节省 CPU 运行时间,但会使存储的文件变大(巨大)。
  • rdbchecksum yes
    说明:设置是否进行 rdb 文件格式校验,该校验过程在写文件和读文件过程均进行。
    经验:通常默认为开启状态,如果设置为 no,可以节约读写性过程约 10% 时间消耗,但是存储一定的数据损坏风险。

在这里插入图片描述
数据恢复演示:

# 关闭redis进程
ps -ef | grep redis-
kill -9 端口号

# 启动redis,观察是否有数据
redis-server conf/redis-6379.conf 
redis-cli -p 6379
>keys *
>"输出" # 关闭前的数据存在,持久化生效
save指令工作原理

在这里插入图片描述

save配置自动执行

  反复执行保存指令,忘记了,不知道数据产生了多少变化,何时保存。由 Redis 服务器发起指令(基于条件)自动执行保存数据。

# 在conf文件中进行配置,重新以配置文件启动,满足限定时间范围内key的变化数量达到指定数量即进行持久化 
# second:监控时间范围,changes:监控key的变化量
save second changes

在这里插入图片描述

RDB启动方式——bgsave指令

  数据量过大,单线程执行方式造成效率过低,需要后台执行( Redis 操作者(用户)发起指令;Redis 服务器控制指令执行)

# 手动启动后台保存操作,但不是立即执行
bgsave
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> set age 25
OK
127.0.0.1:6379> get age
"25"
127.0.0.1:6379> bgsave
Background saving started

# 保存在dump.rdb
bgsave指令相关配置
  • dbfilename dump.rdb
    说明:设置本地数据库文件名,默认值为 dump.rdb
    经验:通常设置为dump-端口号.rdb
  • dir
    说明:设置存储.rdb文件的路径。
    经验:通常设置成存储空间较大的目录中,目录名称data
  • rdbcompression yes
    说明:设置存储至本地数据库时是否压缩数据,默认为 yes,采用 LZF 压缩。
    经验:通常默认为开启状态,如果设置为no,可以节省 CPU 运行时间,但会使存储的文件变大(巨大)。
  • rdbchecksum yes
    说明:设置是否进行 rdb 文件格式校验,该校验过程在写文件和读文件过程均进行。
    经验:通常默认为开启状态,如果设置为 no,可以节约读写性过程约 10% 时间消耗,但是存储一定的数据损坏风险。
  • stop-writes-on-bgsave-error yes
    说明:后台存储过程中如果出现错误现象,是否停止保存操作。
    经验:通常默认为开启状态。
bgsave指令工作原理

在这里插入图片描述

RDB三种启动方式对比

方式save指令bgsave指令
读写同步异步
阻塞客户端指令
额外内存消耗
启动新进程

RDB特殊启动形式

  • 全量复制
    在主从复制中详细讲解

  • 服务器运行过程中重启
    debug reload

  • 关闭服务器时指定保存数据
    shutdown save
    默认情况下执行 shutdown 命令时,自动执行 bgsave( 如果没有开启AOF持久化功能 )

RDB优点与缺点

RDB优点:

  • RDB 是一个紧凑压缩的二进制文件,存储效率较高。
  • RDB 内部存储的是 Redis 在某个时间点的数据快照,非常适合用于数据备份,全量复制等场景。
  • RDB 恢复数据的速度要比 AOF 快很多。
  • 应用:服务器中每x小时执行 bgsave 备份,并将 RDB 文件拷贝到远程机器中,用于灾难恢复。

RDB缺点:

  • RDB 方式无论是执行指令还是利用配置,无法做到实时持久化,具有较大的可能性丢失数据。
  • bgsave 指令每次运行要执行 fork 操作创建子进程,要牺牲掉一些性能。
  • Redis 的众多版本中未进行 RDB 文件格式的版本统一,有可能出现各版本服务之间数据格式无法兼容现象。

AOF (Append Only File)

解决 RDB 存储的弊端:

  • 存储数据量较大,效率较低 基于快照思想,每次读写都是全部数据,当数据量巨大时,效率非常低。
  • 大数据量下的 IO 性能较低。
  • 基于 fork 创建子进程,内存产生额外消耗。
  • 宕机带来的数据丢失风险。

解决思路:

  • 不写全数据,仅记录部分数据。
  • 降低区分数据是否改变的难度,改记录数据为记录操作过程。
  • 对所有操作均进行记录,排除丢失数据的风险。

AOF概念
  AOF (append only file) 持久化:以独立日志的方式记录每次写命令,重启时再重新执行 AOF 文件中命令达到恢复数据的目的。
  与 RDB 相比可以简单描述为改记录数据为记录数据产生的过程。AOF 的主要作用是解决了数据持久化的实时性,目前已经是 Redis 持久化的主流方式。

在这里插入图片描述

AOF写数据三种策略 (appendfsync)

  • always(每次)
    每次写入操作均同步到 AOF 文件中,数据零误差,性能较低。

  • everysec(每秒)
    每秒将缓冲区中的指令同步到 AOF 文件中,数据准确性较高,性能较高。
    在系统突然宕机的情况下丢失 1 秒内的数据。

  • no(系统控制)
    由操作系统控制每次同步到 AOF 文件的周期,整体过程不可控。

AOF相关配置

# AOF持久化文件名,默认文件名未appendonly.aof,建议配置为appendonly-端口号.aof
appendfilename filename

# AOF持久化文件保存路径,与RDB持久化文件保持一致即可
dir

在这里插入图片描述

测试添加数据:

127.0.0.1:6379> set age 55
OK

cat appendonly-6379.aof
...
set
$3
age
$2
55

AOF工作流程

在这里插入图片描述

AOF重写

在这里插入图片描述
  随着命令不断写入 AOF,文件会越来越大,为了解决这个问题,Redis 引入了 AOF 重写机制压缩文件体积。AOF 文件重写是将 Redis 进程内的数据转化为写命令同步到新 AOF 文件的过程。
  简单说就是将对同一个数据的若干个条命令执行结果转化成最终结果数据对应的指令进行记录。

AOF重写作用
  • 降低磁盘占用量,提高磁盘利用率。
  • 提高持久化效率,降低持久化写时间,提高IO性能。
  • 降低数据恢复用时,提高数据恢复效。
AOF重写规则
  • 进程内已超时的数据不再写入文件。
  • 忽略无效指令,重写时使用进程内数据直接生成,这样新的 AOF 文件只保留最终数据的写入命令,如del key1hdel key2srem key3set key4 111set key4 222
  • 对同一数据的多条写命令合并为一条命令,如 lpush list1 alpush list1 blpush list1 c 可以转化为:lpush list1 a b c
  • 为防止数据量过大造成客户端缓冲区溢出,对list、set、hash、zset等类型,每条指令最多写入 64 个元素。
AOF重写方式
  • 手动重写 (bgrewriteaof 指令)
127.0.0.1:6379> set name 111
OK
127.0.0.1:6379> set name 222
OK
[root@koma data]# cat appendonly-6379.aof

127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started

[root@koma data]# cat appendonly-6379.aof

在这里插入图片描述

  • 自动重写
# 自动重写触发条件设置
auto-aof-rewrite-min-size size
auto-aof-rewrite-percentage percentage

# 自动重写触发比对参数(运行指令info Persistence获取具体信息)
aof_current_size
aof_base_size

自动重写触发条件:
在这里插入图片描述

AOF重写流程

在这里插入图片描述

RDB (Redis DataBase) VS AOF (Append Only File)

持久化方式RDB (Redis DataBase)AOF (Append Only File)
占用存储空间小(数据级:压缩)大(指令级:重写)
存储速度
恢复速度
数据安全性会丢失数据依据策略决定
资源消耗高 / 重量级低 / 轻量级
启动优先级
  • 对数据非常敏感,建议使用默认的 AOF 持久化方案
    ​AOF持久化策略使用 everysecond,每秒钟 fsync 一次。该策略 Redis 仍可以保持很好的处理性能,当出现问题时,最多丢失 0-1 秒内的数据。
    ​注意:由于 AOF 文件存储体积较大,且恢复速度较慢。
  • 数据呈现阶段有效性,建议使用 RDB 持久化方案
    ​数据可以良好的做到阶段内无丢失(该阶段是开发者或运维人员手工维护的),且恢复速度较快,阶段 点数据恢复通常采用 RDB 方案。
    ​注意:利用RDB实现紧凑的数据持久化会使Redis降的很低,慎重总结:
  • 综合比对
    RDB 与AOF 的选择实际上是在做一种权衡,每种都有利有弊。
    如不能承受数分钟以内的数据丢失,对业务数据非常敏感,选用AOF。
    ​如能承受数分钟以内的数据丢失,且追求大数据集的恢复速度,选用RDB。
    ​灾难恢复选用RDB。
    ​双保险策略,同时开启 RDB 和 AOF,重启后,Redis优先使用 AOF 来恢复数据,降低丢失数据的量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/819802.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux下 Docker容器引擎基础(1)

简述&#xff1a; Docker的容器技术可以在一台主机上轻松为任何应用创建一个轻量级的、可移植的、自给自足的容器。通过这种容器打包应用程序&#xff0c;意味着简化了重新部署、调试这些琐碎的重复工作&#xff0c;极大的提高了工作效率。例如&#xff1a;项目从腾讯云迁移阿…

机器学习-New Optimization

机器学习(New Optimization) 前言&#xff1a; 学习资料 videopptblog 下面的PPT里面有一些符号错误&#xff0c;但是我还是按照PPT的内容编写公式&#xff0c;自己直到符号表示什么含义就好了 Notation 符号解释 θ t \theta_t θt​第 t 步时&#xff0c;模型的参数 Δ L …

CSS图片放到<div>里面,自适应宽高全部显示,点击图片跳到新页面预览

有一个需求是图片放到一个固定宽高的<div>里面&#xff0c;不管是横图还是竖图&#xff0c;都要全部显示出来并且保持图片的长宽比例不变形&#xff0c;点击图片可以跳到一个新页面预览&#xff0c;代码如下&#xff1a; <!DOCTYPE html> <html> <head>…

第六次作业 密码学

发送者为Alice 接受者为Bob 首先对原始信息进行hash运算得到信息摘要&#xff0c;然后使用发送者Alice私钥进行签名&#xff08;签名的作用是验证该信息是Alice的&#xff09;&#xff0c;然后将原始信息数字签名Alice证书&#xff08;该Alice的证书是由CA组织进行办发的&…

YOLOV8改进:更换PoolFormer主干网络

1.该文章属于YOLOV5/YOLOV7/YOLOV8改进专栏,包含大量的改进方式,主要以2023年的最新文章和2022年的文章提出改进方式。 2.提供更加详细的改进方法,如将注意力机制添加到网络的不同位置,便于做实验,也可以当做论文的创新点。 2.涨点效果:添加PoolFormer主干,有效涨点。 论…

bigemap在工程项目行业里的应用案例

一、工程单位为什么要选择bigemap : 1.地图影像清晰&#xff0c;更新及时 2.能直接用软件做等高线地形图进行投影转换配合cad来使用 3.直接在线下载卫星图和高程节省测绘时间&#xff0c;以及手机端去做数据的采集&#xff0c;对工作有帮助 二、工程单位使用场景&#xff1a; …

Stable Diffusion AI绘画初学者指南【概述、云端环境搭建】

概述、云端环境搭建 Stable Diffusion 是什么、能干啥&#xff1f; 是一种基于深度学习的图像处理技术&#xff0c;可以生成高质量的图像。它可以在不需要真实图像的情况下&#xff0c;通过文字描述来生成逼真的图像。 可以对图像进行修复、超分辨率转换&#xff0c;将低分辨…

可以写进简历的kafka优化-----吞吐量提升一倍的方法

冲突 在看到项目工程里kafka 生产端配置的batch.size为500&#xff0c;而实际业务数据平均有1K大小的时候&#xff1b;我有点懵了。是的&#xff0c;这里矛盾了&#xff1b;莫非之前的作者认为这个batch.size是发送的条数&#xff0c;而不是kafka生产端内存缓存记录的大小&…

【数据结构】手撕排序NO.2----直接插入排序与希尔排序

目录 一. 导入 二. 直接插入排序 2.1 基本思想 2.2 过程分析 2.3 代码实现 2.4 复杂度/稳定性分析 三. 希尔排序(缩小增量排序) 3.1 基本思想 3.2 过程分析 3.3 代码实现 3.4 复杂度/稳定性分析 一. 导入 本期是排序篇的第二期&#xff0c;我们的主角是插入排序。在座的各…

提高电脑寿命的维护技巧与方法分享

在维护电脑运行方面&#xff0c;我有一些自己觉得非常有用的技巧和方法。下面我将分享一些我常用的维护技巧&#xff0c;并解释为什么我会选择这样做以及这样做的好处。 首先&#xff0c;我经常清理我的电脑内部的灰尘。电脑内部的灰尘会影响散热效果&#xff0c;导致电脑发热…

Nodejs 第五章(Npm run 原理)

npm run xxx 发生了什么 按照下面的例子npm run dev 举例过程中发生了什么 读取package json 的scripts 对应的脚本命令(dev:vite),vite是个可执行脚本&#xff0c;他的查找规则是&#xff1a; 先从当前项目的node_modules/.bin去查找可执行命令vite如果没找到就去全局的node…

C++模板初阶学习

目录 1.函数模板1.1函数模板概念1.2函数模板格式1.3函数模板的原理1.4函数模板实例化隐式实例化显示实例化 1.5模板参数适配原则 2.类模板2.1类模板的定义格式2.2类模板实例化 总结 1.函数模板 如何实现一个通用的交换函数呢&#xff1f; #include<iostream> using nam…

软件外包开发的JAVA开发框架

Java的开发框架有很多&#xff0c;以下是一些常见的Java开发框架及其特点&#xff0c;每个框架都有其特定的使用场景和优势&#xff0c;开发者可以根据项目的需求选择合适的框架。今天和大家介绍常见的框架及特点&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&…

Day50 算法记录| 动态规划 17(子序列)

这里写目录标题 647. 回文子串516.最长回文子序列总结 647. 回文子串 1.动态规划和2.中心扩展 这个视频是基于上面的视频的代码 方法1:动态规划 布尔类型的dp[i][j]&#xff1a;表示区间范围[i,j] &#xff08;注意是左闭右闭&#xff09;的子串是否是回文子串&#xff0c;如…

面向切面编程(SpringAOP)、通过注解实现AOP代码、AOP的工作流程

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaweb 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 SpringAOP 一、AOP简介1.1连接点&#xff08;JoinPoint&am…

IntelliJ IDEA 2023.1.3 主菜单不见了

通过以下操作&#xff0c;去掉了勾&#xff0c;把主菜单玩没了 然后主菜单找半天都不知道怎么回来&#xff0c;下面记录找回来的过程 双击shift&#xff0c;在弹出的菜单里面搜索 "main menu"&#xff0c;在下图高亮位置选项改为 on

火警智能感知定位 智慧消防大数据可视化监测系统

伴随着城市建设的快速发展&#xff0c;城市消防安全风险的不断上升&#xff0c;城市高层、超高层建筑和大型建筑日益增多&#xff0c;建筑消防安全问题越来越突出。 建设背景 市场背景 近年来&#xff0c;我国多数省级以上城市、90%左右地级以上城市均提出了智慧消防建设计划…

无涯教程-jQuery - width( )方法函数

width()方法获取第一个匹配元素的当前计算像素宽度。 width( ) - 语法 selector.width( ) 此方法能够找到窗口和文档的宽度&#xff0c;如下所示: $(window).width(); //返回浏览器窗口的宽度 $(document).width(); //返回 HTML 文档的宽度 width( ) - 示例 <html&g…

如何用C#实现上位机与下位机之间的Wi-Fi通信?

有IP协议支持的话用UDP报文或者TCP直接发IP地址和端口不行么&#xff1f;你说的WiFi难道是2.4GHz频率模块那种东东&#xff1f; 你既然用了wifi&#xff0c;那么只要上位机和下位机的对应wifi网卡都具有ip地址以及其协议支持&#xff0c;那么和网络编程没啥子明显区别的吧………

pycharm制作柱状图

Bar - Bar_rotate_xaxis_label 解决标签名字过长的问题 from pyecharts import options as opts from pyecharts.charts import Barc (Bar().add_xaxis(["高等数学1&#xff0c;2","C语言程序设计","python程序设计","大数据导论",…