java-redis理论及与springboot整合

如果有新知识,需要完善的知识,本人表达错误的地方,等等。欢迎大家留言,一起进步。

在博客 java-完整项目搭建 中有具体应用。

章节目录:

  一:基础

  二:持久化

  三:架构

  四:搭建

  五:与springboot整合

一:基础

  k.v形式,基于内存,支持多种类型。

  具体的参考网络上资料。

二:持久化

  2.1 为什么持久化?

    redis是基于内存的数据库,所以在机器宕机,或者服务重启,内存数据就不在了,所以需要持久化到磁盘。

  2.2 一定要持久化吗?

    如果不持久化,那么就相当于要重新查数据,缓存数据了。

    如果持久化了,重启服务时候恢复了之前持久化的数据,那么redis就接着之前的继续使用了。

  2.3 怎么持久化?

     2.3.1 持久化的方式

      有两种:RDB(默认方式)与AOF(默认是关闭的)

      1:RDB方式(数据快照)

        1.1 在默认情况下,Redis 将数据快照保存在名字为 dump.rdb的二进制文件中,启动redis时候加载dump.rdb文件即可。

        1.2 执行方式

          1,阻塞方式(即 save ,阻塞Redis服务,无法响应客户端请求,好处是数据不丢失):

             过程:客户端中发起save命令 -->

                服务端停止客户端的服务请求,创建新的rdb文件,然后替换掉旧文件 -->

                服务端恢复客户端的服务请求。

               

          2,非阻塞方式(即 bgsave ,坏处是可能造成数据丢失)

             过程:客户端发起bgsave命令 -->

                服务端会 fork() (创建)一个子进程 -->

                这个子进程开始创建rdb文件 -->

                创建完成通知父进程 -->

                父进程将新的rdb文件替换旧文件。

                

                

         1.3 执行策略

          1,自动(本质为bgsave)

            默认的配置(配置文件 redis.conf 中):(只要满足下面三个的任意一个就会执行持久化操作)

              save 900 1

              save 300 10

              save 60 10000

              创建rdb文件之后,时间计数器和次数计数器会清零。所以多个条件的效果不是叠加的。

              说明:例如 save 60 1000,Redis要满足在60秒内至少有1000个键被改动,会自动保存一次;

          2,手动

            在客户端发起save,bgsave命令

          3,比较save,bgsave   

            save 不用创建新的进程,速度略快,但是会阻塞,不适合线上运行。

            bgsave 需要创建子进程,消耗额外的内存,但是是非阻塞,适合线上运行。

        1.4 优缺点        

          优点

            可以设置rdb文件的归档时间,可以恢复到不同的版本;

            单一的rdb文件,很容易远程传输,适合灾难恢复;

            恢复大数据集速度比AOF快;

          缺点

            会丢失最近写入、修改的而未能持久化的数据;

            fork过程较为耗时,会造成毫秒级客户端请求的延迟;

      

      2:AOF方式(日志的记录方式)

        2.1 默认文件appendonly.aof记录所有的写操作命令,在服务启动的时候使用这些命令就可以还原数据库,

           调整AOF持久化策略,可以在服务出现故障时,不丢失任何数据,也可以丢失一秒的数据。相对于RDB损失小得多

        2.2 执行机制

          1 AOF写入机制

            1.1:什么是写入机制:

              日志先写入内存缓冲区(buffer),等buffer满了,或者执行fsync或fdatasync时写入磁盘appendonly.aof文件。所以如果内存缓冲区的数据未写入磁盘时宕机,

            那么就会数据丢失。

            1.2:写入磁盘的策略:(appendfsync选项控制,选项值有:always、everysec、no

              always:(运行速度慢,不会产生数据丢失)

                服务器写入一个指令就调用一次fdatasync,即写入磁盘。

              everysec:(默认选项,运行速度快,如果宕机可能会丢失1s的数据)

                服务器每1s就执行一次fdatasync。

              no:(运行速度快,丢失数据有不确定性)

                服务器不会主动调用fdatasync,由操作系统决定何时调用。  

          2 AOF重写机制

            2.1:什么是重写机制:

              aof文件中会写入很多重复的命令,导致文件也会很大。重写机制会合并一些重复的命令,使用尽量少的命令记录。

            2.2:重写过程

              首先fork(创建)一个子进程负责重写aof工作。

              子进程创建一个临时文件用于写入aof信息。

              父进程开辟一个内存缓冲区写入新的aof命令。

              子进程重写好aof文件之后,会通知父进程,父进程再把缓冲区新的aof命令交由子进程写入新临时文件。新临时文件会替换旧临时文件。

              注意:如果中途发生故障,可以通过redis-check-aof工具修复。

            2.3 重写机制触发方式

              1,手动

                客户端向服务端发出bgrewriteaof命令

              2,自动(通过配置文件中选项,自动执行bgrewriteaof命令)

                auto-aof-rewrite-min-size <size>:(触发AOF重写所需的最小体积)

                  只要在AOF文件的体积大于等于size时,才会考虑是否需要进行AOF重写,这个选项用于避免对体积过小的AOF文件进行重写

                auto-aof-rewrite-percentage<percent>:(指定触发重写所需的AOF文件体积百分比)

                  实际上就是对AOF的一个扩容,即当aof达到一个程度没办法再进行压缩重写时候,就按照这个比例对AOF扩容           

                举例:

                  auto-aof-rewrite-min-size 64mb    当AOF文件大于64MB时候,可以考虑重写AOF文件

                  auto-aof-rewrite-percentage 100     只有当AOF文件的增量大于起始size的100%时(就是文件大小翻了一倍),启动重写

                  appendonly  no  或者 yes        默认关闭,请开启

        2.3 优缺点      

          优点:

            写入机制,默认fysnc每秒执行,性能很好不阻塞服务,最多丢失一秒的数据;

            重写机制,优化AOF文件;

          缺点:

            相同数据集,AOF文件体积较RDB大很多;

            恢复数据库速度叫RDB慢(文本,命令重演);

            开启AOF操作后,默认不再使用RDB作为备选,但二者仍然可以同时开启;

三:架构(2.0版本架构与3.0版本架构)

  1:2.0版本架构(主从架构)

    1.1 角色  

      主节点(master):增删改查(指的是处理客户端请求)

      从节点(slaves):查(可以处理客户端的查询请求)

      高可用(Sentinel  哨兵):实现主从的转换等,可以监控多个集群,哨兵需要做HA

    1.2 集群配置:参考网络资料

    1.3 哨兵搭建

      1.3.1:Sentinel 配置文件说明:

        port 26379  //哨兵默认的端口号是26379

        sentinel monitor mymaster 127.0.0.1 6379 2    //sentinel monitor哨兵监控

                              //mymaster 要监控的集群名称(随便起的,用来区分)

                              //127.0.0.1 6379监控的master

                              //2两个哨兵同意才重新选举master

      1.3.2:Sentinel 配置举例

          1 cp src/redis-sentinel  redis/bin  //将redis的哨兵执行文件cp到bin下

          2 创建目录,在目录下创建配置文件(此处创建3个哨兵,需要3个配置文件)

            找个目录下创建sentinel1.conf,sentinel2.conf,sentinel3.conf:

            sentinel1.conf的内容为:

              port 26379

              Sentinel monitor s1 127.0.0.1 6380 2  //哨兵监控s1这个集群的6380的这个,2个哨兵同意之后就可以主从切换

            sentinel2.conf的内容为:

              Port 26380

              Sentinel monitor s1 127.0.0.1 6380 2

            Sentinel3.conf的内容为:

              Port 26381

              Sentinel monitor s1 127.0.0.1 6380 2

          3 启动redis集群(到创建三个配置文件的目录下)

            $redis-sentinel sentinel1.conf

            $redis-sentinel sentinel2.conf

            $redis-sentinel sentinel3.conf

    1.4 存在的问题

      问题1:从节点掉线,读能力下降

      问题2:主节点掉线,写能力缺失(解决:可以使用哨兵)

      问题3:只有一个主节点才有写的能力,所以写的性能是个问题

          解决思路:让多个节点处理。

          使用:twemproxy(代理模式,具体可以查询网络资料,本人觉得3.0版本架构更胜一筹)。

  2:3.0版本架构(集群架构)

    2.1 架构

      由多个节点的redis集群组成,每个节点上都有master(用于存储数据)与slave(是master的复制品,可以有多个副本)

      

      图中:7000 7001 7002为主节点,7003 7004 7005为相对应的复制节点    

      问题:如果7000掉线,怎么切换到7003的呢?这里就不用sentinel(哨兵)了,由集群中其他master决定,即7001 7002。

    2.2 数据存储方式(槽位的形式,分担了数据写的压力)

        集群分片说明:Slot(槽位),集群将整个数据库分为16384个槽位,有多少个集群节点,就会来平分这些槽位。

 

        举个栗子:有个数据进来了,那么我们去到key值,通过公式 crc16(key)%16384 (crc16为16位的循环冗余校验和函数,我也不懂...,就是一个函数),

             来计算出槽位,然后找个处理这个数据对应的节点是哪个。

四:搭建(搭建版本3.0版本)

  1 安装与环境:

    1、创建目录redis-cluster,在redis-cluster下解压安装redis3.0(tar xf redis.....gz);

    2、在redis的目录下安装redis3.0(make && make PREFIX=/opt/redis install)意思是将redis安装到/opt/redis目录下;

    3、安装ruby编译环境(ruby脚本进行槽位的分发

      # yum -y install ruby rubygems

    4、在redis-cluster安装目录下安装 redis gem 模块(安装gem块进行槽位分发)

      # gem install --local redis-3.3.0.gem(生成出 redis-trib.rb 文件)

 

  2 搭建:

    5、创建配置文件 redis.conf :

      步骤:

        mkdir cluster-test

        cd cluster-test

        mkdir 7000 7001 7002 7003 7004 7005

        在7000-7005每个目录中均放入redis的redis.conf

          redis.conf内容如下:

            cluster-enabled yes  //集群方式集群

            port 700X //这台的端口

      说明:

        指定3个主节点端口为7000、7001、7002,对应的3个从节点端口为7003、7004、7005

     5、创建配置文件redis.conf(启集群模式: 3.0 支持单机集群,但必须在配置文件中说明) (已完成)

      指定不冲突的端口 及 <对应端口号>

      文件内容:

        声明支持集群模式,指定端口:

          在7000-7005每个目录中均放入redis.conf

          redis.conf内容如下:

            cluster-enabled yes  //集群方式集群

            port 700X //这台的端口

    6、创建集群,槽位认领

      在安装目录下的src中,找到 redis-trib.rb 这是rubby脚本执行程序,完成redis3.0集群创建

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

      执行之后,自动分配了主从,自动分配了slots,所有槽都有节点处理,集群状态上线

    7、6个实例分别读取6个配置文件(启动redis)

      # cd 700x

      # redis-server redis.conf

     8、查看运行状态

      # ss -tanl | grep 700  

    9、客户端连接(一般应用是整合springboot使用)

      # redis-cli -p 7000 -c

五:与springboot整合

  1:jar包依赖(使用RedisTemplate工具包)

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

  2:application.properties文件配置

# Redis数据库索引(默认为0)
spring.redis.database=0  
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379  
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8  
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1  
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8  
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0  
# 连接超时时间(毫秒)
spring.redis.timeout=0  

   3:在@configuration中定义bean,可自定义连接工厂JedisConnectionFactory。

    设置连接的集群,序列化设置等。(具体设置参考网络资料)

  4:使用@Component创建工具类(这里只写引入redisTemplate方式)

@Component
public class RedisService {
    //方式1:自动装配,使用时候需要创建对象
    @Autowired
    private RedisTemplate<String, String> stringRedisTemplate;
    //方式2:创建静态的,使用时候直接使用即可
    private static RedisTemplate<String, Object> objectRedisTemplate;
    @Autowired //方法名称是随意的
    public void setObjectRedisTemplate(ObjectRedisTemplate objectRedisTemplate){
       this.objectRedisTemplate = objectRedisTemplate;
    }
}

    

原文地址:https://www.cnblogs.com/dblog/p/12202662.html