redis

Redis

一、这是个啥玩意?

Redis 是一种运行在内存中的NoSql非关系型数据库。可以在1s内完成10万次的读写,具有很高的性能,现在网站应用多数功能是查询,在查询比例较大的模块中用redis 可以极大的提高性能。

二、可以干啥?

Redis 除了操作支持的数据类型功能外,还能支持事务,流水线,发布订阅和lua脚本等功能。一般我们会用来做为缓存来提高应用的性能。

三、咋用?

RedisConnectionFactory 通过驱动配置JedisPoolConfig 进行配置、RedisTemplate进行操作、

基础知识

Redis 是一种键值数据库,而且是以字符串类型为中心(Java是操作对象的,所以存储的时候需要序列化)。

支持的数据类型:字符串、散列、列表(链表)、集合、有序集合、基数和地理位置(后两种很少使用)

驱动

Java 中与Redis 连接的驱动有多种,目前比较广泛使用的是 Jedis。

Spring 中使用 Redis

Spring 提供了RedisConnectionFactory接口可生成RedisConnection对象,是对Redis底层接口的封装。

RedisConnectionFactory 配置
@Configuration
public calss RedisConfig(){
  private RedisConnectionFactory connectionFactory=null;
  
  @Bean(name="RedisConnectionFactory")
  public RedisConnectionFactory initRedisConnectionFactory(){
    if(this.connectionFactory != null){
      return this.connectionFactory;
    }
    JedisPoolConfig pc = new JedisPoolConfig();
    //最大空闲数
    pc.setMaxIdle(30);
    //最大连接数
    pc.setMaxTotal(50);
    //最大等待毫秒数
    pc.setMaxWaitMillis(2000);
    JedisConnectionFactory connectionFactory = new JedisConnectionFactory(pc);
    RedisConnectionConfiguration rsCfg = connectionFactory.getStandaloneConfiguration();
    rsCfg.setHostName("192.168.11.131");
    rsCfg.setPort(6379);
    rsCfg.setPassword("23234");
    this.connectionFactory =connectionFactory;
    return connectionFactory;
  }
  
  //redisTemplate 
  @Bean(name="redisTemplate")
  public RedisTemplate<Object,Object> initRedisTemplate(){
    RedisTemplate<Object,Object> redisTemplate = new RedisTemplate<>();
    
    //RedisTemplate 会自动初始化 setringRedisSerializer,所以这里直接获取
    RedisSerializer setringRedisSerializer = redisTemplate.getStringSerializer();
    //设置字符串序列化器,这样spring 就会吧redis的key 当作字符串处理了
    redisTemplate.setKeySerializer(StringRedisSerializer);
    redisTemplate.setHashKeySerializer(StringRedisSerializer);
    redisTemplate.setHashValueSerializer(StringRedisSerializer);
   
    redisTemplate.setConnectionFactory(this.connectionFactory);
    return redisTemplate;
  }
}
RedisTemplate

RedisTemplate 会自动从 RedisConnectionFactory中获取连接,然后执行对应命令,最后还会关闭Redis连接。(如上代码)

Spring 提供了RedisSerializer接口,用两个方法,serialize,deserialize两个方法。

JdkSerializationRedisSerializer是RedisTemplate默认的序列器。StringRedisSerializer为常用的序列器

RedisTemplate提供的可配置的属性

属性描述备注
defaultSerializer默认序列化器如果没有设置,使用JdkSerializationRedisSerializer
keySerializerRedis键序列化器如果没有设置,则使用默认序列化器
valueSerializerRedis值序列化器同上
hashKeySerializerRedis散列结构field序列化器同上
hashValueSerializerRedis散列结构value序列化器同上
stringSerializer字符串序列化器RedisTemplate自动赋值为StringRedisSerializer对象

示例:

redisTemplate.opsForValue().set("key1","value1");
redisTemplate.opsForHash().set("hash","field","hvalue");

redisTemplate 的操作, 从连接工厂获取一个连接 ——>执行对应命令——>关闭连接。这显然存在资源浪费,

为了克服这个问题,Spring提供了RedisCallback 和SessionCallback

数据类型操作封装
	redisTempalte.opsForGeo();//地理位置操作接口
	redisTempalte.opsForHash();//散列
  redisTempalte.opsForHyperLogLog();//基数
  redisTempalte.opsForList();//列表
  redisTempalte.opsForSet();//集合
  redisTempalte.opsForValue();//字符串
  redisTempalte.opsForZSet();//有序集合
  

如果需要对一个键值对连续操作

redisTempalte.boundGeoOps("geo")//地理位置绑定的操作接口
redisTempalte.boundHashOps("hash");
redisTempalte.boundListOps("list");
redisTempalte.boundSetOps("set");
redisTempalte.boundValueOps("string");
redisTempalte.boundZSetOps("zset");
SessionCallback和RedisCallBack

他们的作用都是让RedisTemplate 进行回调,可以在一条连接中执行多个redis命令,但是SessionCallback提供了良好的封装,对开发者更友好。

//需要处理底层转换规则,如果不考虑改写底层,尽量不使用它
public void useRedisCallback(RedisTemplate redisTemplate){
  redisTemplate.execute(new RedisCallback(){
    @Override
    public Object doInRedis(RedisConnection rc) throws DataAccessException{
      rc.set("key1".getBytes(),"value1".getBytes());
      rc.hSet("hash".getBytes(),"field".getBytes(),"hvalue".getBytes());
      return null;
    }
  })
}
//一般情况下,优先使用
public void useSeesionCallback(RedisTemplate redisTemplate) throws DataAccessException{
  redisTemplate.execute(new SeesionCallback(){
    @Override
    public Object doInRedis(RedisConnection rc){
      rc.set("key1","value1");
      rc.hSet("hash","field","hvalue");
      return null;
    }
  })
}

Spring Boot中配置使用Redis

配置

 redis:
    jedis:
      pool:
        min-idle: 5
        max-idle: 20
        max-active: 20
        max-wait: 2000
    port: 6379
    password: 123456
    timeout: 1000
    host: 127.0.0.1 ##本机地址,默认地址,可在redis.conf中修改
  cache:
    type: redis
    cache-names: redisCache  

初始化

进行字符串序列化初始化

@PostConstruct
	public void init(){
	initRedisTemplate();
	}
	
	private void initRedisTemplate() {
		//RedisTemplate 会自动初始化 setringRedisSerializer,所以这里直接获取
		RedisSerializer setringRedisSerializer = redisTemplate.getStringSerializer();
		//设置字符串序列化器,这样spring 就会吧redis的key 当作字符串处理了
		redisTemplate.setKeySerializer(setringRedisSerializer);
		redisTemplate.setHashKeySerializer(setringRedisSerializer);
		redisTemplate.setHashValueSerializer(setringRedisSerializer);
	}

操作数据类型

常用注解开发

spring.cache.cache-names= #如果由底层的缓存管理器支持创建,以逗号分隔的列表来缓存名称 spring.cache.redis.cache-null-values=true  #是否允许Redis缓存空值
spring.cache . redis . key-prefix= # Redis 的键前缀
spring.cache.redis . time-to-live=Oms # 缓存超时时间戳,配置为 0 则不设置超时时间 
spring.cache .redis.use-key-prefix=true #是否启用 Redis 的键前缀
spring. cache.type= # 缓存类型, 在默认的情况下, Spring 会自动根据上下文探测

@CachePut(value=“redisCache”, key="") 插入 修改 表示将方法结果返回存放到 缓存中。

@Cacheable(value ="redisCache”, key =”’redis_user_’+#id”) 查询 表示先从缓存中通过定义的键查询,如果可以查询到数据,则返回,否则执行该方法,返回数据,并且将返回结果保存到缓存中。

@CacheEvict(va工ue =”redisCache”, key =” ’r edis_user_’+#id”, before 工 nvocation = false) 移除 通过定义的键移除缓存,它有一个 Boolean类型的配置项 beforelnvocation,表 示在方法之前或者之后移除缓存。因为其默认值为也lse,所以默认为方法之后将缓存移除 。

事务

首先 Redis 是支持一定事务能力的 NoSQL, 在 Redis 中使用事务,通常的命令组合是 watch… multi…exec,也就是要在一个 Redis 连接中执行多个命令,这时我们可以考虑使用SessionCallback接 口来达到这个目的。其中, watch命令是可以监控 Redis的一些键: multi命令是开始事务,开始事务后,该客户端 的命 令不会马上被执行 ,而 是存放在一个队列里,这点是需要注意的地方,也就是在这时我们执行一些堪回数据的命令, Redis也是不会马上执行的,而是把命令放到一个队列里,所以 此时调用 Redis 的命令,结果都是返回 null,这是初学者容易犯的错误: exe 命令的意义在于执行事 务,只是它在队列命令执行前会判断被 watch监控的 Redis 的键的数据是否发生过变化 (即使赋予与 之前相同的值也会被认为是变化过〉,如果它认为发生了变化,那么 Redis 就会取消事务 , 否则就会 执行事务, Redis在执行事务时,要么全部执行, 要么全部不执行,而且不会被其他客户端打断,这样就保证了 Redis事务下数据的一致性。图 就是 Redis事务执行的过程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xeWPAHkd-1618120062634)(https://raw.githubusercontent.com/shadowlgd/image/master/blogimg屏幕快照 2021-04-11 下午1.41.27.png)]

public Map<String, Ob] ect> testMulti () {
  redisTemplate.opsForValue() .set(”keyl”, ”valuel”);
  List
list= (List)redisTemplate.execute((RedisOperations operations) - > { //设置要监控 keyl
operations.watch (” keyl ”);
//开启事务,在 exec 命令执行前,全部都只是进入队列
operat工ons .multi();
operations.opsForV alue() . set (” key2 ” , ” value2 ” );
II operations.opsForValue () .increment (” keyl”, 1); 
//获取值将为 null, 因为自由 s 只是把命令放入队列
Object value2 = operations.opsForV alue() . get (” key2 ” );
System.out .println (”命令在队列,所以 value 为 null <+ value2 +>); operations.opsForValue() .set (” key3”, "value3” );
Object value3 = operat工ons.opsForValue() .get (”key3”);
System.out.println (”命令在队列,所以 value 为 null 【川 value3 +>) ;
//执行 exec 命令,将先判别 keyl 是否在监控后被修改过,如果是则不执行事务,否则就执行事务 
                                                                                               return operations exec(); 
                                                                                               });
}

持久化

持久化流程

  1. 客户端向服务端发送写操作(数据在客户端的内存中)。
  2. 数据库服务端接收到写请求的数据(数据在服务端的内存中)。
  3. 服务端调用write这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)。
  4. 操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)。
  5. 磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)。

上面的5种持久化流程并不是能一直全部完成,当数据出现损毁的时候,就需要一定的恢复机制

RDB机制

RDB其实就是把数据以快照的形式保存在磁盘上,RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。

触发方式
  1. save

    执行save命令期间,会阻塞redis其他命令,直至完成生成二进制RDB文件。

  2. bgsave

    具体操作是Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。基本上 Redis 内部所有的RDB操作都是采用 bgsave 命令。

  3. 自动化

    **①save:**这里是用来配置触发 Redis的 RDB 持久化条件,也就是什么时候将内存中的数据保存到硬盘。比如“save m n”。表示m秒内数据集存在n次修改时,自动触发bgsave。

    默认如下配置:

    表示900 秒内如果至少有 1 个 key 的值变化,则保存save 900 1;表示300 秒内如果至少有 10 个 key 的值变化,则保存save 300 10;表示60 秒内如果至少有 10000 个 key 的值变化,则保存save 60 10000

    不需要持久化,那么你可以注释掉所有的 save 行来停用保存功能。

    **②stop-writes-on-bgsave-error :**默认值为yes。当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了

    **③rdbcompression ;**默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。

    **④rdbchecksum :**默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。

    **⑤dbfilename :**设置快照的文件名,默认是 dump.rdb

    **⑥dir:**设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。

优点
  1. RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复。
  2. 生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
  3. RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
缺点

RDB快照是一次全量备份,存储的是内存数据的二进制序列化形式,存储上非常紧凑。当进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据。

AOF机制

以追加的方式记录数据的变动,每次有写命令就会存到aof文件中

这种方式会造成文件越来越大,为了压缩aof文件,redis 提供了bgrewriteaof命令。将内存中的数据以命令的方式保存到临时文件中,同时会fork出一条新进程来将文件重写。

触发机制

  1. 每修改同步always:同步操作,每次数据变化都会同步,数据完整度高,性能差
  2. 每秒同步everysec:异步操作,每秒记录一次,如果期间宕机会有数据丢失
  3. 不同步no:从不同步
优点
  1. AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据。
  2. AOF日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损。
  3. AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。
  4. AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据
缺点
  1. 对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大
  2. AOF开启后,支持的写QPS会比RDB支持的写QPS低,因为AOF一般会配置成每秒fsync一次日志文件,当然,每秒一次fsync,性能也还是很高的
  3. 以前AOF发生过bug,就是通过AOF记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来。

两种机制配合使用,选择最合适的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值