Redis的安装与应用

155 阅读9分钟

Redis

Redis简介

Remote Dictionary Server(Redis)是一个开源的使用 ANSIC 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。

它通常被称为数据结构服务器,因为值(value)可以是字符串(String), 哈希(Map),列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

Redis的特点

  1. 支持多种数据结构,如 string(字符串)、 list(双向链表)、dict(hash 表)、set(集合)、zset(排序 set)、hyperloglog(基数估算)等等
  2. 支持持久化操作,可以进行 aofrdb 数据持久化到磁盘,从而进行数据备份或数据恢复等操作,较好的防止数据丢失的手段。
  3. 支持通过 Replication 进行数据复制,通过 master-slave 机制,可以实时进行数据的同步复制,支持多级复制和增量复制,master-slave 机制是 Redis 进行 HA 的重要手段。
  4. 单进程请求,所有命令串行执行,并发情况下不需要考虑数据一致性问题(解决超卖问题)。

Redis单机版的安装

第一步

需要在linux系统中安装gcc

yum install -y gcc-c++

第二步

需要将下载好的 redis 压缩包添加到 linux 服务器(官网下载 redis.io/download)

第三步

解压压缩包

tar -zxvf   redis......(这是你要解压的redis名字)

第四步

编译redis

进入 redis 的解压完毕的根目录下 执行命令:make

第五步

安装 redis

进 入 redis 的 解 压 完 毕 的 根 目 录 下 ,执 行 命 令 :
make install PREFIX=/usr/local/redis

第六步

启动 redis

  1. 前端启动

    在 bin 目录下执行命令:  ./redis-server  
    (ctrl+c)退出 redis
    

image-20220123172033364.png

出现上面的表示redis启动成功,但是启动之后我们在当前环境下没法进行其他的操作,所以要改成后端启动

  1. 后端启动

    • 先将 redis 解压目录下的 redis.conf 文件拷贝到 安装好的 redis 的 bin 目录下命令

      cp redis.conf /usr/local/redis/bin
      
    • 修改拷贝过来的 redis.conf 配置文件

      vim redis.conf
      将daemonize no  改为 yes
      
    • 启动 redis:在后台启动redis bin 目录下执行

      ./redis-server redis.conf
      
    • 查看 redis 启动是否成功

      ps aux|grep redis
      

      image-20220123173706104.png

    • 进入redis

      ./redis-cli
      
    • 关闭 redis 的命令

      ./redis-cli shutdown
      
    • 第七步:测试 redi

      在bin 目录下启动 redis 自带的客户端 
      ./redis-cli
      后可以开始操作redis的数据了
      常见 redis 命令: 
      ping返回>pong   
      说明客户端连接上了redis
      

Redis数据类型

String(字符串)

Redis 字符串是字节序列。Redis 字符串是二进制安全的,这意味着他们有一个已知的

127.0.0.1:6379> set name xiao
OK
127.0.0.1:6379> get name
"xiao"
127.0.0.1:6379> 
  • incr 让当前键值以 1 的数量递增,并返回递增后的值
  • incrby 可以指定参数一次增加的数值,并返回递增后的值
  • decr 让当前键值以 1 的数量递减 并返回递减后的值
  • decrby 可以指定参数一次递减的数值,并返回递减后的值
  • incrbyfloat 可以递增一个双精度浮点数
  • append 作用是向键值的末尾追加 value。如果键不存在则将该键的值设置为 value。返回值是追加后字符串的总长度。
  • mget/mset 作用与 get/set 相似,不过 mget/mset 可以同时获得/设置多个键的键值
  • del 根据 key 来删除 value
  • keys * :显示当前redis中所有的key

Hash哈希

image-20220123204613807.png Redis 的哈希是键值对的集合。 Redis 的哈希值是字符串字段和字符串值之间的映射,因此它们被用来表示对象。

  • hset 存储一个哈希键值对的集合
  • hset key field value
  • hget 获取一个哈希键的值
  • hget key field
  • hmset 存储一个或多个哈希是键值对的集合
  • hmset key field1 value1 ......fieldN keyN
  • hmget 获取多个指定的键的值
  • hmget key field1 ... fieldN
  • hexists 判断哈希表中的字段名是否存在 如果存在返回 1 否则返回 0
  • hexists key field
  • hdel 删除一个或多个字段
  • hdel key field
  • hgetall 获取一个哈希是键值对的集合
  • hgetall key
  • hvals 只返回字段值
  • hvals key

List链表

image-20220123210858757.png

Redis 的链表是简单的字符串列表,排序插入顺序。可以添加元素到 Redis 的列表的头部或尾部(可以理解为java中linkedlist)队列的特点:FIFO(先进的先出)

image-20220123210921777.png

  • lpush key value 向链表左侧添加
  • rpush key value 向链表右侧添加
  • lrange key index1 index2 遍历链表:取index1到index2之间的元
  • lrange c 0 -1 遍历整个c键中的元素-1代表索引最后一个
  • lpop 从链表的左边取数据,并且移除
  • rpop 从链表的右边取数据,并且移除
  • lindex key indexnumber 如果要将链表类型当做数组来用,lindex 命令是必不可少的。
  • lindex 命令用来返回指定索引的元素,索引从 0 开始,如果是负数表示从右边开始计算的索引,最右边元素的索引是-1
  • Lset key indexnumber value 是另一个通过索引操作列表的命令,它会将索引为 index 的元素的值修改为 value。

Redis的数据持久化

RDB方式(默认的备份方式)

RDB 方式:将 Redis 在内存中的数据库状态保存到磁盘里面,RDB 文件是一个经过压缩的二进制文件,通过该文件可以还原生成 RDB 文件时的数据库状态(默认下,持久化到 dump.rdb 文件,并且在 redis 重启后,自动读取RDB文件中的数据,据悉,通常情况下一千万条字符串类型键,或1GB大小 的快照文件,同步到内存中的时间是 20-30 秒)

生成方式

  1. 执行命令手动生成

    有两个 Redis 命令可以用于生成 RDB 文件,一个是 SAVE,另一个是 BGSAVE;

    • SAVE 命令会阻塞 Redis 服务器进程,直到 RDB 文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求
    • BGSAVE 命令会派生出一个子进程,然后由子进程负责创建 RDB 文件,服务器进程(父进程)继续处理命令请求,创建 RDB 文件结束之前,客户端发送的 BGSAVE 和 SAVE 命令会被服务器拒绝
  2. 通过配置自动生成(默认开启)

    可以设置服务器配置的 save 选项,让服务器每隔一段时间自动执行一次 BGSAVE 命令,可以通过 save 选项设置多个保存条件,但只要其中任意一个条件被满足,服务器就会执行 BGSAVE 命令

    例如:

    save 900 1
    ​
    save 300 10
    ​
    save 60 10000
    

    那么只要满足以下三个条件中-的任意一个,save 命令就会被执行服务器在

    900 秒之内,对数据库进行了至少 1 次修改服务器在

    300 秒之内,对数据库进行了至少 10 次修改服务器在

    60 秒之内,对数据库进行了至少 10000 次修改

AOF方式

AOF 持久化方式在 redis 中默认是关闭的,需要修改配置文件开启该方式。

AOF:把每条命令都写入文件,类似 mysql 的 binlog 日志

AOF 方式:是通过保存 Redis 服务器所执行的写命令来记录数据库状态的文件。

AOF 文件刷新的方式,有三种存储策略:

appendfsync always  
​
每提交一个修改命令都调用 fsync 刷新到 AOF 文件,非常非常慢,但也非常安全
appendfsync everysec 
​
每秒钟都调用 fsync 刷新到 AOF 文件,很快,但可能会丢失一秒以内的数据
appendfsync no 
​
依靠 OS(操作系统) 进行刷新,redis 不主动刷新 AOF,这样最快,但安全性就差默认并推荐每秒刷新,这样在速度和安全上都做到了兼顾

我使用的是第三种 image-20220124205513730.png

手动开启aop持久化方案,数据不仅会持久化到aop中,还有持久化到rdb中,这样的持久化数据有两份,但是aop开启的时候,数据恢复是从aop文件中恢复的,只有在关闭(appendonly no)时,才会从rdb中恢复数据

AOF 数据恢复方式

服务器在启动时,通过载入和执行 AOF 文件中保存的命令来还原服务器关闭之前的数据库状态,具体过程:

  1. 载入AOF文件模拟客户端从AOF 文件中读取命令使用模拟客户端执行命令循环读取并执行命令,直到全部完成。

  2. 如果同时启用了 RDB 和 AOF 方式,AOF 优先,启动时只加载 AOF 文件恢复数据,想要加载RDB需要把AOF关闭。

Redis在SpringBoot中的应用

  1. 首先将redis需要的包导入

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
        <version>2.6.3</version>
    </dependency>
    
  2. 将redis连接的配置文件application.yml配置好

    spring:
      redis:
        host: 填入你的IP
        port: 6379
    
  3. 需要对redis.conf中进行修改,不然会连接失败

    进入文件将bind 127.0.0.1 -::1注释
    protected-mode yes 改为 protected-mode no
    
  4. 准备工作做完之后,便可以测试redisTemplate的方法了

    package com.my;
    ​
    import com.my.redis.RedisApp;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.test.context.junit4.SpringRunner;
    ​
    import java.io.Serializable;
    import java.util.Set;
    import java.util.concurrent.TimeUnit;
    ​
    //表示基于springboot的测试依赖当前工程的启动类
    @SpringBootTest(classes = RedisApp.class)
    @RunWith(SpringRunner.class)
    public class MyTest {
        //注入可以操作redis的模板对象
        @Autowired
        private RedisTemplate redisTemplate;
    ​
        /**
         * 操作String类型
         */
        @Test
        public void testRedis_String(){
            Student student = new Student("小明", 18);
            //设置键为name,值为lili
            redisTemplate.boundValueOps("name").set("lili");
            redisTemplate.boundValueOps("age").set(19);
            redisTemplate.boundValueOps("student").set(student);
            //取数据,默认提升为Object需要转换
            String name = (String)redisTemplate.boundValueOps("name").get();
            Integer age = (Integer)redisTemplate.boundValueOps("age").get();
            //取出存入的对象
            Student stu = (Student)redisTemplate.boundValueOps("student").get();
            System.out.println(stu);
            System.out.println(name);
            System.out.println(age);
    ​
            /**
             * 设置过期时间
             * 第一个参数时数目
             * 第二个参数时单位
             */
            redisTemplate.boundValueOps("name").expire(1, TimeUnit.MINUTES);
    ​
            //删除key
            Boolean delete = redisTemplate.delete("age");
    ​
        }
    ​
    ​
        /**
         * 操作Hash类型
         */
        @Test
        public void testRedis_Hash(){
            redisTemplate.boundHashOps("user").put("name","小明");
            //获取到值
            String name = (String)redisTemplate.boundHashOps("user").get("name");
        }
    ​
    ​
        /**
         * 操作List类型
         */
        @Test
        public void testRedis_List(){
            redisTemplate.boundListOps("cities").leftPush("北京");
            redisTemplate.boundListOps("cities").rightPush("上海");
            //获取到值
            String city1 = (String)redisTemplate.boundListOps("cities").leftPop();
            String city2 = (String)redisTemplate.boundListOps("cities").rightPop();
        }
    ​
    ​
        /**
         * 操作Set类型
         */
        @Test
        public void testRedis_Set(){
            redisTemplate.boundSetOps("province").add("湖南");
            redisTemplate.boundSetOps("province").add("湖北");
            redisTemplate.boundSetOps("province").add("广西","广东");
            //打印集合
            Set province = redisTemplate.boundSetOps("province").members();
            
        }
    ​
    }
    //存入redis的对象一定要序列化
    class Student implements Serializable {
        private String name;
        private int age;
    ​
        public Student() {
        }
    ​
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    ​
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + ''' +
                    ", age=" + age +
                    '}';
        }
    }
    ​
    

    当你在虚拟机redis中查看时会发现key带有前缀,是因为RedisTemplate默认使用反序列化工具,但不影响我们使用,这也是为什么在构建POJO类时需要让他可序列化

image-20220125194125083.png