Redis基础理解

一、Redis 简介

1. NoSQL 简介

目前市场主流数据存储都是使用关系型数据库。每次操作关系型数据库时都是 I/O 操作,I/O 操作是主要影响程序执行性能原因之一,连接数据库关闭数据库都是消耗性能的过程。关系型数据库索引数据结构都是树状结构,当数据量特别大时,导致树深度比较深,当深度深时查询性能会大大降低。尽量减少对数据库的操作,能够明显的提升程序运行效率。

针对上面的问题,市场上就出现了各种 NoSQL(Not Only SQL,不仅仅可以使用关系型数据库)数据库,它们的宣传口号:不是什么样的场景都必须使用关系型数据库,一些特定的场景使用 NoSQL 数据库更好。

常见 NoSQL 数据库:

  • memcached:键值对,内存型数据库,所有数据都在内存中。
  • Redis:和 Memcached 类似,还具备持久化能力。
  • HBase:以列作为存储。
  • MongoDB:以 Document 做存储。

2. Redis 简介

Redis 是以 Key-Value 形式进行存储的 NoSQL 数据库。

Redis 是使用 C 语言进行编写的。

平时操作的数据都在内存中,效率特高,读的效率11 0000次/s,写8 1000次/s,所以多把 Redis 当做缓存工具使用(在一些框架中还把 Redis 当做临时数据存储工具)。

缓存工具:把数据库中数据缓存到 Redis 中,由于 Redis 读写性能较好,访问 Redis 中数据,而不是频繁访问数据库中数据。

Redis 以 slot(槽)作为数据存储单元,每个槽中可以存储N多个键值对。Redis 中固定具有16384。理论上可以实现一个槽是一个 Redis。每个向 Redis 存储数据的 key 都会进行 crc16算法得出一个值后对16384取余就是这个 key 存放的 solt 位置。

虽然槽的大小是不固定的,但是 Redis 一个键值对最大大小为512M(String 类型 Value)

3. Redis 的安装与启动

解压:Redis-x64-5.0.14.1.zip 即可安装 Redis。

启动方式:

二、Redis 作为缓存工具时的流程

  1. 应用程序向 Redis 查询数据
  2. 判断 Key 是否存在
  3. 是否存在    存在 : 把结果查询出,返回数据给应用程序
  4. 不存在 : 向 MySQL 查询数据
    1. 把结果缓存到 Redis 中
    2. 把数据返回给应用程序

三、Redis 数据类型

Redis 中数据是 key-value 形式。不同类型 Value 是有不同的命令进行操作。key 和 value 都支持下面类型(在代码中多把 key 设置为 String 类型):

  • String 字符串
  • Hash 哈希表
  • List 列表
  • Set 集合
  • Sorted Set 有序集合

1. key 操作

1.1. exists

判断 key 是否存在。

语法:exists key名称

返回值:存在返回数字,不存在返回0

1.2. expire

设置 key 的过期时间,单位秒

语法:expire key 秒数

返回值:成功返回1,失败返回0

1.3. ttl

查看 key 的剩余过期时间

语法:ttl key

返回值:返回剩余时间,如果不过期返回-1

1.4. del

根据 key 删除键值对。

语法:del key

返回值:被删除 key 的数量

1.5. keys

命令:keys *

查看所有存在的 key

2. 字符串值(String)

2.1. set

设置指定 key 的值。如果 key 不存在是新增效果,如果 key 存在是修改效果。键值对是永久存在的。

语法:set key value

返回值:成功 OK

2.2. get

获取指定 key 的值

语法:get key

返回值:key 的值。不存在返回 nil

2.3. setnx

当且仅当 key 不存在时才新增。恒新增,无修改功能。

语法:setnx key value

返回值:不存在时返回1,存在返回0

2.4. setex

设置 key 的存活时间,无论是否存在指定 key 都能新增,如果存在 key 覆盖旧值。同时必须指定过期时间。

语法:

语法:setex key seconds value

返回值:OK

3. 哈希表(Hash)

Hash 类型的值中包含多组 field value。

3.1. hset

给 key 中 field 设置值。

语法:hset key field value

返回值:成功1,失败0

3.2. hget

获取 key 中某个 field 的值

语法:hget key field

返回值:返回 field 的内容

3.3. hmset

给 key 中多个 filed 设置值

语法:hmset key field value field value

返回值:成功OK

3.4. hmget

一次获取 key 中多个 field 的值

语法:hmget key field field

返回值:value 列表

3.5. hvals

获取 key 中所有 field 的值

语法:hvals key

返回值:value 列表

3.6. hgetall

获取所有 field 和 value

语法:hgetall key

返回值:field 和 value 交替显示列表

3.7. hdel

删除 key 中任意个 field

语法:hdel key field field

返回值:成功删除 field 的数量

4. 列表(List)

key value1 value2 value3 value4

4.1. rpush

向列表末尾中插入一个或多个值

语法;rpush key value value

返回值:列表长度

4.2. lrange

返回列表中指定区间内的值。可以使用-1代表列表末尾

语法:lrange list 0 -1

返回值:查询到的值

4.3. lpush

将一个或多个值插入到列表前面

语法:lpush key value value

返回值:列表长度

4.4. llen

获取列表长度

语法:llen key

返回值:列表长度

4.5. lrem

删除列表中元素。count 为正数表示从左往右删除的数量。负数从右往左删除的数量。

语法:lrem key count value

返回值:删除数量。

5. 集合(Set)

set 和 java 中集合一样。不允许重复值,如果插入重复值后新增返回结果为0。

5.1. sadd

向集合中添加内容。不允许重复。

语法:sadd key value value value

返回值:集合长度

5.2. scard

返回集合元素数量

语法:scard key

返回值:集合长度

5.3. smembers

查看集合中元素内容

语法:smembers key

返回值:集合中元素

6. 有序集合(Sorted Set)

有序集合中每个 value 都有一个分数(score),根据分数进行排序。

6.1. zadd

向有序集合中添加数据

语法:zadd key score value score value

返回值:长度

6.2. zrange

返回区间内容,withscores 表示带有分数

语法:zrange key 区间 [withscores]

返回值:值列表

说明:上面的区间指的是元素的索引区间

四、Jedis(了解)

1. 概述

Redis 给 Java 语言提供了客户端 API,称之为 Jedis。

Jedis API 和 Redis 命令几乎是一样的。

例如:Redis 对 String 值新增时 set 命令,Jedis 中也是 set 方法。所以本课程中没有重点把所有方法进行演示,重要演示 Jedis 如何使用。

Jedis API 特别简单,基本上都是创建对象调用方法即可。由于 Jedis 不具备把对象转换为字符串的能力,所以每次都需要借助 Json 转换工具进行转换,这个功能在 Spring Data Redis 中已经具备,推荐使用Spring Data Redis。

2. 添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xszx</groupId>
    <artifactId>springboot_redis_demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

3. 不带连接池

import org.junit.Test;
import redis.clients.jedis.Jedis;

public class Demo {

    @Test
    public void test1(){
        Jedis jedis = new Jedis("127.0.0.1",6379);
        jedis.set("name","哈哈");
        String value = jedis.get("name");
        System.out.println(value);
    }
}

4. 带有连接池

@Test
public void test2(){
    JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
    jedisPoolConfig.setMaxTotal(20);
    jedisPoolConfig.setMaxIdle(5);
    jedisPoolConfig.setMinIdle(3);
    JedisPool jedisPool = new JedisPool(jedisPoolConfig,"127.0.0.1",6379);
    Jedis jedis = jedisPool.getResource();
    jedis.set("name","呵呵");
    String value = jedis.get("name");
    System.out.println(value);
}

五、SpringBoot 整合 SpringDataRedis 操作 Redis

1. SpringData 简介

SpringData 是 Spring 公司的顶级项目,里面包含了N多个二级子项目,这些子项目都是相对独立的项目。每个子项目是对不同 API 的封装。

所有 SpringBoot 整合 SpringData xxxx 的启动器都叫做 spring-boot-starter-data-xxxx

SpringData 好处很方便操作对象类型(基于 POJO 模型)。

只要是 SpringData 的子项目被 SpringBoot 整合后都会有一个 XXXXTemplate 示实例。

把 Redis 不同值的类型放到一个 opsForXXX 方法中。

  • opsForValue:String 值(最常用),如果存储 Java 对象或 Java 中集合时就需要使用序列化器,进行序列化成 JSON 字符串。
  • opsForList:列表 List
  • opsForHash:哈希表 Hash
  • opsForZSet:有序集合 Sorted Set
  • opsForSet:集合

2. SpringDataRedis 序列化器介绍

经常需要向 Redis 中保存 Java 中 Object 或 List 等类型,这个时候就需要通过序列化器把 Java 中对象转换为字符串进行存储。

2.1. StringRedisSerializer

只能对 String 类型序列化操作。

2.2. GenericJackson2JsonRedisSerializer

该序列化器可以将对象自动转换为 Json 的形式存储,效率高且对调用者友好。底层依然使用 Jackson 工具包。转换后的结果中多了 _class 列,列里面存储类(新增时类型)的全限定路径,从 Redis 取出时根据_class 类型进行转换,解决了泛型问题。

3. 案例

3.1. 添加依赖

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.2.6.RELEASE</version>
</parent>

<dependencies>
  <!-- 为了要在项目中jackson工具包 -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>
</dependencies>

3.2. 编写配置文件

使用默认的 Redis 配置即可。

3.3. 编写配置类

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return redisTemplate;
    }
}

3.4. 编写启动类

package com.xszx;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MainApplication {

    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }
}

3.5. 代码1

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Test
public void testString() {
    People peo = new People(1, "张三");
    redisTemplate.opsForValue().set("peo1", peo);
}
@Test
public void testGetString() {
    People peo = (People) redisTemplate.opsForValue().get("peo1");
    System.out.println(peo);
}

3.6. 代码2

@Test
public void testList() {
    List<People> list = new ArrayList<>();
    list.add(new People(1, "张三"));
    list.add(new People(2, "李四"));
    redisTemplate.opsForValue().set("list2", list);
}
@Test
public void testGetList(){
    List<People> list2 = (List<People>) redisTemplate.opsForValue().get("list2");
    System.out.println(list2);
}

3.7. 代码3

@Test
void hasKey(){
    Boolean result = redisTemplate.hasKey("name");
    System.out.println(result);
}
@Test
void del(){
    Boolean result = redisTemplate.delete("name");
    System.out.println(result);
}

六、Redis 持久化策略

1. 概述

Redis 不仅仅是一个内存型数据库,还具备持久化能力。

Redis 每次启动时都会从硬盘存储文件中把数据读取到内存中。运行过程中操作的数据都是内存中的数据。

一共包含两种持久化策略:RDB 和 AOF

2. RDB(Redis DataBase)

rdb 模式是默认模式,可以在指定的时间间隔内生成数据快照(snapshot),默认保存到 dump.rdb 文件中。当 redis 重启后会自动加载 dump.rdb 文件中内容到内存中。

用户可以使用 SAVE(同步)或 BGSAVE(异步)手动保存数据。

可以设置服务器配置的 save 选项,让服务器每隔一段时间自动执行一次 BGSAVE 命令,可以通过 save 选项设置多个保存条件,但只要其中任意一个条件被满足,服务器就会执行 BGSAVE 命令。
例如:
save 900 1
save 300 10
save 60 10000
那么只要满足以下三个条件中的任意一个,BGSAVE 命令就会被执行。计时单位是必须要执行的时间,save 900 1 ,每900秒检测一次。在并发量越高的项目中 Redis 的时间参数设置的值要越小。
服务器在900秒之内,对数据库进行了至少1次修改
服务器在300秒之内,对数据库进行了至少10次修改
服务器在60秒之内,对数据库进行了至少10000次修改。

优点:

rdb 文件是一个紧凑文件,直接使用 rdb 文件就可以还原数据。

数据保存会由一个子进程进行保存,不影响父进程做其他事情。

恢复数据的效率要高于 aof

总结:性能要高于 AOF

缺点:

每次保存点之间导致 redis 不可意料的关闭,可能会丢失数据。

由于每次保存数据都需要 fork() 子进程,在数据量比较大时可能会比较耗费性能。

3. AOF(AppendOnly File)

AOF 默认是关闭的,需要在配置文件 redis.conf 中开启 AOF。Redis 支持 AOF 和 RDB 同时生效,如果同时存在,AOF 优先级高于 RDB(Redis 重新启动时会使用 AOF 进行数据恢复)

AOF 原理:监听执行的命令,如果发现执行了修改数据的操作,同时直接同步到数据库文件中,同时会把命令记录到日志中。即使突然出现问题,由于日志文件中已经记录命令,下一次启动时也可以按照日志进行恢复数据,由于内存数据和硬盘数据实时同步,即使出现意外情况也需要担心。

优点:

相对 RDB 数据更加安全。

缺点:

相同数据集 AOF 要大于 RDB。

相对 RDB 可能会慢一些。

开启办法:

修改 redis.conf 中。

appendonly yes 开启 aof

appendfilename 设置 aof 数据文件,名称随意。

# 默认no
appendonly yes
# aof文件名
appendfilename "appendonly.aof"
  • 28
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值