声明:文章内容皆取于学习黑马课程、若想了解原版内容请去B站了解黑马程序员Redis课程

1.Redis入门篇

1.什么是Redis

Redis全称为Remote Dictionary Server,远程词典服务器,基于内存的键值型NoSQL数据库

NoSQL(Not only SQL)是对不同于传统的关系数据库的数据库管理系统的统称,即广义地来说可以把所有不是关系型数据库的数据库统称为NoSQL。

特征:

1.键值型,value支持多种不同数据结构

2.单线程,每个命令具有原子性

3.低延迟,速度快(基于内存、IO多路复用、良好编码)

4.支持数据持久化(RDB AOF)什么是RDB、AOF后面会提及

5.支持主从集群、分片集群

6.支持多语言客户端

2.安装Redis(基于Linux)

1.单机安装

1.单机安装Redis

Redis基于c语言编写所以安装gcc依赖

yum install -y gcc tcl

2.上传安装包

我个人使用的是Xftp传的就不作展示了

3.解压redis

解压到当前目录

tar -zxvf redis-6.2.6.tar.gz

4.编译安装

安装并编译

make && make install

5.安装成功

我们默认安装成功的路径 /usr/local/bin

如果安装成功那么就会有很多命令例如redis server、redis-cli等等

3.指定启动方式

1.默认启动

redis-server

没有配置启动就是默认启动

2.配置文件启动

进入redis软件目录

我个人是放在/opt/module

cd /opt/module/redis-6.2.6

我们先cp一下配置文件并备份

cp redis.conf redis.conf.bak

1.修改配置

我个人改了一些配置例如

bind 0.0.0.0

# 监听地址,允许所有ip访问(这玩意云服务器和生产别这样搞除非有密码)

daemonize yes

# 后台运行

requirepass simon0414

#密码

maxmemory 512mb

#最多512mb

logfile "redis.log"

#日志目录(默认是"",也就是不记录)

2.运行

redis-server redis.conf

3.查看进程

ps -ef |grep redis

3.开机自启动

1.去新建一个系统服务文件

vim /etc/systemd/system/redis.service

2.内容如下

注意那个配置文件填你自己的解压地址

[Unit]

Description=redis-server

After=network.target

[Service]

Type=forking

ExecStart=/usr/local/bin/redis-server /opt/module/redis-6.2.6/redis.conf

PrivateTmp=true

[Install]

WantedBy=multi-user.target

3.重载系统服务

systemctl daemon-reload

4.设置自启动

systemctl enable redis

4.客户端访问

1.命令行客户端

-a 指定密码

-p 端口

-h ip地址

redis-cli -a simon0414

后续验证

auth simon0414

2.图形化客户端

按个人喜好

3.编程api

后续补充

4.常见命令

1.Redis的数据结构(基本类型)

1.String

2.Hash

3.List

4.Set

5.SortedSet

2 Redis的数据结构(特殊类型)

6.GEO

7.BitMap

8.HyperLog

以及等等其他特殊类型(消息队列)

3.通用命令(常用)

1.help

help @组名 | help 具体命令

作用:帮助文档

2.keys

作用:找到符合要求的key

keys 表达式

?代表一个字符、*代表多个字符

查找以a开头的key

keys a*

我们知道redis是一个单线程的程序,我们的keys后面是表达式自然就是模糊搜素引擎效率不高,如果查找时间过长就会长时间

阻塞我们的业务,所以生成环境最好别用

3.del

del key的名称 | del key1 key2

作用:删除一个key值、删除多个key值

即便是不存在的key也没关系(返回的是有效的删除数)

4.exists

exists key

key是否存在

5.expire、ttl

作用:给key设置有效时间,ttl查看key的有效时间

expire age 20

ttl age

给age设置20秒有效时间并查看age的有效时间

4.String类型(常用命令)

字符串类型,是Redis最基础的类型,也就是value是字符串

我们可以又能细分我们的value的类型为三种(最大512mb)

string、int、float

无论这三种什么格式,底层均采用字节数组来存储

在 Redis 中,字符串是一个字节数组。一个数字型字符串类型的数据占用的内存空间取决于其存储的文本长度。当一个字符串类型存储的是数字时,采用二进制的形式存储会降低存储和处理数字型字符串的空间和时间开销。基于这个原因,Redis对于数字型字符串类型进行了特殊处理,采用了一种称为整数编码的方式来优化存储效率。

整数编码包括三种形式:

int:采用 8 个字节的有符号整数存储。embstr:采用较小的编码存储数值,这个编码长度只能存储值在 0 ~ 4 个字节范围内的整数类型。这种方式又分为 16 位和 32 位两种,采用 6 bit 的前缀来表示。例如,如果前缀为 110,则接下来的 14 bit 就是这个整数的数值。raw:直接采用字符串方式存储。

1.set

set key value

作用:添加或者修改key的value

2.get

get key

作用:获取key的值

3.mset

mset k1 v1 k2 v2

作用:设置多个key的值

4.mget

mget k1 k2

作用:获取多个key值的一个数组

5.Incr

incr key

作用:给整型的key自增1

6.incrby

incrby key 步长

作用:给整型的key自增指定步长(可以为负数)

7.incrbyfloat

incrbyfloat key 步长

作用:给浮点型的key自增步长

浮点数没有默认增长

8.setnx

setnx key value

作用:若不存在则设置值、若存在则不设置值

9.setex

setex key expire value

作用:设置一个key的值并设置有效期

可以分为增改查模块1234、自增模块567、组合模块89来记忆

5.key的层次结构

与HashMap很类似

Redis的key允许有多个单词形成层级结构,多个单词之间用":"隔开

例如:

simonstudy:user:1

这个就是二级结构

那么我们如果想要存储一个用户id为1的java对象数据怎么办呢

我们可以将我们的用户的对象变为json字符串序列化来存储到我们的value

6.Hash类型(常用命令)

Hash类型也叫散列,其value是一个无序字典,类似于java中的HashMap结构

String结构是将对象序列化后转化为json字符串后存储,当需要修改对象某个字段时很不方便

Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做crud

也就是我们的value又拆分成了两个字段

一个是field,一个是value

keyfieldvaluesimonstudy:user:1namesimonsimonstudy:user:1age18

常见命令

1.hset

hset key field value

作用:给key的某个字段field添加值value

2.hget

hget key field

作用:获取key的某个字段field的值

3.hmset

hset key1 field1 value1 filed2 value2

作用:给一个key设置多个field的值

4.hmget

hget key1 field1 filed2

作用:返回key的多个字段的值数组

5.hgetall

hgetall key

作用:返回key的所有filed和value的数组

6.hkeys

hkeys key

作用:返回key所有的field值

7.hvals

hvals key

作用:返回key所有的value值

8.hincrby

hincrby key field 步长

作用:给key的字段的值增长指定步长

9.hsetnx

hsetnx key field value

作用:判断key的字段filed值是否存在,如果存在则不设置,如果不存在则设置

7.List类型(常用命令)

Redis中的List和LinkedList类似,可以看作一个双向链表来操作

特点:

1.有序

2.元素可以重复

3.插入和删除快

4.查询速度一般

常用来保存有序的一个列表

常见命令

1.lpush

lpush key ele1 ele2 ele3

作用:将数据从左边push进去(上面的顺序自然就是ele3<->ele2<->ele1)

2.lpop

lpop key

作用:取出最左边的元素

3.rpush

rpush key ele1 ele2 ele3

作用:将数据从右边push进去(上面的顺序自然就是ele1<->ele2<->ele3)

4.rpop

rpop key

作用:取出最右边的元素

5.lrange

lrange key index1 index2

作用:取出范围为index1-index2的元素(从0开始)

blpop和brpop

blpop key time

broop key time

作用:阻塞等待获取

思考:

如何利用List结构模拟一个栈?

同向存取

如何利用List结构模拟一个队列

异向存取

如何利用List结构模拟一个阻塞队列

异向存阻塞取

8.Set类型(常用命令)

与java中的HashSet类似

特点:

1.无序

2.元素不可重复

3.查找快

4.支持交集、并集、差集

常见命令:

1.sadd

sadd key member1 member2

作用:向set中添加一个或多个元素

2.srem

srem key a

作用:删除key集合中的某个元素

3.sismember

sismember key a

作用:key集合中是否存在a元素

4.scard

scard key

作用:返回set中元素的个数

5.smembers

smembers key

作用:获取set这种所有元素

6.sinter

sinter key1 key2

作用:获取key1和key2两个集的交集

7.sdiff

sdiff key1 key2

作用:获取key1和key2两个集的差集

8.sunion

sunion key1 key2

作用:获取key1和key2两个集的并集

练习:

1.张三的好友有:李四、王五、赵六

2.李四的好友有:王五、麻子、二狗

利用set命令完成下列功能

sadd zs ls ww zl

sadd ls ww mz eg

1.计算张三的好友有几个人

scard zs

2.计算张三和李四有哪些共同好友

求交集

sinter zs ls

3.查询哪些人是张三的好友而不是李四的

求差集

sdiff zs ls

4.查询张三和李四好友共有哪些人

求并集

sunion zs ls

5.判断李四是否是张三的好友

sismember zs ls

6.判断张三是否是李四的好友

sismember ls zs

7.将李四从张三的好友泪飙移除

srem zs ls

9.SortedSet类型(常用命令)

类似于java中的treeset类似,但是底层的数据结构差别很大,SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现

是一个调表skiplist+hash表

它具有一下特性:

1.可排序

2.元素不重复

3.查询速度快

因为可排序,所以经常被用来实现排行榜这样的功能

1.zadd

zadd key score member

作用:添加一个或多个元素到sorted set,如果已经存在就更新其score值

2.zrem

zrem key member

作用:删除sorted set中指定的一个元素

3.zscore

zscore key memeber

作用:获取指定元素的score

4.zrank

zrank key memeber

作用:获取指定元素的排名

5.zcard

zcard key

作用:获取sorted set中的个数

6.zcount

zcount key min max

作用:获取score值范围的所有元素个数

7.zincrby

zincrby key increment member

作用:给指定元素增加步长

8.zrange

zrange key min max

作用:按照score排序后获取指定排名范围的元素

9.zrangebyscore

zrangebyscore key min max

作用:按照score排序后获取指定score范围的元素

10.zdiff、zinter、zunion

交、差、并集

11.反转

z后面加上rev 表示reverse反转(默认升序)

练习:

jack 85 lucy 89 rose 82 tom 98 jerry 78 amy98 miles 76

zadd grade 85 jack 89 lucy 82 rose 95 tom 78 jerry 92 amy 76 miles

1.删除tom同学的

zrem grade tom

2.获取amy同学分数

zrem grade tom

3.获取rose同学排名

zrevrank grade rose

4.查询80分以下几个学生

zcount grade 0 80

5.给Amy同学加2分

zincrby grade 2 amy

6.查出成绩前3名同学

zrevrange grade 0 2

7.查出成绩80分以下同学

zrangebyscore grade 0 80

5.Redis客户端

1.客户端介绍

Java客户端最常见的三个客户端

1.Jedis

以Redis命令作为方法名称简单适用,但是Jedis实例是线程不安全的,多线程环境需要基于连接池来使用

2.lettuce

Lettuce是基于Netty实现,支持同步、异步或者响应式编程方式,并且是线程安全。支持哨兵模式、集群

模式和管道模式

3.Redisson

基于Redis实现的分布式、可伸缩的Java数据结构集合。包含了诸如Map、Queue、Lock、Semaphore、

AtomicLong等强大功能

4.特注

我们的Spring Data Redis定义的一套API既可以通过jedis实现也可以通过lettuce实现,所以我们后期会学

习Spring Data Redis

2.Jedis客户端

1.引入核心依赖

redis.clients

jedis

3.7.0

org.junit.jupiter

junit-jupiter

5.7.1

test

2.编写连接操作释放代码

package com.simon.test;

import com.simon.JedisConnectionFactory;

import org.junit.jupiter.api.AfterEach;

import org.junit.jupiter.api.BeforeEach;

import org.junit.jupiter.api.Test;

import redis.clients.jedis.Jedis;

import java.util.HashMap;

import java.util.Map;

public class JedisTest {

private Jedis jedis;

@BeforeEach

void setUp(){

//建立连接

jedis = new Jedis("192.168.11.100",6379);

//设置密码

jedis.auth("simon0414");

jedis.select(0);

}

@Test

void testString() throws InterruptedException {

String result = jedis.set("name", "simon");

System.out.println(result);

String value = jedis.get("name");

System.out.println(value);

}

@Test

void testHash(){

HashMap map = new HashMap<>();

map.put("name","simon");

map.put("age","18");

String result = jedis.hmset("user:1", map);

System.out.println(result);

Map stringStringMap = jedis.hgetAll("user:1");

System.out.println(stringStringMap);

}

@AfterEach

void tearDown(){

if (jedis != null){

jedis.close();

}

}

}

3.Jedis客户端连接池优化

JedisConnectionFactory

package com.simon;

import redis.clients.jedis.Jedis;

import redis.clients.jedis.JedisPool;

import redis.clients.jedis.JedisPoolConfig;

/**

* @author Simon

*/

public class JedisConnectionFactory {

private static final JedisPool JEDIS_POOL;

static {

JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();

//最大连接

jedisPoolConfig.setMaxTotal(8);

//最大空闲连接

jedisPoolConfig.setMaxIdle(8);

//最小空闲连接

jedisPoolConfig.setMinIdle(8);

//最长等待时间

jedisPoolConfig.setMaxWaitMillis(1000);

JEDIS_POOL = new JedisPool(jedisPoolConfig,"192.168.11.100",6379,1000,"simon0414");

}

public static Jedis getJedis(){

return JEDIS_POOL.getResource();

}

}

JedisTest

package com.simon.test;

import com.simon.JedisConnectionFactory;

import org.junit.jupiter.api.AfterEach;

import org.junit.jupiter.api.BeforeEach;

import org.junit.jupiter.api.Test;

import redis.clients.jedis.Jedis;

import java.util.HashMap;

import java.util.Map;

public class JedisTest {

private Jedis jedis;

@BeforeEach

void setUp(){

// //建立连接

// jedis = new Jedis("192.168.11.100",6379);

// //设置密码

// jedis.auth("simon0414");

//

// jedis.select(0);

jedis = JedisConnectionFactory.getJedis();

}

@Test

void testString() throws InterruptedException {

String result = jedis.set("name", "simon");

System.out.println(result);

String value = jedis.get("name");

System.out.println(value);

}

@Test

void testHash(){

HashMap map = new HashMap<>();

map.put("name","simon");

map.put("age","18");

String result = jedis.hmset("user:1", map);

System.out.println(result);

Map stringStringMap = jedis.hgetAll("user:1");

System.out.println(stringStringMap);

}

@AfterEach

void tearDown(){

if (jedis != null){

jedis.close();

}

}

}

4.Spring Data Redis

1.Spring Data Redis介绍

我们使用Spring Data Redis有什么好处呢?

1.提供对不同Redis客户端的整合

2.提供了同一的RedisTemplate统一api来操作Redis

3.支持Redis的发布订阅模型

4.支持Redis哨兵和Redis集群

5.支持基于Lettuce响应式编程

6.支持基于JDK、Json、字符串、Spring对象的数据序列化和反序列化

7.支持基于Redis的JDKCollection实现(基于Redis实现的分布式的集合)

2.API分组

我们知道我们Redis中对命令做了分组

同样我们的Spring Data Redis也做了

1.redisTemplate 通用命令

redisTemplate.opsForValue 对String类型的命令

redisTemplate.opsForHash 对Hash类型的命令

redisTemplate.opsForList 对List类型的命令

redisTemplate.opsForSet 对Set类型的命令

redisTemplate.opsForSortedSet 对SortedSet类型的命令

3.快速入门

因为我们SpringBoot对starter依赖做好了自动装配,所以我们使用SpringBoot快速整合

1.导入依赖(其实还有类似于lombok和SpringBootStarterTest依赖这里只展示核心的)

org.springframework.boot

spring-boot-starter-data-redis

org.apache.commons

commons-pool2

这两个是依赖一个是SpringBoot的data-redis的起步依赖

第二个是连接池依赖

注意:我们SpringDataRedis默认的客户端是Lettuce奥,这一点我们看spring-boot-starter-data-redis里面包含的依赖也是可以发现的

2.配置我们的yml文件(当然你也可以写配置类非常麻烦)

我个人就上yml了

spring:

redis:

host: 192.168.11.100

port: 6379

password: "simon0414"

#我们的spring data redis 默认使用的是Lettuce奥,如果使用jedis就要引入jedis依赖

lettuce:

pool:

max-active: 8

max-idle: 8

min-idle: 0

max-wait: 100 #连接等待时间ms

写你自己的连接数据

3.编写测试类

package com.simon;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest

public class RedisTest {

@Autowired

private RedisTemplate redisTemplate;

@Test

void testString(){

redisTemplate.opsForValue().set("name","ximeng");

//获得String数据

Object name = redisTemplate.opsForValue().get("name");

System.out.println(name);

}

}

测试成功

4.注意问题

但其实是有问题的

我们发现我们原有的name并没有被覆盖

反而多了一个很奇怪的key

\xAC\xED\x00\x05t\x00\x04name

他的value为\xAC\xED\x00\x05t\x00\x06ximeng

我们之间说过我们的SpringDataRedis会给我们自动进行序列化,我们传入set的时候都可以接受Object对象

这个时候它会将我们传入的String作为对象来处理,通过ObjectOutputStream输出的所以说就变成了这样

我们可以点进去看看

@Nullable

private RedisSerializer keySerializer = null;

@Nullable

private RedisSerializer valueSerializer = null;

@Nullable

private RedisSerializer hashKeySerializer = null;

@Nullable

private RedisSerializer hashValueSerializer = null;

public void afterPropertiesSet() {

super.afterPropertiesSet();

boolean defaultUsed = false;

//当我们的defaultSerializer为null时,那么就会将赋值为JdkSerializationRedisSerializer,而它序列化采用就是ObjectOutputStream

//可以打断点去看,我这里就不深入了

if (this.defaultSerializer == null) {

this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());

}

if (this.enableDefaultSerializer) {

if (this.keySerializer == null) {

this.keySerializer = this.defaultSerializer;

defaultUsed = true;

}

if (this.valueSerializer == null) {

this.valueSerializer = this.defaultSerializer;

defaultUsed = true;

}

if (this.hashKeySerializer == null) {

this.hashKeySerializer = this.defaultSerializer;

defaultUsed = true;

}

if (this.hashValueSerializer == null) {

this.hashValueSerializer = this.defaultSerializer;

defaultUsed = true;

}

}

if (this.enableDefaultSerializer && defaultUsed) {

Assert.notNull(this.defaultSerializer, "default serializer null and not all serializers initialized");

}

if (this.scriptExecutor == null) {

this.scriptExecutor = new DefaultScriptExecutor(this);

}

this.initialized = true;

}

我们总结下来我们如果想采用我传入啥值就用什么序列化器

我们发现我们的RedisSerializer的序列化器接口实现类有很多其他的实现类

例如:OxmSerializer、ByyeArrayRedisSerializer、GenericJackson2JsonRedisSerializer、GenricToStringSerializer、StringRedisSerializer、JdkSeializationRedisSerializer、Jackson2JsonRedisSerializer

倘若我们的key为String类型、值为对象

我们该如何去修改这个序列化器呢?

很简单,我们只需要写配置类就行了

package com.simon.config;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.connection.RedisConnectionFactory;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;

import org.springframework.data.redis.serializer.RedisSerializer;

/**

* @author Simon

*/

@Configuration

public class RedisConfig {

@Bean

public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){

//创建RedisTemplate对象

RedisTemplate redisTemplate = new RedisTemplate<>();

//设置连接工厂

redisTemplate.setConnectionFactory(redisConnectionFactory);

//创建Json序列化工具

GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();

//设置key的序列化

//单例对象

redisTemplate.setKeySerializer(RedisSerializer.string());

redisTemplate.setHashKeySerializer(RedisSerializer.string());

//设置value的序列化

redisTemplate.setValueSerializer(valueSerializer);

redisTemplate.setHashValueSerializer(valueSerializer);

//返回

return redisTemplate;

}

}

我们针对RedisTemplate上述的情形的redisTemplate注入了我们自己的序列化器以及连接池

然后执行我们的测试代码,有的同学可能有问题会报错

即Jackson依赖的ClassNotFind的错误(我们都知道我们SpringMvc中有JackSon的依赖,因为处理对象请求体也会用到)

我们这里没有引入我们的SpringMvc所以我们就自然引入一下JackSon的依赖

com.fasterxml.jackson.core

jackson-databind

然后执行测试代码成功

5.测试对象写入

我们知道我们配置的是key为String而value为对象的RedisTemplate,所以我们可以测试一下写入User

package com.simon.pojo;

import lombok.Data;

/**

* @author Simon

*/

@Data

public class User {

private String name;

private Integer age;

}

@Test

void testUser(){

User user = new User();

user.setName("simon");

user.setAge(18);

redisTemplate.opsForValue().set("user:1",user);

User user1 = (User) redisTemplate.opsForValue().get("user:1");

System.out.println(user1);

}

不仅写入了User的Json字符串,还可以反序列化我们的数据

怎么做到的呢?

我们来看看存储的数据

{

"@class": "com.simon.pojo.User",

"name": "simon",

"age": 18

}

原来把我们的类写入了,所以自动化地反序列化我们的数据了但是我们这样会浪费我们的内存空间

使用起来确实很方便,但是我们不太希望这样,如果我们的每一个对象都存储了这个信息就会多占

用浪费很多空间无法接受,所以我们基本不会使用JSON序列化器来处理value,所以统一使用String

序列化器,要求只能存取String类型的key和value,当需要存储Java对象时,我们手动完成对象的序

列化和反序列化。

6.StringRedisTemplate

我们为了解决问题,我们Spring默认提供了一个StringRdisTemplate类,它的key和value的序列化

方式默认为String,我们自动使用即可

1.配置连接池

@Bean

public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory){

StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();

stringRedisTemplate.setConnectionFactory(redisConnectionFactory);

return stringRedisTemplate;

}

2.测试

@Autowired

private StringRedisTemplate stringRedisTemplate;

private static final ObjectMapper MAPPER = new ObjectMapper();

@Test

void TestStringUser() throws JsonProcessingException {

User user = new User();

user.setName("StringSimon");

user.setAge(18);

//手动序列化

String userJson = MAPPER.writeValueAsString(user);

stringRedisTemplate.opsForValue().set("user:1",userJson);

//手动反序列化

String userGetJson = stringRedisTemplate.opsForValue().get("user:1");

User userGet = MAPPER.readValue(userGetJson, User.class);

System.out.println(userGet);

}

//Hash类似的只是改成了Put

测试成功

7.总结

RedisTemplate序列化实践方案

一.

1.自定义RedisTemplate

2.修改RedisTemplate的序列化器为GennericJack2JsonRedisSerializer

二.

1.使用StringRedisTemplate

2.手动序列化和反序列化对象

好文链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。