IT老齐的Redis 6实战课

news2024/11/20 10:33:38

NoSQL与Redis6新特性

什么是Redis
  • REDIS一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存分布式可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 APIRedis的读写性能非常好,官方压测数据为 Redis能读的速度是110000次/s,写的速度是81000次/s
  • https://redis.io/
Redis的实际工作场景
  • JD在若干高并发场景替代了MySQL
  • JD商城环境下热点商品数据缓存
  • 消息的订阅/发布(部分场景可取代MQ)
  • 各种高频读,低频写的数据场景
    • 排行榜 / 购物车 / 字典数据
  • 提供分布式架构下的分布式锁
  • 快速实现"交并差"运算
    • 查询两个读者都买过哪些书籍
    • 你的XX位好友也关注了这个频道
Redis 6 新特性

多线程IO

  • redis 6.0 提供了多线程的支持,redis 6 以前的版本,严格来说也是多线程,只不过执行用户命令的请求时单线程模型,还有一些线程用来执行后台任务, 比如 unlink 删除 大key,rdb持久化等。
  • redis 6.0 提供了多线程的读写IO,但是最终执行用户命令的线程依然是单线程的,这样就没有多线程数据的竞争关系,依然很高效。

客户端缓存

  • redis 6 提供了服务端追踪key的变化,客户端缓存数据的特性,可以减少网络通信次数

提升了RDB日志加载速度

Redis集群代理模块 Redis Cluster proxy

  • 在 Redis 集群中,客户端会非常分散,现在为此引入了一个集群代理,可以为客户端抽象Redis 群集,使其像正在与单个实例进行对话一样。同时在简单且客户端仅使用简单命令和功能时执行多路复用。
    在这里插入图片描述

Linux 安装Redis 6

第一步,更新GCC

GCC是Linux C语言编译器,Redis采用最新版GCC才能编译通过
#安装gcc
yum install -y gcc-c++ autoconf automake

第二步,获取Redis源码,解压编译

cd /usr/local/
wget http://download.redis.io/redis-stable.tar.gz
tar xvzf redis-stable.tar.gz
cd redis-stable
make
echo "redis installed"

第三步,修改配置文件,修改ip访问权限

bind * -::*

第四步,防火墙放行6379端口

firewall-cmd --zone=public --add-port=6379/tcp --permanent
firewall-cmd --reload

第五步,按照修改的配置,重启redis服务器

./redis-server ../redis.conf

Redis数据类型-String

Redis 字符串数据类型的相关命令用于管理 redis 字符串值

应用场景

  • 最常用的数据类型,通常用于保存单个对象/记录的数据 . 例如在Web应用中存储某个对象的
    JSON文本

Redis String 命令

GET/SET - 获取/设置KV
Redis SET 命令用于设置给定 key 的值。如果 key 已经存储其他值, SET 就覆写旧值,
且无视类型。

redis> SET key "value"
OK
redis> GET key
"value"

GETSET - 获取设置值KV
Redis Getset 命令用于设置指定 key 的值,并返回 key 的旧值。

redis> GETSET db mongodb # 没有旧值,返回 nil
(nil)
redis> GET db
"mongodb"
redis> GETSET db redis # 返回旧值 mongodb
"mongodb"
redis> GET db
"redis"

MGET/MSET命令-批量获取/设置
Redis Mget 命令返回所有(一个或多个)给定 key 的值。 如果给定的 key 里面,有某个 key 不
存在,那么这个 key 返回特殊值 nil 。

redis 127.0.0.1:6379> SET key1 "hello"
OK
redis 127.0.0.1:6379> SET key2 "world"
OK
redis 127.0.0.1:6379> MGET key1 key2 someOtherKey
1) "Hello"
2) "World"
3) (nil)

Redis Mset 命令用于同时设置一个或多个 key-value 对。

redis 127.0.0.1:6379> MSET key1 "Hello" key2 "World"
OK
redis 127.0.0.1:6379> GET key1
"Hello"
redis 127.0.0.1:6379> GET key2
1) "World"

SETNX - NIL时设置
Redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。

redis> EXISTS job # job 不存在
(integer) 0
redis> SETNX job "programmer" # job 设置成功
(integer) 1
redis> SETNX job "code-farmer" # 尝试覆盖 job ,失败
(integer) 0
redis> GET job # 没有被覆盖
"programmer"

Redis INCR - 数据自增
Redis Incr 命令将 key 中储存的数字值增一。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。本操作的值限制在 64 位(bit)有符号数字表示之内。

redis> SET page_view 20
OK
redis> INCR page_view
(integer) 21
redis> GET page_view # 数字值在 Redis 中以字符串的形式保存
"21"

Redis数据类型-Hash

Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)。
你可以形象的看成每一个Key存储了一个HashMap对象,通过一个Key可以保存一个数据结构。
在这里插入图片描述

Redis Hash 命令

HSET 设置单个Hash键值对

redis> HSET myhash field1 "foo"
OK
redis> HGET myhash field1
"foo"
redis> HSET website google "www.g.cn"
(integer) 1
redis>HSET website google "www.google.com"
(integer) 0

HMSET一次性设置多个HASH KV

redis> HMSET myhash field1 "Hello" field2 "World"
OK
redis> HGET myhash field1
"Hello"
redis> HGET myhash field2
"World"

HGET 获取某个redis key的某个hash值

redis> HSET site website google
(integer) 1
redis> HGET site website
"google"

HGETALL 获取某个Key下的所有数据

redis> HSET myhash field1 "Hello"
(integer) 1
redis> HSET myhash field2 "World"
(integer) 1
redis> HGETALL myhash
1) "field1"
2) "Hello"
3) "field2"
4) "World"
redis>

HEXISTS 判断某个Hash Key是否存在

redis 127.0.0.1:6379> HSET myhash field1 "foo"
(integer) 1
redis 127.0.0.1:6379> HEXISTS myhash field1
(integer) 1
redis 127.0.0.1:6379> HEXISTS myhash field2
(integer) 0

HDEL 删除某个Key下的Hash Key

redis 127.0.0.1:6379> HSET myhash field1 "foo"
(integer) 1
redis 127.0.0.1:6379> HDEL myhash field1
(integer) 1
redis 127.0.0.1:6379> HDEL myhash field2
(integer) 0

HVALS 返回HASH VALUE的列表。 当 key 不存在时,返回一个空表。

redis 127.0.0.1:6379> HSET myhash field1 "foo"
(integer) 1
redis 127.0.0.1:6379> HSET myhash field2 "bar"
(integer) 1
redis 127.0.0.1:6379> HVALS myhash
1) "foo"
2) "bar"

Redis数据类型-List

List列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者部(右边)
一个列表最多可以包含 2的32次方 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

应用场景

保存有序数据队列,例如: 排行榜ID队列 / XX购物车商品ID队列 / XX班级ID队列
获取到ID队列后,再根据每一个ID获取对应的HASH对象

Redis List 命令

rpush 在队尾插入新数据

> rpush listkey c b a
3

lpush 在队首插入新数据

> lpush listkey f e d
6

lrange 获取队列数据 -1获取所有

> lrange listkey 0 -1
d
e
f
c
b
a
> lrange listkey 0 2
d
e
f

rpop 弹出最右侧数据

> RPOP listkey
a
> lrange listkey 0 -1
d
e
f
c
b

lpop 弹出最左侧数据

> LPOP listkey
d
> lrange listkey 0 -1
e
f
c
b

Redis数据类型-Set

Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
Redis 中Set集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1),对Set操作的执行效率极高。
集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

应用场景

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Redis Set命令

sadd 新增SET 数据

> sadd user:1:follow it
1

smembers 查看Set数据

> smembers user:1:follow
it
> sadd user:1:follow music
1
> sadd user:1:follow his
1
> sadd user:1:follow sports
1
> sadd user:1:follow it
0
> smembers user:1:follow
sports
music
his
it

srandmember 随机提取n个数据 总数不变 抽奖

> srandmember user:1:follow 2
sports
his
> srandmember user:1:follow 2
it
his

spop 随机弹出一个元素 总数减少 抽奖

> spop user:1:follow
sports
> smembers user:1:follow
it
his
music

sdiff 计算差集

> sadd user:2:follow it news ent sports
4
> sdiff user:1:follow user:2:follow
music
his
> sdiff user:2:follow user:1:follow
sports
ent
news

注意:sdiff计算的是以左侧Set为基准,查找在右侧Set不存在的条目
sinter 计算交集 共同好友

> sinter user:1:follow user:2:follow
it

sunion 计算并集

> sunion user:1:follow user:2:follow
sports
it
his
music
ent
news

Redis数据类型-ZSet

Redis 的 ZSet 是 String 类型的有序Set集合,很多地方也被称为SortedSet,Set集合成员是唯一的,这就意味着集合中不能出现重复的数据。

List与ZSet的区别
  • List强调数据是按存储顺序有序排列,存储顺序与迭代顺序是一致的
  • ZSet则是给出分数进行排序,存储顺序与迭代顺序是不一致的

应用场景

各种多维度排行榜
在这里插入图片描述

Redis ZSet命令

zadd - 新增数据

> zadd player:rank 1000 ronaldo 900 messi 800 cronaldo 600 kaka
4

zscore - 获取分数

> zscore player:rank kaka
600

zcard - 获取ZSet总量

> zcard player:rank
4

zrank 查看排名(从小到大,从0开始)

> zrank player:rank ronaldo
3

zrem 移除指定元素

> zrem player:rank messi
1

zrange 获取指定范围集合

> zrange player:rank 0 -1
kaka
cronaldo
ronaldo
> zrange player:rank 0 -1 withscores
kaka
600
cronaldo
800
ronaldo
1000

zrevrange 反向排序

> zrevrange player:rank 0 -1 withscores
ronaldo
1000
messi
900
cronaldo
800

zcount 获取符合分数要求数据量

> zcount player:rank 700 1000
2

zrangebyscore 按分数范围获取数据

> zrangebyscore player:rank 700 1000 withscores
cronaldo
800
ronaldo
1000

Redis通用命令

Redis通用命令是Redis全局的管理命令,例如删除Redis Key / 设置过期时间 / 切换数据库都属于通用命令的范畴

Redis通用命令

select 切换数据库
Redis 默认拥有0~15号数据库,通过select命令进行切换,不同数据库间在内存存储上是隔离的,不同数据库允许持有相同的KV

> select 0
OK
> select 10
OK
> select 20
ERR DB index is out of range
> select 15
OK

keys 查询复合表达式要求的Redis key
keys时间复杂度是0(n),会进行"全库扫描",执行效率差,不建议使用

> set hello world
OK
> set java best
OK
> keys he[h-l]*
hello

exists 判断key是否存在

> EXISTS hello
1
> exists hello1
0

expire 设置key过期时长(秒),过期后该key自动删除

> set helloworld java
OK
> expire helloworld 3600
1

ttl 查询key剩余有效期

> ttl helloworld
3590
> ttl helloworld
3589
> ttl helloworld
3588

del 删除指定key

> del helloworld
1

flushdb 清空当前数据库,慎用慎用

> flushdb
OK

flushall 轻松所有数据库,慎用慎用慎用

> flushall
OK

Redis基础设置

几乎所有的Redis配置信息都保存在redis.conf文件中,其中以下几项是最关键的,在这里我们进行讲解

#面向所有IP开放
bind * -::*
#绑定端口号
port 6379
#采用后台方式运行
daemonize yes
#数据库总量
databases 16
#日志文件存储路径
logfile ./redis_log.log
#数据文件保存路径
dir ./
#设置访问密码
requirepass 123456
#启动redis命令
[root@localhost redis-stable]# ./src/redis-server ./redis.conf
#关闭redis命令
[root@localhost redis-stable]# ./src/redis-cli -p 6379
127.0.0.1:6379> auth 123456
127.0.0.1:6379> shutdown save

Redis开发规约

key的设计

在这里插入图片描述

不同类型的应用场景

在这里插入图片描述

Redis的安全建议

  • Redis不要在外网被访问,禁止:bind 0.0.0.0
    bind 192.168.132.128
  • 更改Redis端口,不要6379
    port 8838
  • Redis使用非root启动
  • Redis没有设置密码或者弱密码,不要与登录密码相同
    requirepass 与masterauth
  • 定期备份,save命令
  • 配置好Linux防火墙规则

Spring Boot与Redis交互

Spring Boot 内置了spring-boot-starter-data-redis这个模块用于简化Spring与Redis的交互过程.

Spring-data-redis是spring大家族的一部分,提供了在spring应用中通过简单的配置访问redis服务,对reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate提供了redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1cache进行了实现。

引入依赖

<dependency> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-data-redis</artifactId> 
</dependency> 
<dependency> 
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId> 
</dependency> 

额外引入commons-pool2是因为data-redis底层Redis连接池基于apachecommonspool2开发,不加入依赖回报ClassNotFoundException

注意

  • Spring Boot 2后默认使用Lettuce作为访问redis的客户端。
  • Lettuce是一个可伸缩线程安全的Redis客户端,它利用优秀netty NIO框架来高效地管理连接池。
  • 旧版本lettuce存在堆外内存溢出的bug, 5.3版本修复了这个bug, 我们是用 6.1。

配置application.yml

starter-data-redis默认利用lettuce作为连接池组件,配置时除了配置Redis连接信息外,还需要设置连接池

server:
  port: 80
spring:
  redis:
    host: 192.168.31.103
    port: 6379
    password: 123456
    #设置默认访问的数据库
    database: 1
    #设置超时时间
    timeout: 2000
    lettuce:
      pool:
        #最大允许连接数
        max-active: 100
        #最小空闲连接数,最少准备5个可用连接在连接池候着
        min-idle: 5
        #最大空闲连接数,空闲连接超过10个后自动释放
        max-idle: 10
        #当连接池到达上限后,最多等待30秒尝试获取连接,超时报错
        max-wait: 30000

利用RedisTemplate对象开发

spring-redis中使用了RedisTemplate来进行redis的操作,在Spring Boot IoC容器启动后创建,在应用中可以直接注入使用

@SpringBootTest
public class RedisTemplateTestor {
    @Resource
    private RedisTemplate redisTemplate;

    @Test
    public void testString(){
        redisTemplate.opsForValue().set("name" , "itlaoqi");
        Map map = new HashMap();
        map.put("a", "b");
        redisTemplate.opsForValue().set("age" , map);
        String name = (String)redisTemplate.opsForValue().get("name");
        System.out.println(name);
        Map age = (Map)redisTemplate.opsForValue().get("age");
        System.out.println(age);
    }

    @Test
    public void testHash(){
        redisTemplate.opsForHash().put("website" , "name" , "IT老齐");
        Map map = new HashMap<>();
        map.put("name", "张三");
        map.put("hiredate", "2021-05-06");
        redisTemplate.opsForHash().putAll("employee",map);
        Map employee = redisTemplate.opsForHash().entries("employee");
        System.out.println(employee);
    }

    @Test
    public void testBasic(){
        redisTemplate.expire("employee", 30, TimeUnit.MINUTES);
        redisTemplate.delete("employee");
    }

    @Test
    public void  testList(){
        redisTemplate.opsForList();
    }
    @Test
    public void testSet(){
        redisTemplate.opsForSet();
    }
    @Test
    public void testZset(){
        redisTemplate.opsForZSet();
    }

}

详细看这里
RedisTemplate常用方法总结

Spring Boot Redis序列化问题

什么是序列化

把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。

对象的序列化主要有两种用途

  • 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中
  • 在网络上传送对象的字节序列。
Redis为什么要序列化
  • 性能可以提高,不同的序列化方式性能不一样
  • 可视化工具更好查看
Spring Boot 序列化问题的由来

默认情况下,Redis缓存和Redis模板配置为使用Java本地序列化。Java本地序列化以允许运行由有效远程代码而闻名,但执行过程中可能出现向存在漏洞的库和类注入字节码的骚操作,别有用心的人可以在反序列化过程使应用程序中运行额外(恶意)的代码。因此,不要在不受信任的环境中使用序列化。一般来说,我们强烈推荐任何其他消息格式(如JSON)。如果您担心由于Java序列化而导致的安全漏洞,请考虑核心JVM级别的通用序列化过滤器机制,该机制最初是为JDK9开发的,但后来向后移植到JDK8、7.

自定义redis序列化方式,提供了多种可选择策略
  • JdkSerializationRedisSerializer
    POJO对象的存取场景,使用JDK本身序列化机制,默认机制
    ObjectInputStream/ObjectOutputStream进行序列化操作

  • StringRedisSerializer
    Key或者value为字符串

  • Jackson2JsonRedisSerializer
    利用jackson-json工具,将pojo实例序列化成json格式存储

  • GenericFastJsonRedisSerializer
    另一种javabean与json之间的转换,同时也需要指定Class类型

Jackson对象序列化

@Configuration
public class RedisTemplateConfiguration {
    @Bean
    public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        //忽略任何值为null的属性
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        // 设置key和value的序列化规则
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // 设置hashKey和hashValue的序列化规则
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        //afterPropertiesSet和init-method之间的执行顺序是afterPropertiesSet 先执行,init-method 后执行。
        redisTemplate.afterPropertiesSet();
        return redisTemplate;

    }
}

Spring Cache声明式事务

自Spring 3.1版本以来,Spring框架支持低侵入的方式向已有Spring应用加入缓存特性。与声明式事务类似,声明式缓存Spring Cache抽象允许一致的API来已支持多种不同的缓存解决方案,同时将对代码的影响减少到最小。

从Spring 4.1开始,Spring已完整支持JSR-107注解和更多的定制选项。

在这里插入图片描述

区分Cache与Buffer

很多情况下,Buffer(缓冲)与Cache(缓存)是类似的。然而在表现形式与应用场景上两个的差别还是比较明显的。传统意义上,Buffer作为快速实体与慢速实体之间的桥梁。比如:硬盘上的文件数据会先到内存,再被CPU加载,内存作为Buffer可以减少等待时间,同时利用Buffer可将原本小块数据攒成整块一次性交给处理者,可以有效减少IO。此外,通常至少有一个对象对其可见。
而Cache缓存,相对来说是隐藏的,对于访问者与被访问者来说应该是隐藏的,好的程序设计可以让使用者对缓存无感知,同时它还可以提高性能,允许应用多次、快速的读取缓存数据。

如何使用Spring Cache

第一步,开启Spring Cache

@SpringBootApplication
@EnableCaching //通知Spring Boot启用声明式缓存
public class SpbRedisApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpbRedisApplication.class, args);
    }
}

第二步,在Service方法层面使用@Cacheable等注解声明式的控制缓存

@Service
public class EmpService {
    @Resource
    EmpDAO empDao;
    //对于默认情况下, Redis对象的序列化使用的是JDK序列化.必须要求实体类实现Serili..接口
    //Cacheable会将方法的返回值序列化后存储到redis中,key就是参数执行的字符串
    //Cacheable的用途就是在执行方法前检查对应的key是否存在,存在则直接从redis中取出不执行方法中的代码
    //没有对应的key则执行方法代码,并将返回值序列化保存到redis中
    //condition代表条件成立的时候才执行缓存的数据 , 反之有一个unless ,代表条件不成立的时候才获取缓存
    @Cacheable(value = "emp" , key = "#empId" ,condition = "#empId != 1000" ,cacheManager = "cacheManager1m")
    public Emp findById(Integer empId) {
        return empDao.findById(empId);
    }

    @Cacheable(value = "emp:rank:salary")
    public List<Emp> getEmpRank() {
        return empDao.selectByParams();
    }
    //@CachePut 更新缓存,没有key则创建
    @CachePut(value="emp" , key="#emp.empno")
    public Emp create(Emp emp) {
        return empDao.insert(emp);
    }

    @CachePut(value="emp" , key="#emp.empno")
    public Emp update(Emp emp) {
        return empDao.update(emp);
    }
    //@CacheEvict 删除缓存
    @CacheEvict(value = "emp",key = "#empno")
    public void delete(Integer empno) {
        empDao.delete(empno);
    }

}

第三步,做一个测试用例,验证缓存是否生效

@SpringBootTest
public class SpringCacheTestor {
    @Resource
    private EmpService empService;

    @Test
    public void testFindById(){
        Emp emp = empService.findById(1001);
        emp = empService.findById(1001);
        emp = empService.findById(1001);
        emp = empService.findById(1001);
        emp = empService.findById(1001);
        emp = empService.findById(1001);
        System.out.println(emp.getName());
        emp = empService.findById(1000);
        emp = empService.findById(1000);
        emp = empService.findById(1000);
        System.out.println(emp.getName());
    }

    @Test
    public void testEmpRank() {
        List<Emp> list = empService.getEmpRank();
        list = empService.getEmpRank();
        for(Emp emp:list){
            System.out.println(emp);
        }
    }
    @Test
    public void testCreate(){
        empService.create(new Emp(1002 , "emp" + new Date().getTime() , new Date() , 1234f , "MARKET"));
        empService.create(new Emp(1002 , "emp" + new Date().getTime() , new Date() , 1234f , "MARKET"));
        empService.create(new Emp(1002 , "emp" + new Date().getTime() , new Date() , 1234f , "MARKET"));
        Emp emp = empService.findById(1002);
        System.out.println(emp);
    }

    @Test
    public void testUpdate(){
        empService.update(new Emp(1002 , "u-emp" + new Date().getTime() , new Date() , 1234f , "MARKET"));
        empService.update(new Emp(1002 , "u-emp" + new Date().getTime() , new Date() , 1234f , "MARKET"));
        empService.update(new Emp(1002 , "u-emp" + new Date().getTime() , new Date() , 1234f , "MARKET"));
        Emp emp = empService.findById(1002);
        System.out.println(emp);
    }
    @Test
    public void testDelete(){
        empService.delete(1002);
        //Emp emp = empService.findById(1002);
        //System.out.println(emp);
    }
}

Spring Cache的亿点点细节调整

在使用Spring Cache的时候有有三点困扰

  1. 默认Spring Cache采用 :: 分割数据,并不是约定俗称的 冒号 分割
  2. 默认使用JDK序列化,JDK序列化的问题之前我们也提到了,应该为JSON序列化
  3. 默认Spring Cache注解是不支持Expire过期的,但这是日常开发中必然会用到的特性,该如何处理呢?

通过自定义CacheManager解决上述问题

@Configuration
public class SpringCacheConfgiration {
    @Bean
    @Primary //设置默认的CacheManager
    public CacheManager cacheManager(LettuceConnectionFactory factory){
        //加载默认Spring Cache配置信息
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        //设置有效期为1小时
        config = config.entryTtl(Duration.ofHours(1));
        //说明缓存Key使用单冒号进行分割
        config = config.computePrefixWith(cacheName -> cacheName + ":");
        //Redis Key采用String直接存储
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        //Redis Value则将对象采用JSON形式存储
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        //不缓存Null值对象
        config = config.disableCachingNullValues();

        //实例化CacheManger缓存管理器
        RedisCacheManager cacheManager = RedisCacheManager.RedisCacheManagerBuilder
                //绑定REDIS连接工厂
                .fromConnectionFactory(factory)
                //绑定配置对象
                .cacheDefaults(config)
                //与声明式事务注解@Transactional进行兼容
                .transactionAware()
                //完成对象构建
                .build();
        return cacheManager;

    }

    @Bean
    public CacheManager cacheManager1m(LettuceConnectionFactory factory){
        //加载默认Spring Cache配置信息
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        //设置有效期为1小时
        config = config.entryTtl(Duration.ofMinutes(1));
        //说明缓存Key使用单冒号进行分割
        config = config.computePrefixWith(cacheName -> cacheName + ":");
        //Redis Key采用String直接存储
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        //Redis Value则将对象采用JSON形式存储
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
        //不缓存Null值对象
        config = config.disableCachingNullValues();

        //实例化CacheManger缓存管理器
        RedisCacheManager cacheManager = RedisCacheManager.RedisCacheManagerBuilder
                //绑定REDIS连接工厂
                .fromConnectionFactory(factory)
                //绑定配置对象
                .cacheDefaults(config)
                //与声明式事务注解@Transactional进行兼容
                .transactionAware()
                //完成对象构建
                .build();
        return cacheManager;

    }
}

在Service使用时,如果需要特别的过期时间,需要在@Cacheable增加cacheManager属性

@Cacheable(value = "emp" , key = "#empId" ,condition = "#empId != 1000" ,
cacheManager = "cacheManager1m")
    public Emp findById(Integer empId) {
        return empDao.findById(empId);
    }

Redis数据持久化之RDB

Redis持久化介绍

Redis是一个内存数据库,如果没有配置持久化,redis重启后数据就全丢失
因此开启redis的持久化功能,将数据保存到磁盘上,当redis重启后,可以从磁盘中恢复数据。

RDB (Redis DataBase) 全量二进制备份

在指定的时间间隔内将内存中的数据集快照写入磁盘
默认的文件名为dump.rdb

RDB快照的应用场景

  • save
    会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止
  • bgsave
    fork创建子进程,RDB持久化过程由子进程负责,会在后台异步进行快照操作,快照同时还可以响应客户端请求
  • 自动化触发
    配置文件来完成,配置触发 Redis的 RDB 持久化条件
    比如 “save m n”。表示m秒内数据集存在n次修改时,自动触发bgsave
  • 主从架构
    从服务器同步数据的时候,会发送sync执行同步操作,master主服务器就会执行bgsave

优点
RDB文件紧凑,全量备份,适合用于进行备份和灾难恢复
在恢复大数据集时的速度比 AOF 的恢复速度要快
生成的是一个紧凑压缩的二进制文件

缺点
每次快照是一次全量备份,fork子进程进行后台操作,子进程存在开销
在快照持久化期间修改的数据不会被保存,可能丢失数据

关于RDB的关键配置

Redis.conf

#持久化文件名称
dbfilename itlaoqi.rdb
#持久化文件存储路径
dir /usr/local/redis/data
#持久化策略, M秒内有个n个key改动,执行快照
save 3600 1
save 300 100
save 60 10000
#导出rdb数据库文件压缩字符串和对象,默认是yes,会浪费CPU但是节省空间
rdbcompression yes
#导入时是否检查
rdbchecksum yes

Redis数据持久化之AOF

AOF (append only file)增量日志

追加文件的方式,文件容易被人读懂
以独立日志的方式记录每次写命令, 重启时再重新执行AOF文件中的命令达到恢复数据的目的
写入过程宕机,也不影响之前的数据,可以通过 redis-check-aof检查修复问题

核心原理

Redis每次写入命令会追加到aof_buf(缓冲区)
新增x1:v1
新增x2:v2
删除x2
新增x3:v3
覆盖x4:v4
AOF缓冲区根据对应的策略向硬盘做同步操作
高频AOF会带来影响,特别是每次刷盘

同步方式

提供了3种同步方式,在性能和安全性方面做出平衡

  • appendfsync always
    每次有数据修改发生时都会写入AOF文件,消耗性能多
  • appendfsync everysec
    每秒钟同步一次,该策略为AOF的缺省策略。
  • appendfsync no
    不主从同步,由操作系统自动调度刷磁盘,性能是最好的,但是最不安全
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec

AOF Rewrite重写机制

AOF文件越来越大,需要定期对AOF文件进行重写达到压缩
旧的AOF文件含有无效命令会被忽略,保留最新的数据命令
多条写命令可以合并为一个
AOF重写降低了文件占用空间
更小的AOF 文件可以更快地被Redis加载

示例:
新增x1:v1
覆盖x1:v2
覆盖x1:v3
覆盖x1:v4
删除x1
新增x1:v5
Rewrite以后
新增x4:v5

重写触发配置

  • 手动触发
    bgrewriteaof
  • 自动触发
    • auto-aof-rewrite-percentage 100
    • Redis记住上次重写时AOF日志的大小,
      比如上一次重写后50mb,当增长率达到100%,
      也就是AOF文件达到50+50=100mb后,就会
      自动触发rewrite.60mb = 120mb
    • auto-aof-rewrite-min-size 50mb 设置触发Rewrite的最小尺寸
# 是否开启aof
appendonly yes
# 文件名称
appendfilename "appendonly.aof"
# 同步方式,每秒同步
appendfsync everysec
# aof重写期间是否同步
no-appendfsync-on-rewrite no
# 重写触发配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 50mb
# 加载aof时如果有错如何处理
# yes表示如果aof尾部文件出问题,写log记录并继续执行。no表示提示写入等待修复后写
入
aof-load-truncated yes

AOF与RDB的取舍

RDB持久化以指定的时间间隔执行数据集的时间点快照。
AOF持久化记录服务器接收的每个写入操作,将在服务器启动时再次读取,重建原始数据。
集。使用与Redis协议本身相同的格式以仅追加方式记录命令,当文件太大时,Redis能够重写。

RDB的优缺点
  • 优点
    • RDB最大限度地提高了Redis的性能,父进程不需要参与磁盘I/O
    • RDB文件紧凑,全量备份,适合用于进行备份和灾难恢复
    • 在恢复大数据集时的速度比 AOF 的恢复速度要快
    • 生成的是一个紧凑压缩的二进制文件
  • 缺点
    • 如果您需要在Redis停止工作时(例如断电后)将数据丢失的可能性降至最低,则RDB并不好
    • RDB经常需要fork才能使用子进程持久存储在磁盘上。如果数据集很大,Fork可能会非常耗时
AOF的优缺点
  • 优点
    • 数据更加安全
    • 当Redis AOF文件太大时,Redis能够在后台自动重写AOF
    • AOF以易于理解和解析的格式,一个接一个地包含所有操作的日志
  • 缺点
    • AOF文件通常比同一数据集的等效RDB文件大
    • 根据确切的fsync策略,恢复的时候AOF可能比RDB慢

在线上我们到底该怎么做?

  • AOF通常作为RDB的补充使用
  • 如果Redis中的数据并不是特别敏感或者可以通过其它方式重写生成数据,集群中可以关闭AOF持久化,靠集群的备份方式保证可用性
  • 自己制定策略定期检查Redis的情况,然后可以手动触发备份、重写数据;
Redis4.0后开始的rewrite支持混合模式

就是RDB和AOF一起用
直接将rdb持久化的方式来操作将二进制内容覆盖到aof文件中,rdb是二进制,所以很小
有写入的话还是继续append追加到文件原始命令,等下次文件过大的时候再次rewrite
默认是开启状态
AOF日志↓↓↓↓
新增x1:v1
覆盖x1:v2
覆盖x1:v3
覆盖x1:v4
删除x1
新增x1:v5
新增x2:v6
混合模式Rewrite以后
RDB部分(二进制压缩表达):
x1:v5

AOF日志:↓↓↓
新增x3:v7

  • 好处
    • 混合持久化结合了RDB持久化 和 AOF 持久化的优点,采取了rdb的文件小易于灾难恢复
    • 同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失
  • 坏处
    • 前部分是RDB格式,是二进制,所以阅读性较差

数据恢复

  • 先看是否存在aof文件,若存在则先按照aof文件恢复,aof比rdb全,且aof文件也rewrite成rdb二进制格式
  • 若aof不存在,则才会查找rdb是否存在

Redis主从复制实战

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

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

相关文章

【MySQL系列】MySQL的用户管理

「前言」文章内容大致是MySQL的用户管理。 「归属专栏」MySQL 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、用户管理1.1 用户信息1.2 创建新用户1.3 删除用户1.4 修改用户密码 二、数据库的权限2.1 给用户授权2.2 回收用户权限 一、用户管理 MySQL与Linux类似&#x…

图形学线性代数

最近学图形学&#xff01;学的是GAMES101-现代计算机图形学入门-闫令琪↓会做点笔记 Lecture 03 Transformation_哔哩哔哩_bilibili 点乘 1&#xff09;主要作用是找到两个方向/向量的夹角 2&#xff09;找一个向量投影到另一个向量 作用 叉乘 此处x,y,z都是坐标轴 作用 1&a…

HTML+CSS画一个卡通中秋月饼

HTMLCSS画一个卡通中秋月饼&#x1f96e;&#x1f96e;&#x1f96e; 中秋活动水个文章 整个divcss实现个月饼&#xff0c;给前端初学者一个练手的demo 效果图 思路 HTMl 先来个轮廓画脸上的东西&#xff1a;眼睛、眉毛、腮红、嘴巴眼睛丰富下瞳孔画20个花瓣 CSS 轮廓是要外…

4G版本云音响设置教程腾讯云平台版本

文章目录 4G本云音响设置教程介绍一、申请设备三元素1.腾讯云物联网平台2.创建产品3.设置产品参数4.添加设备5.获取三元素 二、设置设备三元素1.打开MQTTConfigTools2.计算MQTT参数3.使用USB连接设备4.设置参数 三、腾讯云物联网套件协议使用说明1.推送协议信息2.topic规则说明…

WebGL 根据模型矩阵的逆转置矩阵计算运动物体的光照效果

目录 前言 坐标变换引起法向量变化 变化规律&#xff1a; 魔法矩阵&#xff1a;逆转置矩阵 逆转置矩阵的用法总结 Matrix4对象的 setInverseOf 、transpose 方法规范&#xff08;以完成逆转置矩阵&#xff09; 示例代码&#xff08;LightedTranslatedRotatedCube.js&am…

虚拟机Ubuntu操作系统常用终端命令(2)(详细解释+详细演示)

本篇概要 本篇讲述了Ubuntu操作系统常用的几个功能&#xff0c;即超级用户&#xff0c;虚拟机系统损坏如何修复&#xff0c;用户和组&#xff0c;如何以root登录界面以及文件的权限方面的知识。希望能够得到大家的支持。 文章目录 本篇概要1.超级用户1.1使用超级用户1.2切换到…

【ICer必备基础】MOS电容——电容电压特性详解

【ICer必备基础】MOS电容——电容电压特性详解 1相关定义2MOS电容描述3MOS电容能带分析4可变电容实际应用 1相关定义 MOS电容是集成电路中非常重要的应用&#xff0c;器件电容的定义为&#xff1a; 阈值反型点&#xff1a; 当达到最大耗尽宽度且反型层电荷密度为零时的情形。此…

在Windows上无法使用TortoiseSVN等工具管理WSL2中的代码的问题

环境 Windows 11 WSL2&#xff08;Ubuntu 22.04&#xff09; 前言 众所周知&#xff0c;WSL2 的跨系统IO读写性能非常差&#xff08;详情见我之前写的这篇文章&#xff09;&#xff0c;而我的代码又是在 WSL2 中运行的&#xff0c;为了提高性能&#xff0c;所以我的代码也必…

2023 Google 开发者大会:无障碍游戏体验升级、安卓开发人员生产力爆棚

目录 前言 一、会说话的狗 - “大黄” 二、GameFace -联合《荒野行动》&#xff0c;提升无障碍游戏体验 三、Android Studio Bot-解放开发人员生产力 五、Purnima致中国开发者的一封“信” 结语 &#x1f388;个人主页&#xff1a;库库的里昂 &#x1f390;CSDN新晋作者 …

9. 原型模式

引言 关键在于具备clone函数&#xff1b;克隆自身。 原型模式&#xff08;Prototype&#xff09;&#xff0c;用原型实例指定创建对象的种类&#xff0c;并且通过拷贝这些原型创建新的对象。 UML 测试代码&#xff1a; #include <iostream> using namespace std;class …

Python 之plt.plot()的介绍以及使用

文章目录 介绍代码实例 介绍 plt.plot() 是Matplotlib库中用于绘制线图&#xff08;折线图&#xff09;的主要函数之一。它的作用是将一组数据点连接起来&#xff0c;以可视化数据的趋势、关系或模式。以下是 plt.plot() 的详细介绍&#xff1a; plt.plot(x, y, fmt, **kwarg…

sun.security.validator.ValidatorException: PKIX path building failed

问题说明 在A系统使用HttpPost调用B系统的接口时报&#xff0c;B系统的ssl证书使用的是阿里云免费ssl证书&#xff0c;很郁闷调用别的系统都没有出现过这样的问题。 sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.Su…

ChatGLM-6b的微调与推理

基于ChatGLM-6B的推理与部署 1.使用git clone命令ChatGLM项目地址&#xff0c;将项目clone到本地。 2.下载ChatGLM-6B模型文件 【注意】运行下面代码的时候&#xff0c;要将源代码中的模型文件路径改成自己的地址&#xff0c;不然会报错&#xff01;&#xff01;&#xff01;…

destoon自定义一个archiver内容文档

在archiver目录建立以下代码&#xff1a; <?php define(DT_REWRITE, true); require ../common.inc.php; $EXT[archiver_enable] or dheader(DT_PATH); //$DT_BOT or dheader(DT_PATH); $N $M $T array(); $mid or $mid 5; $vmid $list 0; foreach($MODULE as $k>…

微服务保护-授权规则

个人名片&#xff1a; 博主&#xff1a;酒徒ᝰ. 个人简介&#xff1a;沉醉在酒中&#xff0c;借着一股酒劲&#xff0c;去拼搏一个未来。 本篇励志&#xff1a;三人行&#xff0c;必有我师焉。 本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》&#xff0c;SpringCloud…

【JUC】Java并发编程从挖坑到入土全解(2)

目录 我们锁的到底是什么&#xff08;8个案例&#xff09; 案例1 案例2 案例3 案例4 案例5 案例6 案例7 案例8 总结 我们锁的到底是什么&#xff08;8个案例&#xff09; 有a、b两个线程&#xff0c;我们基于如下代码进行改造&#xff1a; public static void main…

数据结构---二叉搜索树

二叉搜索树 二叉搜索树什么是二叉搜索树&#xff1f; 二叉搜索树的操作查找插入删除 源代码非递归版 二叉搜索树 什么是二叉搜索树&#xff1f; 二叉搜索树(Binary Search Tree 简称BST)又称二叉排序树&#xff0c;是一种二叉树的特殊形式&#xff0c;它在每个节点上存储的键…

动态规划-货币问题

动态规划-货币问题 题目一 arr是货币数组&#xff0c;其中的值都是正数。再给定一个正数aim。每个值都认为是一张货币&#xff0c;即便是值相同的货币也认为每一张都是不同的&#xff0c;返回组成aim的方法数。例如 : arr { 1,1,1 }&#xff0c;aim 2&#xff0c;第0个和第…

C++之std::monostate应用实例(二百二十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

安卓恶意应用识别(三)(批量反编译与属性值提取)

前言 上篇说到对安卓APK反编译&#xff0c;本篇实现批量反编译和批量特征提取及计算&#xff0c;主要就是通过python代码与cmd进行批量化交互&#xff0c;我在写文章之前&#xff0c;尝试批量下载了安卓apk&#xff08;大约10来个&#xff09;&#xff0c;发现现在这个应用软件…