Redis官方文档

在开始之前

NoSQL-非关系型数据库

特点

  1. 结构-非结构化
  • 键值(Redis)
  • 文档(MongoDB)
  • 图(Neo4j)
  • 列(HBase)
  1. 关联-无关联的
  2. 查询-非SQL
1
SELECT id,name age FROM tb_user WHERE id =1

非SQL-不统一的语法

1
get user: 1
1
db.users.find({_id: 1})
  1. 事务-BASE-相对于SQL的ACID(原子性,一致性,隔离性,持久性)
  2. 存储-内存
    • SQL为磁盘存储
  3. 扩展性- 水平
    • SQL为垂直
  4. 使用场景
    • 数据结构不固定
    • 对一致性、安全性要求不高
    • 对性能要求高的时候

Redis介绍和Redis的安装

Redis的特征

  • 键值型
  • 单线程(核心),每个命令具备原子性
  • 低延迟,速度快(基于内存、IO多路复用、良好的编码)
  • 支持数据持久化
  • 支持主从集群、分片集群
  • 支持多语言客户端

Redis的安装

Ubuntu 下安装

参考文章戳我跳转

Redis的启动

我觉得最舒服的还是service

1
service redis-server start

关闭同理改成stop就好了

配置文件

1
2
3
4
5
6
7
8
# 设置日志文件,默认为空不记录
logfile "redis.log"
# 守护进程,修改成yes就可以在后台运行
daemonize yes
#密码,设置后访问Redis必须要输入密码
requirepass 123321
#监听的地址,默认127.0.0.1,会导致只能本地访问,修改为0.0.0.0则可以在任意IP访问,生产环境不要设置为0.0.0.0
bind 0.0.0.0

Redis客户端

Redis命令行客户端

也就是redis-cli
使用方法:

1
redis- cli [options] [commonds]

其中常见的options有:

  • -h 127.0.0.1:指定要连接的redis节点的IP地址,默认是127.0.0.1
  • -p 6379:指定要连接的redis节点的端口,默认是6379
  • -a 123321:指定redis的访问密码
    其中的commonds就是Redis的操作命令,例如
  • ping:心跳测试,服务端正常会返回pong
    不指定commond时,就会进交互控制台

图形化界面客户端

tingRDM

Redis常见命令

官方文档中也有记录,学习时可以多加参考

Redis数据结构介绍

作为一个key-value的数据库,key一般是String类型,不过value就类型多种多样:

  • String
  • Hash
  • List
  • Set
  • SortedSet

    以上为基本类型

  • GEO
  • BitMap
  • HyperLog

通用命令

官方文档,或者help @genertic都可以查询

这里介绍常见的命令:

  • KEYS:查看符合模板的所有key,不建议在生产环境设备上使用,会阻塞所有的请求
  • DEL:删除key 如果没有的key删除不会报错,但是会提示删除0个
  • EXISTS:判断key是否存在
  • EXPIRE:给一个key设置有效期,有效期到期时该key会被自动删除
  • TTL:查看一个key的剩余有效期
    • -1:代表永久有效
    • -2:代表已经删除

String

如果value是字符串,根据字符串的不同格式有可以分为三种:

  • string:普通字符串
  • int:整数类型,可以做自增、自减操作
  • float:浮点类型,可以做自增、自减操作

    以上的区别是编码方式不同。
    字符串类型的最大空间不能超过512mb

String类型的常见命令

String的常见命令有:

  • SET:添加或者修改已经存在的一个String类型的键值对
  • GET:根据key获取String类型的value
  • MSET:批量添加多个String类型的键值对
  • MGET:根据多个key获取多个String类型的value
  • INCR:让一个整型的key自增1
  • INCRBY:让一个整型的key自增并指定步长,例如:incrby num 2让num值自增2
  • INCRBYFLOAT:让一个浮点类型的数字自增并指定步长
  • SETNX:添加一个String类型的键值对,前提是这个key不存在,否则不执行
  • SETEX:添加一个String类型的键值对,并且指定有效期

Key的层级格式

Redis的key允许有多个单词形成层级结构,多个单词之间用:隔开,格式如下:
项目名:业务名:类型:id
这个格式并非固定,也可以根据自己的需求来删除或者添加词条
在图形客户端就会形成层级结构

Hash类型

类似HashMap,但也有差异

  • 需求:String结构存储对象序列化JSON后,需要修改对象某个字段并不方便
  • Hash结构可将对象中的每个字段独立存储,可以针对单个字段作CRUD
  • Hash的常见命令:
    • HSET key fielld value:添加或者修改hash类型key的field的值
    • HGET key field:获取一个hash类型key的field的值
    • HMSET:批量添加多个hash类型key的field的值
    • HMGET:批量获取多个hash类型key的field的值
    • HGETALL:获取一个hash类型的key中的所有的field和value
    • HKEYS:获取一个hash类型的key中的所有field
    • HVALS:获取一个hash类型的key中的所有value
    • HINCRBY:让一个hash类型key的字段值自增并指定步长
    • HSETNX:添加一个hash类型的key的field值,前提是这个field不存在,否则不执行

List类型

  • 支持正向检索以及反向检索
  • 特征与LinkList类似:
    • 有序
    • 元素可以重复
    • 插入和删除快
    • 查询速度一般
  • 常用来存储一个有序数据,例如:朋友圈点赞列表,评论列表等
  • List类型常见命令:
    • LPUSH key element…: 向了列表左侧插入一个或多个元素
    • LPOP key: 移除并返回列表左侧的第一个元素,没有则返回nil
    • RPUSH key element…: 向列表右侧插入一个或多个元素
    • RPOP key: 移除并返回列表右侧的第一个元素
    • LRANGE key star end: 返回一段角标范围内的所有元素这个角标从0开始,不要参考图形化的id
    • BLPOP和BRPOP、与LPOP和RPOP类似,只不过在没有元素时等待指定时间,而不是直接返回nil 阻塞队列使用

Set类型

可以看作一个value为null的HashMap

  • 具备与HashSet类似的特征:
    • 无序
    • 元素不可重复
    • 查找快
    • 支持交集、并集、差集等功能(不同于HashSet
  • Set的常见命令
    • SADD key member…: 向set中添加一个或多个元素
    • SREM key member…: 移除set中的指定元素
    • SCARD key: 返回set中元素的个数
    • SISMEMBER key member: 判断一个元素是否存在于set中
    • SMEMBERS: 获取set中的所有元素
    • SINTER key1 key2…: 求key1与key2的交集
    • SDIFF key1 key2…: 求key1与key2的差集
    • SUNION key1 key2…: 求key1与key2的并集

SortedSet类型

TreeSet功能类似,但底层数据结构完全不同。SortedSet中每一个元素都带一个score属性,可以基于score属性对元素排序

底层的实现是一个跳表(SkipList)加hash表

  • SortedSet具备下列特性:
    • 可排序
    • 元素不重复
    • 查询速度快
  • 因为可排序特性,通常被用来实现排行榜这样的功能
  • SortedSet类型常见命令:
    • ZADD key score member: 添加一个或者多个元素到sorted set,如果已经存在则更新其score值
    • ZREM key member: 删除sorted set中的一个指定元素
    • ZSCORE key member: 获取sorted set中的指定元素的score值
    • ZRANK key member: 获取sorted set中的指定元素的排名
    • ZCARD key: 获取sorted set中的元素个数
    • ZCOUNT key min max:统计score值在给定范围内的所有元素的个数
    • ZINCRBY key increment member: 让sorted set中指定的元素自增,步长为指定的increment
    • ZRANGE key min max:按照score排序后,获取指定排名范围内的元素
    • ZRANGEBYSCORE key min max: 按照score排序后,获取指定score范围内的元素
    • ZDIFF、ZINTER、ZUNION: 求差集、交集、并集

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

Redis的Java客户端

Jedis

使用简单,但是线程不安全,多线程需要连接池来使用

Jedis快速入门

官方地址

  1. 引入依赖
1
2
3
4
5
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>7.1.0</version>
</dependency>
  1. 建立连接
1
2
3
4
5
6
7
8
9
10
private Jedis jedis;
@BeforeEach
void setUp(){
//建立连接
jedis = new Jedis("192.168.150.101", 6379);
// 设置密码
jedis.auth("123321");
//选择库
jedis.select(0);
}
  1. 测试string
1
2
3
4
5
6
7
8
9
@Test
void testString(){
//插入数据,方法名称就是redis命令名称
String result = jedis.set("name", "张三");
System.out.println("result = " + result);
//获取数据
String name = jedis.get("name");
System.out.println("name = "+ name);
}
  1. 释放资源
1
2
3
4
5
6
7
@AfterEach
void tearDown(){
//释放资源
if(jedis !=null){
jedis.close();
}
}

Jedis连接池

Jedis本身线程是不安全的,并且频繁的创建和销毁连接会有性能损耗,因此推荐大家使用Jedis连接池代替Jedis直连方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class JedisConnectionFactory{
private static final JedisPool jedisPool;

static{
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//最大连接
jedisPoolConfig.setMaxTotal(8);
//最大空闲连接
jedisPoolConfig.setMaxIdle(8);
//最小空闲连接
jedisPoolConfig.setMinIdle(0);
//设置最长等待时间,ms
jedisPoolConfig.setMaxWaitMillis(200);
jedisPool = new JedisPool(jedisPoolConfig, "192.168.150.101", 6379, 1000. "123321")
}
//获取Jedis对象
public static Jedis getJedis(){
reurn jedisPool.getResource();
}
}

其他都与之前一致,释放的时候,如果库又被调用非空,实际上是归还给了连接池而非销毁

Spring Data Redis

可以参考官方文档

概述

SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis

  • 提供了对不同Redis客户端的整合,eg:兼容了Jedislettuce
  • ✨提供了RedisTemplate统一API来操作Redis(核心)
  • 支持Redis的发布订阅模型
  • 支持Redis哨兵和Redis集群
  • 支持基于Lettuce的响应式编程
  • 支持基于JDK、JSON、字符串、Spring对象的数据序列化和反序列化
  • 支持基于Redis的JDKCollection实现

SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的草做。并且将不同的数据类型的操作API封装到了不同的类型中:
SpringDataRedis命令


快速入门

  1. 引入依赖
1
2
3
4
5
6
7
8
9
10
11
12
<dependencies>
<!-- Redis依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 连接池依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies>

在创建的时候可以直接选择nosql中的redis依赖

  1. 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
spring:
data:
redis:
host: 192.168.150.101
port: 6379
password: 123321
lettuce:
pool:
max-active: 8 # 最大连接
max-idle: 8 # 最大空闲连接
min-idle: 0 # 最小空闲连接
max-wait: 100 # 连接等待时间

就是application.properties文件,也可以改成yaml写入这些

  1. 注入RedisTemplate
1
2
@Autowired
private RedisTemplate redisTemplate;
  1. 编写测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootTest
public class RedisTest{
@Autowired
private RedisTemplate redisTemplate;

@Test
void testString(){
//插入一条string类型数据
redisTemplate.opsForValue().set("name", "李四");
//读取一条string类型数据
Object name = redisTemplate.opsForValue().get("name");
System.out.println("name = "+ name);
}
}

RedisTemplate的RedisSerializer(序列化)

  • RedisTemplate可以接受任意的Object作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认采用的式JDK序列化,具有以下缺点
    • 可读性差
    • 内存占用较大
  • 因此,我们可以自定义RedisTemplate的序列化方式

    参考文档

    1. 方案一:
      • 自定义RedisTemplate
      • 修改RedisTemplate的序列化器
      • 写入数据时自动序列化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @Configuration
    public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
    // 创建redistemplate对象
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    // 设置连接工厂
    template.setConnectionFactory(connectionFactory);
    // 创建JSON序列化工具
    GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
    // 设置Key的序列化
    template.setKeySerializer(RedisSerializer.string());
    template.setHashKeySerializer(RedisSerializer.string());
    // 设置Value的序列化
    template.setValueSerializer(jsonRedisSerializer);
    template.setHashValueSerializer(jsonRedisSerializer);
    // 返回
    return template;
    }
    }

    记得引入Jackson依赖,报错的话

    • 缺点:由于是自动反序列化,所以在redis中存储了类型,这样会带来额外的内存开销
    1. 方案二:
      • 使用StringRedisTemplate
      • 手动序列化
      • 手动反序列化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    // JSON工具
    private static final ObjectMapper mapper = new ObjectMapper();
    @Test
    void contextLoads4() throws JsonProcessingException {
    User user = new User("可乐", 18);
    // 手动序列化
    String json = mapper.writeValueAsString(user);
    //写入数据
    stringRedisTemplate.opsForValue().set("user:2", json);
    String s = stringRedisTemplate.opsForValue().get("user:2");
    // 反序列化
    User u = mapper.readValue(s, User.class);
    System.out.println(u);
    }

注意StringRedisTemplate(对应方案二):Spring默认提供的类,它的key和value序列化方式默认就是String方式。也就是不需要自己再去定义RedisTemplate了

Redis的Lua脚本

Redis提供了Lua脚本功能,在一个脚本中编写多条Redis命令,确保多条命令执行时的原子性。Lua是一种编程语言,参考网站Lua 教程

Lua脚本编写

  • Redis提供的调用函数
1
redis.call('命令名称', 'key', '其他参数', ...)
  • 例如:执行 set name jack
1
redis.call('set', 'name', 'jack')
  • 例如,要先set name Rose,再执行get name:
1
2
3
redis.call('set', 'name', 'jack')
local name = redis.call('get', 'name')
return name

Redis的调用

1
EVAL script numkeys key [key ...]  arg [arg ...]
  • 例如:执行redis.call('set', 'name', 'jack'):
1
EVAL "return redis.call('set', 'name', 'jack')" 0

这个0就是脚本需要的key类型的参数个数

  • 如果要传参,key类型放入KEYS数组,其他参数放入ARGV数组
    lua传参

最后

SpringRedisValue中,命令可能会有些许不同,报错的时候记得参照官方文档查看是否有错🤗