redis之全面解析

1、什么是redis?

redis是一种基于内存并且可以持久化到硬盘的key-value型noSql数据库,支持丰富的数据类型如:String、List、Set、ZSet、Hash五种数据类型。是单线程,单进程,不支持并发操作,因为速度非常之快(Redis读的速度是110000次/s,写的速度是81000次/s), 所以也可称做宏观并行,微观串行。

2、单机和集群搭建

2.1 单机版(仅仅入门学习使用)

环境准备:centos7、redis-4.0.10.tar.gz
①关闭防火墙

systemctl stop firewalld
systemctl disable firewalld

②安装redis环境依赖

yum install gcc

③解压缩包

tar -zxf  redis-4.0.10.tar.gz

④进入解压文件夹,编译

[root@localhost redis-4.0.10]# make

⑤修改redis.conf文件

[root@localhost redis-4.0.10]# vim redis.conf 
daemonize yes  #开启后台运行模式
protected-mode no  #开启远程访问权限
bind 0.0.0.0  #或者将这一行注释掉

⑥启动redis

[root@localhost src]# ./redis-server ../redis.conf 
# 因为前面配置文件修改为后台运行模式,所以验证是否成功查看redis进行号
[root@localhost src]# ps aux | grep "redis"
root      1490  0.0  1.0 145312  7664 ?        Ssl  12:23   0:00 ./redis-server *:6379
root      1495  0.0  0.1 112724   996 pts/0    R+   12:23   0:00 grep --color=auto redis

⑦启动客户端,默认端口 6379,也可以在redis.conf文件修改端口号,启动指令如:./redis-cli -p 7000

[root@localhost src]# ./redis-cli

2.2 redis集群版(本地模拟,采用三主三从模式)

①关闭防火墙

systemctl stop firewalld
systemctl disable firewalld

②安装redis集群环境

yum install ruby
gem install redis-3.2.1.gem

③解压缩

tar -zxf  redis-4.0.10.tar.gz

④编译

[root@localhost redis-4.0.10]# make

⑤模拟不同的端口,所以咱们在解压目录下建立文件夹来模拟,每个文件下防止redis.conf文件即可,修改配置文件

cluster-enabled  yes 	        	//开启集群模式
cluster-config-file  nodes-7000.conf 		//集群节点配置文件
cluster-node-timeout  5000      	//集群节点超时时间
appendonly  yes 

⑥启动节点7000~7005

[root@localhost src]# ./redis-server ../7000/redis.conf

⑦覆盖所有的hash槽

./redis-trib.rb create --replicas 1 192.168.139.172:7000 192.168.139.172:7001 192.168.139.172:7002 192.168.139.172:7003 192.168.139.172:7004 192.168.139.172:7005

在这里插入图片描述
⑧查看集群节点状态

./redis-trib.rb check 192.168.139.172:7000

⑨启动客户端

./redis-cli -c -p 7000

3、redis持久化机制

redis提供了两种不同的持久化机制,分别是快照(snapShotting)AOF追加文件(append only file)

3.1 快照(snapShotting)

这种方式可以将某一时刻的所有数据都写入硬盘中,当然这也是redis的默认持久化方式,因为保存的文件是以.rdb形式结尾的文件,因此这种方式也称之为RDB方式。

在redis.conf中,我们可以看到.rdb的文件名

也可以看到该文件的保存路径:
在这里插入图片描述
快照的创建方式有两种,分别是BGSAVESAVE
(a)SAVE
可以使用SAVE命令来创建一个快照,接收到SAVE命令的redis服务器在快照创建完毕之前将不再响应任何其他的命令也就是我们所说的阻塞状态。当redis通过shutdown指令接收到关闭服务器的请求时,会执行一个save命令,阻塞所有的客户端,不再执行客户端执行发送的任何命令,并且在save命令执行完毕之后关闭服务器。

(b)BGSAVE
可以使用BGSAVE命令来创建一个快照,当接收到客户端的BGSAVE命令时,redis会调用fork来创建一个子进程,然后子进程负责将快照写入磁盘中,而父进程则继续处理命令请求。

fork作用:创建父、子线程,当有客户端调用时,父、子线程分别独立使用自己的内存,当客户端没有调用时,子线程占用父线程的内存处理快照。
在这里插入图片描述
在redis.confZ中可以设置快照的执行周期,如图默认配置的意思是: 是1分钟内改了1万次,或5分钟内改了10次,或15分钟内改了1次。

快照方式缺点分析:在非正常退出如redis进程直接被kill的情况,还没来得及快照会导致数据丢失。

3.2 AOF追加文件(append only file)

在redis的默认配置中AOF持久化机制是没有开启的,AOF持久化会将被执行的写命令写到AOF的文件末尾,以此来记录数据发生的变化,因此只要redis从头到尾执行一次AOF文件所包含的所有写命令,就可以恢复AOF文件的记录的数据集。

开启AOF持久化机制,在redis.conf中,将appendonly改为yes
在这里插入图片描述
也可以指定生成的aof文件名
在这里插入图片描述
指定日志更新的频率
在这里插入图片描述

选项 同步频率
always 每个redis写命令都要同步写入硬盘,严重降低redis速度
everysec 每秒执行一次同步显式的将多个写命令同步到磁盘
no 由操作系统决定何时同步

分析:如果使用了always选项,那么每个redis写命令都会被写入硬盘,而将发生系统崩溃时出现的数据丢失减到最少,遗憾的是,因为这种同步策略需要对硬盘进行大量的写入操作,所以redis处理命令的速度会受到硬盘性能的限制。
注意 : 转盘式硬盘在这种频率下200左右个命令/s ; 固态硬盘(SSD) 几百万个命令/s;
警告 : 使用SSD用户请谨慎使用always选项,这种模式不断写入少量数据的做法有可能会引发严重的写入放大问题,导致将固态硬盘的寿命从原来的几年降低为几个月

为了兼顾数据安全和写入性能,可以考虑使用everysec选项,让redis每秒一次的频率对AOF文件进行同步,redis每秒同步一次AOF文件时性能和不使用任何持久化特性时的性能相差无几,而通过每秒同步一次AOF文件,redis可以保证即使系统崩溃,最多丢失一秒之内产生的数据(推荐使用这种方式)。

最后使用no选项,将完全有操作系统决定什么时候同步AOF日志文件,这个选项不会对redis性能带来影响但是系统崩溃时,会丢失不定数量的数据,另外如果用户硬盘处理写入操作不够快的话,当缓冲区被等待写入硬盘数据填满时,redis会处于阻塞状态,并导致redis的处理命令请求的速度变慢(不推荐使用)

AOF文件重写

aof 的方式也同时带来了另一个问题,持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态其实文件中保存一条set test 100就够了。为了压缩aof的持久化文件Redis提供了两种AOF重写机制,分别是:BGREWRITEAOF配置文件redis.conf中的auto-aof-rewrite-percentage

(1)执行BGREWRITEAOF指令:直接在客户端执行该命令就可以。
(2)redis.conf中的auto-aof-rewrite-percentage
在这里插入图片描述
说明:该行动自动执行BGREWRITEAOF.,如图auto-aof-rewrite-percentage值为100和auto-aof-rewrite-min-size 64mb,并且启用的AOF持久化时,那么当AOF文件体积大于64M,并且AOF文件的体积比上一次重写之后体积大了至少一倍(100%)时,会自动触发,如果重写过于频繁,可以考虑将auto-aof-rewrite-percentage设置为更大。

AOF重写过程
①redis调用fork ,现在有父子两个进程,子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令。
②父进程继续处理client请求,除了把写命令写入到原来的aof文件中,同时把收到的写命令缓存起来,这样就能保证如果子进程重写失败的话并不会出问题。
③当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程,然后父进程把缓存的写命令也写入到临时文件。
④现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。

4、redis分布式缓存

现有mybatis缓存的缺点:
①mybatis属于本地缓存,项目放入服务器中会占用服务器的内存。
②分布式环境中,无法做到缓存共享。

mbatis在操作增删改时会自动清空缓存
利用redis做分布式缓存代码,java语言描述
工具类,因为咱们自己实现的缓存,所以SpringBoot无法扫描到咱们自己实现的类,也就无法使用自动注入

package com.chinaTelecom.cache;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringContextUtil implements ApplicationContextAware {
    
    

   private static ApplicationContext context;
   @Override
   public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    
       context = applicationContext;
   }

   //获取整个工厂对象
   public static ApplicationContext getContext(){
    
    
       return context;
   }

   //根据名称获取指定bean
   public static Object getBean(String name){
    
    
       return context.getBean(name);
   }

   //根据类型获取指定bean
   public static Object getBean(Class clazz){
    
    
       return context.getBean(clazz);
   }

   //根据名称和类型获取
   public static Object getBean(String name,Class clazz){
    
    
       return context.getBean(name,clazz);
   }
}

功能实现代码

package com.chinaTelecom.cache;


import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class MybatisRedisCache implements Cache {
    
    

    private final String id; //id: 包名.DAO名,即mapper文件的namespace
    private final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    public MybatisRedisCache(String id) {
    
    
        this.id = id;
    }

    @Override
    public String getId() {
    
    
        return null;
    }

    /**
     * @param key : 唯一值,代表方法唯一
     * @param value: 结果
     */
    @Override
    public void putObject(Object key, Object value) {
    
    
        RedisTemplate redisTemplate = (RedisTemplate)SpringContextUtil.getBean("redisTemplate");
        redisTemplate.opsForHash().put(id,key,value);
    }

    @Override
    public Object getObject(Object key) {
    
    
        RedisTemplate redisTemplate = (RedisTemplate)SpringContextUtil.getBean("redisTemplate");
        return redisTemplate.opsForHash().get(id,key);
    }

    @Override
    public Object removeObject(Object key) {
    
    
        RedisTemplate redisTemplate = (RedisTemplate)SpringContextUtil.getBean("redisTemplate");
        return redisTemplate.opsForHash().delete(id,key);
    }

    @Override
    public void clear() {
    
    
        RedisTemplate redisTemplate = (RedisTemplate)SpringContextUtil.getBean("redisTemplate");
        redisTemplate.opsForHash().delete(id);
    }

    @Override
    public int getSize() {
    
    
        RedisTemplate redisTemplate = (RedisTemplate)SpringContextUtil.getBean("redisTemplate");
        return redisTemplate.opsForHash().size(id).intValue();
    }

    @Override
    public ReadWriteLock getReadWriteLock() {
    
    
        return reentrantReadWriteLock;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_44962429/article/details/113754733