Redis:Key-Value的NoSQL数据库

发布时间 2023-11-20 20:12:27作者: yanggdgg

Redis:Key-Value的NoSQL数据库(基础)

主要内容:

  • 数据库分类
  • Redis入门
  • 客户端连接Redis
  • Redis数据类型及操作
  • Springboot整合Redis
  • Spring Cache

学习目标:

知识点 要求
数据库分类 熟悉
Redis使用场景 熟悉
Redis下载与安装 了解
Redis启动关闭 掌握
客户端连接Redis 熟悉
Redis数据类型及常见操作 掌握
Springboot整合Redis 掌握
Spring Cache 熟悉

一、数据库分类

  • 数据库共有2种类型:关系型数据库非关系型数据库

1. 关系数据库

1.1 介绍

关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织。

1.2 分类

关系型数据库的使用最为普遍流行,如Oracle、MySQL、微软的SQL Server、PostgreSQL、IBM的DB2、SQLite,大数据库的数据库Hive等。

1.3 特点

优点:

  1. 易于维护:都是使用表结构,格式一致。
  2. 使用方便:SQL语言通用,可用于复杂查询。
  3. 复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。

缺点:

  1. 读写性能比较差,尤其是海量数据的高效率读写。
  2. 固定的表结构,灵活度稍欠。
  3. 高并发读写需求,传统关系型数据库来说,硬盘I/O是一个很大的瓶颈。

2. 非关系型数据库(NoSQL)

2.1 介绍

非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合,可以是文档或者键值对等。

2.2 分类

redis(键值数据库)、MongoDB(文档型数据库)、HBase(列式数据库)、Cassandra(宽列数据库)、Neo4J(图形数据库)等。

2.3 特点

优点:

  1. 格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。
  2. 速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘。
  3. 高扩展性。
  4. 成本低:nosql数据库部署简单,基本都是开源软件。

缺点:

  1. 不提供sql支持,学习和使用成本较高。
  2. 无事务处理。
  3. 数据结构相对复杂,复杂查询方面稍欠。

3. 数据库排名

DB-Engines Ranking 根据数据库管理系统的受欢迎程度对它们进行排名。该排行榜是按搜索、谷歌趋势、 Stack Overflow 网站、LinkedIn、Twitter 等社交网络中的关注度, 综合比较进行的排名。该排名涵盖全球 388个数据库系统,排名每月更新一次。

排名网站

二、Redis入门

1. Nosql介绍

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

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

2. Redis介绍

image

Redis是一款c语言开发的、采用key-value数据存储格式的内存级NoSQL数据库,重点关注数据存储格式,是key-value格式,也就是键值对的存储形式。与MySQL数据库不同,MySQL数据库有表、有字段、有记录,Redis没有这些东西,就是一个名称对应一个值,并且数据以存储在内存中使用为主。 平时操作的数据都在内存中,效率特别高,读的效率110000次/s,写81000次/s。

Redis有它的数据持久化方案,分别是RDB和AOF,但是Redis自身并不是为了数据持久化而生的,主要是在内存中保存数据,加速数据访问的,所以说是一款内存级数据库。

3. Redis使用场景

  • 数据库
  • 缓存
  • 任务队列
  • 消息队列
  • 分布式锁

4. 常见脚本

切换到redis-6.2.6/src目录

  • redis-server:启动服务端工具

  • redis-cli:启动客户端工具

  • redis-sentinel:集群管理工具

  • redis-check-rdb RDB:文件检查工具

  • redis-check-aof AOF:文件检查工具

  • redis-benchmark redis:性能测试工具

5. 启动服务端

5.1 前台启动

  • 默认启动方式为前台启动。
  • 安装redis后,已经自动配置环境变量,任意位置都可以启动。
redis-server

这种启动属于前台启动,会阻塞整个会话窗口,窗口关闭或者按下CTRL + C则Redis停止。不推荐使用。

5.2 后台启动(守护进程启动)

5.2.1 修改redis配置文件
  1. 切换到redis-6.2.6目录。

  2. 修改redis配置文件redis.conf。

  3. vim redis.conf

    #显示行号
    :set number
    
    # 守护进程启动   257行
    daemonize yes 
    
  4. 修改后一定要保存。

5.2.2 启动redis
  • 启动redis时必须指定使用的配置文件
#必须指定启动时使用的配置文件
redis-server ./redis.conf 
5.2.3 查看redis进程
#查看进程号
ps -ef|grep redis	

#杀死进程
kill -9 进程号

6. 其它常见配置

#允许访问的地址,默认是127.0.0.1,会导致只能在本地访问                   75行
#bind 127.0.0.1 -::1
#修改为0.0.0.0则可以在任意IP访问,生产环境尽量不要设置为0.0.0.0
bind 0.0.0.0

#默认监听的端口                                                     98行
port 6379

# 默认没有指定。日志文件,指定日志文件名                                302行
logfile "redis.log"

# 数据库数量,默认有16个库,编号0~15。可以手动指定使用多少个库(1~16范围)  327行
databases 16

# 默认工作目录,默认为当前目录。日志、持久化等文件会保存在这个目录          454行
dir ./

#默认没有密码。设置后访问Redis必须输入密码                              901行
requirepass 123456

# 默认没有指定。指定redis能够使用最大内存                              992行
maxmemory 512mb

三、客户端连接redis

  • 确保redis为启动状态。

image

1. 自带客户端连接redis

redis-cli [options] [commonds]

常用options(参数):

  • -h 127.0.0.1:指定要连接的redis节点的IP地址,默认是127.0.0.1
  • -p 6379:指定要连接的redis节点的端口,默认是6379
  • -a 123456:指定redis的访问密码

image

commonds是Redis的操作命令,例如:

  • ping:与redis服务端做心跳测试,服务端正常会返回pong

  • 不指定commond时,会进入redis-cli的交互控制台。

image

四、Redis数据类型及操作

1. 数据类型

image

2. 通用命令

命令 介绍
KEYS parrern 查找所有符合给定模式(pattern)的可以。例如:keys * 获取所有的key。
EXISTS key 检查key是否存在
TYPE key 返回key所存储值的类型
EXPIRE key key设置有效期,有效期到期时该key会被自动删除
TTL key 返回key的剩余生存时间,秒为单位。TTL:time to live
DEL key 删除存在的key

3. 字符串类型string

  • String的常见命令。
命令 描述
SET key value 添加键值对或修改指定key的值
GET key 获取key的值
MSET key1 value1 key2 value2 ... 批量添加多个键值对
MGET key1 key2 ... 根据多个key获取对应的值
INCR key 指定key的值+1
INCRBY key n 指定key的值+n
SETNX key value key不存在时添加
SETEX key seconds value 添加键值对同时为key指定存活时间

image

4. hash类型

  • Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。

  • string的数据存储是一个名称对应一个值,如果要维护的数据过多,可以使用hash哈希存储模型它一个名称下可以存储多个数据每个数据也可以有自己的二级存储名称

命令 描述
HSET key field value 添加或修改
HGET key field 获取指定key中filed对应的值
HDEL key fileld 删除指定key中f指定的filed
HKEYS key 获取指定key中所有filed
HVALS key 获取指定key中所有value
HGETALL 获取指定key中所有filed和value
HSETNX 指定key中filed不存在时添加

image

5. list类型

  • Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
命令 描述
LPUSH key value1 value2 ... 左侧插入一个或多个元素
LPOP key 移除并返回列表左侧的第一个元素,没有则返回nil
RPUSH key value1 value2 ... 右侧插入一个或多个元素
RPOP key 移除并返回列表右侧的第一个元素,没有则返回nil
LLEN key 获取列表长度
LRANGE key start stop 获取指定范围内的元素(0,-1)可以获取所有元素
BLPOP key timeout 移出并获取列表左侧的第一个元素, 如果列表没有元素
会阻塞列表直到等待超时或发现可弹出元素为止
BRPOP key timeout 移出并获取列表右侧的第一个元素, 如果列表没有元素
会阻塞列表直到等待超时或发现可弹出元素为止

image

6. set类型

  • Redis 的 set 是 string 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
命令 描述
SADD key member1 member2 ... 向集合添加一个或多个成员
SREM key member1 member2 ... 移除集合中一个或多个成员
SMEMBERS key 返回集合中的所有成员
SCARD key 获取集合的成员数
SISMEMBER key member 判断 member 元素是否是集合 key 的成员
SDIFF key1 key2 ... 返回第一个集合与其他集合之间的差异
SINTER key1 key2 ... 返回给定所有集合的交集
SUNION key1 key2 ... 返回所有给定集合的并集

image

7. sorted set类型

7.1 命令

Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。

有序集合的成员是唯一的,但分数(score)却可以重复。

命令 描述
ZADD key score1 member1 score 2member2 ... 向有序集合添加一个或多个成员,或者更新已存在成员的分数
ZREM key member1 member2 ... 移除有序集合中的一个或多个成员
ZCARD key 获取有序集合的成员数
ZCOUNT key min max 计算在有序集合中指定区间分数的成员数
ZRANGE key start stop 通过索引区间返回有序集合指定区间内的成员
ZSCORE key member 返回有序集中,成员的分数值
ZRANGEBYSCORE key min max 通过分数返回有序集合指定区间内的成员
ZRANK key member 返回有序集合中指定成员的索引
ZREVRANK key memeber 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序

注意:所有的排名默认都是升序,如果要降序则在命令的Z后面添加REV即可,例如:

  • 升序获取sorted set 中的指定元素的排名:ZRANK key member。
  • 降序获取sorted set 中的指定元素的排名:ZREVRANK key memeber。

7.2 底层存储结构

  • zset:ziplist(压缩列表)或skiplist(跳跃表)。

  • 元素数量小于128个,且每个元素长度小于64字节时使用压缩列表,其他情况使用跳跃表。

  1. 压缩列表:本质是一个数组,数组首部存长度、偏移量、元素个数,尾部存结束标识。每个元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个节点保存元素的分值。
  1. 跳跃表:单向链表按序保存元素及分值,使用哈希表dict来保存元素和分值的映射关系。链表增加了多级索引,先从最上层索引跳跃查,再渐渐往下层到链表地查询,实现了快速查找元素,时间复杂度O(logn),这种查询算法类似于链表版二分查找,是基于有序的。

如果我们想要提高其查找效率,可以考虑在链表上建索引的方式。每两个结点提取一个结点到上一级,我们把抽出来的那一级叫作索引。

从这个例子里,我们看出,加上一层索引之后,查找一个结点需要遍历的结点个数减少了,也就是说查找效率提高了,同理再加一级索引。

从图中我们可以看出,查找效率又有提升。在例子中我们的数据很少,当有大量的数据时,我们可以增加多级索引,其查找效率可以得到明显提升。

zset底层不使用红黑树的原因:

  • 范围查找:因为红黑树范围查找效率低,而跳跃表范围查找效率高,因为是链表结构。zset可以用zrange命令查指定范围内元素。
  • 实现难度:跳跃表实现比红黑树简单。

五、springboot整合redis

  • Spring官网

  • Spring Data Redis 是更大的 Spring Data 产品组合的一部分,它提供了从 Spring 应用程序轻松配置和访问 Redis 的功能。它提供了用于高级抽象,使用户摆脱了基础设施的担忧。

  • 连接包作为跨多个 Redis 驱动程序(LettuceJedis)的低级抽象。

1. lettucs客户端技术操作Redis(默认)

  • lettuce 美[ˈletəs] 英[ˈletɪs]

1.1 RedisTemplate对象

Spring Data Redis中提供了一个高度封装的类:RedisTenplate,将同一类型操作封装为operation接口:

  1. ValueOperations:操作简单字符串key-value。
  2. SetOperations:操作set类型。
  3. ZSetOperations:操作sortedSet类型。
  4. HashOperations:操作hash类型。
  5. ListOperations:操作list类型。

1.2 整合思想

在进行整合之前先梳理一下整合的思想,springboot整合任何技术其实就是在springboot中使用对应技术的API。如果两个技术没有交集,就不存在整合的概念了。所谓整合其实就是使用springboot技术去管理其他技术,几个问题是躲不掉的。

第一,需要先导入对应技术的启动器依赖或依赖。

第二,任何技术被整合后,通常都可以进行相关配置,通过springboot配置文件进行配置。

1.3 springboot使用lettucs客户端整合redis

1.3.1 导入依赖
<!-- redis启动器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- lettuce连接池需要的依赖 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
  • 创建项目时也可以勾选自动导入redis启动器。
1.3.2 配置redis
  • 基本配置
spring:
  redis:
  	# redis的ip
    host: 192.168.93.128
    # redis的端口号
    port: 6379
    # redis的密码
    password: 123456
  • 更多配置
spring:
  redis:
    host: 192.168.93.128    #redis的ip
    port: 6379              #redis的端口号
    password: 123456        #redis的密码
    database: 0             #操作redis几号库
    timeout: 5000           #读取超时时间
    connect-timeout: 10000  #连接超时时间
    lettuce:                #lettuce连接池配置
      pool:
        enabled: true       #开启连接池
        max-active: 10      #最大连接数
        max-idle: 4         #最大空闲连接
        min-idle: 0         #最小空闲连接
        max-wait: -1        #最大连接阻塞等待时间,过该时间仍无法获取连接则抛出异常 -1没有限制
        time-between-eviction-runs: -1  #关闭空闲时间超过指定时间的连接
1.3.3 使用RedisTemplate
@SpringBootTest
class RedisApplicationTests {
    @Autowired
    RedisTemplate redisTemplate;
    @Test
    void testString() {
        ValueOperations valueOperations = redisTemplate.opsForValue();
        valueOperations.set("id", "1");
        Object name = valueOperations.get("id");

        System.out.println(name);
        valueOperations.getAndExpire("id", Duration.ofSeconds(10));

        DataType dataType = redisTemplate.type("id");
        System.out.println(dataType);

        Long l = redisTemplate.getExpire("id");
        System.out.println(l);
    }

    @Test
    void testList(){
        ListOperations<String, String> listOperations = redisTemplate.opsForList();
        listOperations.leftPush("names", "zs");
        listOperations.rightPush("names", "ls");

        List<String> list = listOperations.range("names", 0, -1);
        System.out.println(list);
    }

    @Test
    void testHash(){
        HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
        hashOperations.put("user", "id", "1");
        hashOperations.put("user", "name", "ls");

        Map<Object, Object> user = hashOperations.entries("user");
        System.out.println(user);
    }
}

RedisTemplate序列化器问题

  • RedisTemplate是以对象为操作的基本单元,存到数据库的实际内容是序列化后的。
  • 修改key的序列化器,由jdk序列化器修改为字符串序列化器:
@SpringBootTest
class RedisApplicationTests {
    @Autowired
    RedisTemplate redisTemplate;

    @Test
    void testString() {
        /* 设置key-value 序列化器 */
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        
        ValueOperations valueOperations = redisTemplate.opsForValue();
        valueOperations.set("id", "1");
    }

    @Test
    void testList(){
        /* 设置key-value 序列化器 */
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());

        ListOperations<String, String> listOperations = redisTemplate.opsForList();
        listOperations.leftPush("names", "zs");
        listOperations.rightPush("names", "ls");
    }

    @Test
    void testHash(){
        /* 设置key filed value 序列化器 */
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());

        HashOperations<String, Object, Object> hashOperations = redisTemplate.opsForHash();
        hashOperations.put("user", "id", "1");
        hashOperations.put("user", "name", "ls");
    }

1.3.4 使用StringRedisTemplate
  • StringRedisTemplate是以字符串为操作的基本单元。命令行客户端redis-cli.exe默认使用StringRedisTemplate。

  • 为了方便开发者使用基于字符串为数据的操作,springboot整合redis时提供了专用的API接口StringRedisTemplate。

  • RedisTemplate是以对象为操作的基本单元,存到数据库的实际内容是序列化后的。

@SpringBootTest
class RedisApplicationTests {
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Test
    void testString() {
        ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
        valueOperations.set("sid", "1");
    }

    @Test
    void testList(){
        ListOperations<String, String> listOperations = stringRedisTemplate.opsForList();
        listOperations.leftPush("snames", "zs");
        listOperations.rightPush("snames", "ls");
    }

    @Test
    void testHash(){
        HashOperations<String, Object, Object> hashOperations = stringRedisTemplate.opsForHash();
        hashOperations.put("suser", "id", "1");
        hashOperations.put("suser", "name", "ls");
    }
}

1.4 springboot使用jedis客户端整合redis

springboot整合redis技术提供了多种客户端兼容模式,默认提供的是lettucs客户端技术,也可以根据需要切换成指定客户端技术,例如jedis客户端技术。jedis是Redis传统的客户端技术。

1.4.1 导入依赖
<!-- redis启动器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- jedis依赖 -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
1.4.2 配置redis
  • 基本配置
spring:
  redis:
    host: 192.168.93.128    #redis的ip
    port: 6379              #redis的端口号
    password: 123456        #redis的密码
  • 更多配置
spring:
  redis:
    host: 192.168.93.128    #redis的ip
    port: 6379              #redis的端口号
    password: 123456        #redis的密码
    database: 0             #操作redis几号库
    timeout: 5000           #读取超时时间
    connect-timeout: 10000  #连接超时时间
    jedis:                  #jedis连接池配置
      pool:
        enabled: true       #开启连接池
        max-active: 10      #最大连接数
        max-idle: 4         #最大空闲连接
        min-idle: 0         #最小空闲连接
        max-wait: -1        #最大连接阻塞等待时间,-1表示没有时间限制
        time-between-eviction-runs: -1  #关闭空闲时间超过指定时间的连接
1.4.3 使用RedisTemplate

整合方法和lettucs一样。

1.4.4 使用StringRedisTemplate

整合方法和lettucs一样。

1.5 lettcus与jedis区别

  • lettcus基于Netty框架进行与Redis服务器连接,底层设计中采用StatefulRedisConnection。 StatefulRedisConnection自身是线程安全的,可以保障并发访问安全问题,所以一个连接可以被多线程复用。lettcus也支持多连接实例一起工作。
  • jedis连接Redis服务器是直连模式,当多线程模式下使用jedis会存在线程安全问题,解决方案可以通过配置连接池使每个连接专用,这样整体性能就大受影响。

六、Spring Cache

  • redis使用较多的使用场景为缓存。

1. 介绍

Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。

Spring Cache提供了一层抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓存技术。

CacheManager是Spring提供的各种缓存技术抽象接口。

Snipaste_2023-11-12_16-57-27

缓存:

缓存是一种介于数据永久存储介质与应用程序之间的数据临时存储介质,使用缓存可以有效的减少低速数据读取过程的次数(例如磁盘IO),提高系统性能。此外缓存不仅可以用于提高永久性存储介质的数据读取效率,还可以提供临时的数据存储空间。而springboot提供了对市面上几乎所有的缓存技术进行整合的方案。

2. 常用注解

3. SpringBoot内置缓存Simple

3.1 引入依赖

<!-- 缓存启动器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

3.2 启动类开启缓存

@SpringBootApplication
//开启缓存功能
@EnableCaching
public class SpringbootCacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootCacheApplication.class, args);
    }
}

3.3 设置操作的数据是否使用缓存

使用@Cacheable注解后,执行当前操作,如果发现对应名称在缓存中没有数据,就正常读取数据,然后放入缓存;如果对应名称在缓存中有数据,就终止当前业务方法执行,直接返回缓存中的数据。

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookMapper bookMapper;
    //Cacheable译为可缓存的,可缓冲的。@Cacheable的value属性是存储空间名,key属性是此方法返回值在存储空间内的键名。
    //key属性名必须和形参名一样才能缓存,别忘了#号。 多个参数可以使用拼接方式:"#id+'_'+#name"。
    @Cacheable(value="BookServiceGetById",key="#id")
    public Book getById(Integer id) {
        return bookMapper.selectById(id);
    }
}

注意:

  • 一定一定别忘了#号。
  • 缓存的key属性名必须方法的形参名一样才能缓存。 只要此key对应的缓存已存在,下次不管查询出什么数据,返回的结果都是直接从缓存的这个key里取。
  • 被注解@Cacheable声明的方法不能被本类中其他方法调用,原因是spring容器管理问题。

4. SpringBoot整合Redis缓存

4.1 引入依赖

<!-- 缓存启动器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<!-- redis启动器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- 使用lettucs连接池需要引入 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

4.2 配置

spring:
  redis:
    port: 6379
    host: 192.168.93.128
    password: 123456
  cache:
    type: redis
  • 操作与上边SimpleCache一致。