Redis数据库学习笔记

Redis学习

Redis简介

Redis是完全开源免费的,遵守BDS协议,是一个高性能的NOSQL的key-value数据库,Redis是使用ANSI C语言编写的,支持网格,可基于内存亦可以持久化的日志型、key-value数据库,并提供了多种语言的API支持

NOSQL:指的是非关系型数据库,NOSQL即Not-Only SQL,可作为传统关系型数据库的补充

Redis特点

Redis优点

  • 高性能 – Redis能读写的速度是110000次/s,写的速度是81000次/s
  • 丰富的数据类型 --Redis支持的数据类型有String、Hash、List、Set、Ordered Set
  • 原子性 – Redis的所有操作都是具有原子性的,即要么执行成功,要么执行失败完全不执行
  • 丰富的特性 – Redis还支持publish、subscribe、通知、key过期等特性
  • 高速读写 – Redis使用自己实现的分离器,没有使用lock(MySQL),因此效率非常高

Redis缺点

  • 持久化 – Redis直接将数据存储到内存中,如果要将数据持久化到硬盘中,Redis有两种方式实现持久化
    • 定时快照:每隔一段时间将整个内存中Redis数据库的内容持久化到硬盘,每次操作均是全量数据,因此对磁盘读写量较大
    • 语句追加:只追加变化的数据,但是追加的log可能过大,同时所有的操作均需要重新执行一遍,回复速度慢
  • 耗内存 – 因为Redis所有的数据都是加载到内存中来实现高速读写,这样的话就会导致内存占用过高

Centos7安装Redis

Redis官网:http://redis.io

1、安装gcc

## 因为Redis是使用C语言编写的,我们安装需要进行编译,则需要安装gcc
yum install -y gcc automake autoconf libtool make

2、获取Redis安装包

wget http://download.redis.io/releases/redis-5.0.8.tar.gz

3、解压下载好的压缩包

tar -zxvf redis-5.0.8.tar.gz -C /opt

4、编译Redis

cd /opt/redis-5.0.8 && make

出现:Hint:It's a good idea to run 'make test' :)表示安装成功

5、进行安装Redis

## 安装到指定目录/usr/local/redis
make PREFIX=/usr/local/redis install

6、Redis启动、关闭

## 启动Redis服务端
cd /usr/local/redis/bin/ && ./redis-server

## 启动Redis客户端
./redis-cli -h ip地址 -p 端口号   ## 不设置参数默认是127.0.0.1 端口号是6379
cd /usr/local/redis/bin/ && ./redis-cli

## 有关参数的详细信息可以使用--help参数进行查看
./redis-cli --help

## 查看启动的Redis
ps -ef | grep -i redis

## 启动客户端执行PING可以测试与服务端的连接
[root@localhost bin]# ./redis-cli
127.0.0.1:6379> ping
PONG

## 关闭客户端,在服务端输入以下命令
exit

## 关闭服务端,在客户端输入以下命令
shutdown
./redis-cli shutdown

7、配置Redis

首先将Redis停止掉,因为是前台启动的可以直接ctrl+c停止掉

## 复制解压目录下的redis.conf文件到安装文件的目录下
cp /opt/redis-5.0.8/redis.conf /usr/local/redis/ 

## 启动Redis并加载自己的配置文件
./redis-server /path/redis.conf
Redis配置文件redis.conf
## 绑定的主机地址,如果需要设置任何IP都可以访问,则注释掉即可
bind 127.0.0.1

## 指定Redis的启动端口号,默认是6379
port 6379

## 设置客户端的连接超时时间,设置为0则表示关闭超时断开功能
timeout 300

## Redis默认是以前台形式启动的,只需要修改no为yes则可以以守护进程的方式启动Redis
daemonize no

## Redis以守护进程运行时会把pid写入到/var/run/redis_6379.pid文件,我们进行手动配置
pidfile /var/run/redis_6379.pid

## 设置日志的级别,可用的级别有:debug、verbose、notice、warning
loglevel verbose

## 指定日志输出的文件名称,默认为空
logfile ""

## 设置Redis的数据库数量,默认为16,每个数据库的下标从0开始,可以使select进行切换
databases 16

## 指定在多长的时间内有多少次更新操作,Redis默认提供了三种方式
## save <seconds> <changes>
## 需要注意的是如果在时间范围内修改了对应的数据,但是将Redis非正常停止(杀死进程、服务器关机、服务器断电)则会引起数据丢失
save 900 1     # 900秒(15分钟)后,如果至少更改了1个键
save 300 10    # 300秒(5分钟)后,如果至少更改了10个按键
save 60 10000  # 60秒后,如果至少更改了10000个键

## 指定了数据存储到本地是否进行压缩操作,默认为yes
rdbcompression yes

## 指定本地数据库文件名称
dbfilename dump.rdb

## 指定本地数据库存放目录,目录必须存在,否则启动报错
dir ./

## 设置连接Redis的密码,最好将密码设置为较为复杂的密码,以防止穷举暴力破解
requirepass admin@123

## 设置最大客户端连接数量,如果设置为0则表示不显示,如果超过最大连接数量则提示:max number of clients reached
maxclients 10000

## 设置Redis的最大内存限制
maxmemory <bytes>

## 指定在每次更新操作之后进行日志记录,默认no
appendonly no

## 指定更新日志文件名称,默认为ppendonly.aof
appendfilename "appendonly.aof"

## 指定更新日志的条件
## no 表示每次等待操作系统进行数据缓存同步到磁盘(快)
## always 表示每次更新操作后手动调用fsync()将数据写入到磁盘(慢、安全)
## everysec 表示每秒同步一次(默认值)
appendfsync everysec

Redis内存淘汰机制

1、为数据设置超时时间

  • 设置超时时间

    除了字符串有特殊的方式,别的数据类型都需要依赖expire

    如果没有设置超时时间,那么数据将永久有效

    设置超时时间之后,又不想让数据过期,则使用persist key

    ## 常用的方式
    expire key time()
    
    ## 字符串独有的方式
    setex(String key,int seconds,String value)
    

2、采用LRU算法动态将不用的数据删除

内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据那些数据属于LRU而将其移出内存而腾出空间来加载别的数据

volatile-lru : 设定超时时间的数据中删除不常使用的数据(常用)
allkeys-lru  : 查询所有的key中最近不常使用的数据进行删除,这是应用最广泛的策略(常用)
volatile-random : 在设定了超时的数据中随机删除
allkeys-random : 查询所有的key,然后随机删除
volatile-ttl : 查询全部设定超时时间的数据,之后排序,将马上要过期的数据进行删除
noeviction : 如果设置为该属性,则不会进行删除操作,如果内存溢出则报错返回
volatile-lfu : 从所有配置了过期时间的key中删除使用频率最少的key
allkeys-lfu : 从所有key中删除使用频率最少的key

远程连接Redis

远程连接Redis需要使用到远程连接工具RedisDesktopManager

如果要远程连接,则需要完成Redis以下配置:

开启Redis的远程连接

设置Redis的连接密码

RedisDesktopManager官方网站地址:https://redisdesktop.com/

远程连接工具安装

RedisDesktopManager从0.9.4版本开始不再提供免费的版本,需要安装的话有两种办法

1、下载0.9.4之前的版本进行安装

2、下载安装破解版本

3、使用一些开源的Redis管理工具项目提供的免费版的工具

  • RedisDesktopManager-Windows:https://github.com/jfcherng/RedisDesktopManager-Windows/releases
  • AnotherRedisDesktopManager:https://github.com/qishibo/AnotherRedisDesktopManager/releases

Docker安装Redis

在https://hub.docker.com/搜索Redis并选择适合的版本

下载镜像

docker pull redis

创建容器并运行

docker run -d --name redis_6379 -p 6379:6379 redis --requirepass '密码'

Redis常用命令

常用key命令

## 返回满足条件的所有key,可以使用*模糊匹配 keys a*
keys *
## 是否存在指定的key,存在返回1,不存在返回0
exists key
## 设置指定key的过期时间,单位:秒
expire key second
## 删除key
del key
## 查询key的剩余存活时间,返回剩余时间,单位:秒,不存在返回-2,存在但是没有剩余时间返回-1
ttl key
## 取消过期时间
persist key
## 修改key的过期时间,单位:毫秒
pexpire key millisecond
## 选择数据库,index为数据库标识,0开始
select index
## 将当前数据库中的数据转移到其他数据库
move key dbindex
## 返回一个随机的key
randomkey
## 重命名key
rename oldkeyname newkeyname
## 查询key的存储类型
type key
## 打印命令
echo
## 查询数据库的key数量
dbsize
## 查看数据库信息
info
## 实时传输收到的请求的配置key,返回Redis相关的配置,*代表返回所有配置
config get *
## 清空当前数据库
flushdb
## 清空所有数据库
flushall

key的命名规范

Redis单个key的最大允许存入512M

  • key不要太长,尽量不要超过1024字节,key的长度会消耗内存且降低查询效率
  • key不要太短,不然会降低可读性
  • 命名之间的连接最好使用:这样就可以将相同的进行归类
  • 同一项目中最好使用统一的命名规则
  • key区分大小写

Redis数据类型

String类型

String 是Redis最基本的数据类型,一个key最大能存储512MB的数据

String类型是二进制的,也就是说String可以包含任何数据,比如序列化的对象,一张图片,简单的字符串,一个数字等

应用场景
  • 用于保存单个字符串或者json、xml数据
  • 保存图片文件
  • 计数器(阅读数,粉丝数)
String命令
赋值语法
SET KEY_NAME 'VALUE' :多次赋值会将key对应的值覆盖,且无视类型

## (not exist)如果key不存在,则赋值并返回1,如果key存在,则不设置,并返回0(解决了分布式锁)
setnx key value

## (expired)设置key的值为value,过期时间为10秒,10秒后key清除
setex key 10 value

## 替换字符串
setrange string range value

## 批量写入
mset key1 value1 key2 value2...

## 自增,将key中存储的数字+1,如果key不存在,则初始化key的值为0,然后在执行自增操作
incr key 

## 指定增量的值
incrby key 增值

## 自减,将存储的字符类型的数字-1,自增自减需要注意的是存储的值必须是字符类型的数字
decr key

## 指定减量的值
decrby key 减值

## 字符串拼接,用于给指定key的值拼接字符(末尾追加),如果key不存在,则直接赋值
append key value
取值语法
## Redis get命令用于获取指定key的值,如果key不存在,则返回nil,如果存储的值不是一个字符串,则返回一个错误
get key_name 

## 用于获取存储在指定key中字符串的子字符串,字符串的截取范围有start和end两个偏移量决定(包前包后)
getrange key start end

## 对key所存储的字符串的值,获取该偏移量上的位(bit)
getbit key offset

## getset命令用于设置指定key的值,返回key的旧值,当key不存在时返回nil
getset key_name value

## 返回key所存储的字符串的长度
serlen key

## 批量取值
mget key1 key2...
删除语法
## 删除指定的key,如果存在,则返回数字类型
del key

Hash类型

Hash是String类型的field和value的映射表,hash可以看成一个key-value的map容器特别适合存储对象,相比较而言将一个对象存储在hash类型要比存储在String类型中占用更少的内存空间

Redis中每个hash可以存储2的32次方

应用场景
  • 存储对象内容,比如用户基本信息对象等
Hash命令
赋值语法
## 为指定的key设定field和value,如果key已经存在则是在已经存在的key中追加对应的field和value,如果对应的field也存在了,则是给对应的field重新赋值
hset key field value

## 同时设定多个field和value
hmset key field1 value1 field2 value2.... 

## 类似于字符串的setnx,只判断field是否存在,如果不存在,才会新增,否则不进行增加
hsetnx key field value

## 为指定的key中的指定字段的整数增加增量
hincrby key field 增量

## 为指定的key中的指定字段的浮点增加增量
hincrbyfloat key field 增量
取值语法
## 获取指定key中field对应的值
hget key field

## 获取指定key中多个field对应的值
hmget key field1 field2...

## 返回指定key中存储的所有field和value
hgetall key

## 获取key对应的所有hash表中的字段
hkeys key

## 获取key对应的hash表中的字段数量
hlen key

## 查询hash表key中指定的field字段是否存在
hexists key field
删除语法
## 可以将key对于的hash类型中存储的field对应的值删除掉
hdel key field

## 删除整个key对应的hash数据
del key

Redis-Client

Redis-Client介绍

jedis api地址:https://tool.oschina.net/uploads/apidocs/redis/clients/jedis/Jedis.html

redisson 官网地址:https://redisson.org/

redisson git项目地址:https://github.com/redisson/redisson

lettuce 官网地址:https://lettuce.io/

lettuce git项目地址:https://github.com/lettuce-io/lettuce-core

需要注意的是在Springboot2之后,对Redis的支持默认使用了lettuce

概念

Jedis:是Redis的Java实现客户端,提供了比较全面的Redis命令的支持,

Redisson:实现了分布式和可扩展的Java数据结构。

Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。

优点

Jedis:比较全面的提供了Redis的操作特性

*Redisson:促使使用者对Redis的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过Redis支持延迟队列

*Lettuce:基于Netty框架的时间驱动通讯层,其方法调用是异步的,Lettuce的API是线程安全的,可以操作单个Lettuce连接来完成各种操作

可伸缩性

Jedis:使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis。

Redisson:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Redisson的API是线程安全的,所以可以操作单个Redisson连接来完成各种操作

Lettuce:可以支持Redis4,但是需要jdk8及以上

SpringBoot整合Jedis

注意:Jedis2.7以上版本才支持集群的操作

所有代码均已上传gitee:https://gitee.com/frostysun/Learn.git

引入依赖
<dependency>
	<groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
配置application
spring:
  redis:
    port: 6379          # Redis的端口号
    password: admin@123 # Redis的密码
    host: 192.168.3.20  # Redis的服务器地址
    jedis:
      pool:
        max-active: 10  #最大连接数,默认为8
        max-idle: 6     # 最大空闲数量,默认为8
        min-idle: 2     # 最小空间数量,默认为0
    timeout: 2000       # 连接超时
Config

创建类文件:com.example.jedis.JedisConfig

package com.example.jedis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author : frosty0804@gmail.com
 * @version : v1.0
 * @projectName : Learn
 * @className : JedisConfig
 * @description : TODO(Jedis连接初始化配置类)
 * @date : 2020/3/26 15:44
 */
@Slf4j
@Configuration
public class JedisConfig {

    /**
     * Redis的服务器地址
     */
    @Value("${spring.redis.host}")
    private String host;
    /**
     * Redis的端口号
     */
    @Value("${spring.redis.port}")
    private int port;
    /**
     * Redis的密码
     */
    @Value("${spring.redis.password}")
    private String password;
    /**
     * 连接超时时间
     */
    @Value("${spring.redis.timeout}")
    private int timout;
    /**
     * 最大连接数
     */
    @Value("${spring.redis.jedis.pool.max-active}")
    private int maxActive;
    /**
     * 最大空闲数量
     */
    @Value("${spring.redis.jedis.pool.max-idle}")
    private int maxIdle;
    /**
     * 最小空间数量
     */
    @Value("${spring.redis.jedis.pool.min-idle}")
    private int minIdle;

    @Bean
    public JedisPool jedisPool() {
        // 实例化JedisPoolConfig对象并将配置参数重赋值到对象中
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMinIdle(minIdle);
        jedisPoolConfig.setMaxTotal(maxActive);

        // 实例化JedisPool连接对象
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timout, password);
        log.info("JedisPool连接成功:" + host + ":" + port);
        return jedisPool;
    }

}

Jedis操作String类型

1、工具类

package com.example.jedis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

/**
 * @author : frosty0804@gmail.com
 * @version : v1.0
 * @projectName : Learn
 * @className : JedisUtil
 * @description : TODO(Jedis工具类)
 * @date : 2020/3/26 16:53
 */
@Component
public class JedisUtil {

    @Autowired
    private JedisPool jedisPool;

    /**
     * 获得jedis对象
     */
    public Jedis getJedis() {
        return jedisPool.getResource();
    }

    /**
     * 释放jedis资源
     */
    public void close(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }

}

2、Service

package com.example.service.impl;

import com.example.jedis.JedisUtil;
import com.example.model.User;
import com.example.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.Map;	
@Slf4j
@Service("UserService")
public class UserServiceImpl implements UserService {
	/**
     * 注入连接池
     */
    @Autowired
    private JedisPool jedisPool;

    @Autowired
    private JedisUtil jedisUtil;


    /**
     * TODO 字符串类型的演示
     * 需求:
     * 用户输入一个key
     * 先判断Redis中是否存在该key
     * 如果存在,则在Redis中进行查询,并返回该值
     * 如果不存在,则在MySQL数据库进行查询,并将查询到的值赋给Redis然后返回
     *
     * @param key 请求Redis的key
     * @return Redis中存在的key对应值
     */
    @Override
    public String getString(String key) {
        // 初始化jedis对象
        Jedis jedis = jedisUtil.getJedis();
        String result = null;
        try {
            // 对调用jedis进行key的判断是否存在于Redis
            if (jedis.exists(key)) {
                log.info("数据存在,查询Redis中的数据");
            } else {
                String value = "我是一个没的感情的测试数据";
                log.info("数据不存在于Redis,查询MySQL数据库:" + value);
                log.info("将MySQL查询得到的数据赋值给Redis");
                jedis.set(key, value);
            }
            result = jedis.get(key);
        } catch (Exception e) {
            log.error("jedis查询异常", e);
        } finally {
            jedisUtil.close(jedis);
        }
        return result;
    }
}

3、单元测试

package com.example;

import com.example.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import redis.clients.jedis.JedisPool;

@SpringBootTest
class SpringbootJedisApplicationTests {
    
    @Autowired
    private JedisPool jedisPool;

    @Autowired
    private UserService userService;
    
    /**
     * 模拟操作String类型的数据
     */
    @Test
    void str() {
        System.out.println(userService.getString("name"));
    }   
}
Jedis操作Hash类型

1、Model

package com.example.model;

import lombok.Data;

/**
 * @author : frosty0804@gmail.com
 * @version : v1.0
 * @projectName : Learn
 * @className : User
 * @description : TODO(使用一句话概括这个类)
 * @date : 2020/3/26 15:32
 */
@Data
public class User {

    String id;
    String name;
    String age;
}

2、Service

package com.example.service.impl;

import com.example.jedis.JedisUtil;
import com.example.model.User;
import com.example.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.Map;

/**
 * @author : frosty0804@gmail.com
 * @version : v1.0
 * @projectName : Learn
 * @className : UserServiceImpl
 * @description : TODO(使用一句话概括这个类)
 * @date : 2020/3/26 16:13
 */
@Slf4j
@Service("UserService")
public class UserServiceImpl implements UserService {

    /**
     * 注入连接池
     */
    @Autowired
    private JedisPool jedisPool;

    @Autowired
    private JedisUtil jedisUtil;

    /**
     * TODO hash类型的操作
     * 需求:
     * 前端传入一个用户ID
     * 根据用户传入的ID进行查询对象信息
     * 首先到Redis中查询,如果存在,则返回给页面
     * 如果不存在,则去数据库查询,将结果写入到Redis中,并返回给页面
     *
     * @param id 传入要查询的用户ID号码
     * @return 用户信息
     */
    @Override
    public User queryId(String id) {
        Jedis jedis = jedisUtil.getJedis();
        User user = new User();
        Map<String, String> map = new HashMap<>();
        try {
            String key = "user:" + id;
            if (jedis.exists(key)) {
                log.info("Redis存在数据,查询Redis");
                map = jedis.hgetAll(key);
                user.setId(map.get("id"));
                user.setName(map.get("name"));
                user.setAge(map.get("age"));
            } else {
                log.info("Redis不存在数据,查询MySQL数据库");
                user.setId(id);
                user.setName("李四");
                user.setAge("18");
                log.info("将MySQL中查询到的数据赋值到Redis数据库中");
                map.put("id", user.getId());
                map.put("name", user.getName());
                map.put("age", user.getAge());
                jedis.hmset(key, map);
            }
        } catch (Exception e) {
            log.error("查询Redis异常", e);
        } finally {
            jedisUtil.close(jedis);
        }
        return user;
    }
}

3、单元测试

package com.example;

import com.example.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import redis.clients.jedis.JedisPool;

@SpringBootTest
class SpringbootJedisApplicationTests {

    @Autowired
    private JedisPool jedisPool;

    @Autowired
    private UserService userService;
    
    @Test
    void hash() {
        System.out.println(userService.queryId("2"));
    }
}

SpringBoot2.x整合lettuce

lettuce需要Springboot2.x版本才可以支持

引入依赖
<!-- 默认的lettuce客户端 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis依赖commons-pool这个一定要添加 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
配置文件设置
spring:
  redis:
    port: 6379
    password: admin@123
    host: 192.168.3.20
    lettuce:
      pool:
        max-active: 8 # 连接池最大连接数量,默认为8,负值表示没有限制
        max-idle: 8 # 连接池中最大空闲连接,默认为8
        min-idle: 0 # 连接池中最小空闲连接,默认为0
        max-wait: 1000 # 连接池最大阻塞等待时间,负值表示没有限制
      shutdown-timeout: 100 # 超时关闭时间
RedisConfig配置文件
package com.example.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.lang.reflect.Method;


/**
 * @author : frosty0804@gmail.com
 * @version : v1.0
 * @projectName : Learn
 * @className : RedisConfig
 * @description : TODO(RedisConfig配置文件)
 * @date : 2020/3/27 11:20
 */
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * 自定义缓存key的生成策略,
     */
    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object o, Method method, Object... objects) {
                StringBuilder sb = new StringBuilder();
                sb.append(o.getClass().getName());
                sb.append(method.getName());
                for (Object obj : objects) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    /**
     * 缓存配置管理器
     *
     * @param factory
     * @return
     */
    @Bean
    public CacheManager cacheManager(LettuceConnectionFactory factory) {
        // 以锁的方式创建RedisCacheWriter对象
        RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);
        // 创建默认缓存配置对象
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
        RedisCacheManager cacheManager = new RedisCacheManager(writer, configuration);
        return cacheManager;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jsonRedisSerializer.setObjectMapper(objectMapper);

        StringRedisSerializer serializer = new StringRedisSerializer();

        // 在使用注解@Bean返回RedisTemplate的时候,同时配置hashKey于hashValue的序列化方式
        // key采用String的序列化方式
        template.setKeySerializer(serializer);
        // value采用jackson的序列化方式
        template.setValueSerializer(jsonRedisSerializer);

        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(serializer);
        // hash的value采用jackson的序列化方式
        template.setHashValueSerializer(jsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}
lettuce操作String

1、服务层

package com.example.service;

import com.example.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.jws.soap.SOAPBinding;

/**
 * @author : frosty0804@gmail.com
 * @version : v1.0
 * @projectName : Learn
 * @className : UserService
 * @description : TODO(服务层)
 * @date : 2020/3/27 13:07
 */
@Slf4j
@Service
public class UserService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;


    /**
     * 需求:
     * 用户输入一个key
     * 先判断Redis中是否存在该key
     * 如果存在,则在Redis中进行查询,并返回该值
     * 如果不存在,则在MySQL数据库进行查询,并将查询到的值赋给Redis然后返回
     *
     * @param key 请求Redis的key
     * @return Redis中存在的key对应值
     */
    public String getString(String key) {
        String value = null;
        try {
            // hasKey和existe类似,都是用来判断key是否存在
            if (redisTemplate.hasKey(key)) {
                log.info("key存在于Redis数据库");
                value = String.valueOf(redisTemplate.opsForValue().get(key));
            } else {
                log.info("key不存在于Redis数据库,向MySQL查询数据");
                value = "我是一个没的感情的测试数据";
                log.info("将数据添加到Redis数据库");
                redisTemplate.opsForValue().set(key, value);
            }
        } catch (Exception e) {
            log.error("Redis数据库访问异常", e);
        }
        return value;
    }
}

2、单元测试

package com.example;

import com.example.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringbootLettuceApplicationTests {

    @Autowired
    private UserService userService;

    @Test
    void test1() {
        System.out.println(userService.getString("test1"));
    }
}
lettuce操作Hash类型

1、服务层

package com.example.service;

import com.example.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.jws.soap.SOAPBinding;

/**
 * @author : frosty0804@gmail.com
 * @version : v1.0
 * @projectName : Learn
 * @className : UserService
 * @description : TODO(使用一句话概括这个类)
 * @date : 2020/3/27 13:07
 */
@Slf4j
@Service
public class UserService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 测试hash类型
     *
     * @param id 传入要查询的用户ID号码
     * @return 用户信息
     */
    public User queryId(String id) {
        User user = new User();
        try {
            // 进行判断,是否存在对应的对象以及对象中的字段
            if (redisTemplate.opsForHash().hasKey("user", id)) {
                log.info("Redis中存在对应的对象数据,查询Redis");
                user = (User) redisTemplate.opsForHash().get("user", id);
            } else {
                log.info("Redis中不存在对应的数据,查询MySQL数据库");
                user.setId(id);
                user.setName("张三");
                user.setAge("22");
                /**
                 * @param h 存储的对象名称
                 * @param hk 用户的主键
                 * @param hv 要存储的对象
                 */
                redisTemplate.opsForHash().put("user", id, user);
            }
        } catch (Exception e) {
            log.error("Redis数据库访问异常", e);
        }
        return user;
    }
}

2、单元测试

package com.example;

import com.example.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringbootLettuceApplicationTests {

    @Autowired
    private UserService userService;

    @Test
    void test2() {
        System.out.println(userService.queryId("1002"));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值