REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。
Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
Redis 与其他 key - value 缓存产品有以下三个特点:
Redis的优势:
官网:https://redis.io/
中文官网:http://www.redis.cn/
下载地址:https://redis.io/download
或者
wget http://download.redis.io/releases/redis-5.0.3.tar.gz
如果是直接使用wget下载的就不用再上传
tar -zxvf redis-5.0.3.tar.gz
解压后目录
redis 是基于C++开发的,所有要安装C++的环境
yum -y install gcc-c++ autoconf automake
cd redis-5.0.3 # 进入redis目录
make # 预编译
创建一个安装目录,不然会安装到一个默认路径,后期路径不好找
mkdir -p /usr/local/redis
安装到指定路径
make PREFIX=/usr/local/redis/ install
默认就是前台启动,但是前台启动有一个不好的地方,就是Redis会一致占用前台输入,无法进行别的从操作。这是为了方便就需要把Redis设置为后台启动,让它在后台运行,不耽误前台进行别的操作。
1、ctrl+c,结束前台程序运行
2、复制解压的redis目录下的redis.conf文件到安装的路径
cp /root/redis-5.0.3/redis.conf /usr/local/redis/bin
3、修改bin目录下的redis.conf文件
vim redis.conf
修改daemonize on
为 daemonize yes
4、再次运行查看
再次启动程序,需要戴胜redis.conf文件,否则还是前台运行
./redis-server redis.conf # 启动程序
ps -ef |grep redis # 查看和redis相关的进程
5、后台进程关闭
前台启动的话只需要ctrl+c就可以关闭,但是后台启动相比就麻烦了点,需要直接关闭进程
kill -9 5977
5977:进程号
下载地址:https://lanzous.com/ickc0yd
常规安装,选择安装路径,下一步就可以了
默认情况下,Redis是不允许别的客户端连接访问的,只能是本机(127.0.0.1)访问,需要设置一下,否则windows客户端连接不上redis
vim redis.conf
1、注释掉bind 127.0.0.1
# bind 127.0.0.1
如果注释掉bind就表示可以是任何ip访问,如果是想要指定的IP访问,直接在127.0.0.1 后添加指定IP就可以
2、关闭保护模式,设置protected-mode为no,否则依然无法连接
protected-mode no
3、设置访问认证(密码)
默认状态下是没有开启认证的,需要手动取消#
注释(该命令大概在500行左右)
requirepass root
4、保存退出,结束进程,重新启动redis
./redis-server redis.conf
Redis-cli就是Redis自带的一个客户端程序,可以用来操作Redis,在安装的路径里
./redis-cli -p 6379 -a root
set name zhangsan # 添加一个名为name的String类型数据,值为zhangsan
get name # 获取名为name的String类型数据
mset age 18 addr henan # 批量添加String类型数据
mget age addr # 批量获取String类型数据,获取名为age和addr的数据
hset userInfo name list # 添加一个名为UserInfo的hash类型,其中一个名是name,值为list
hget userInfo name # 获取名为name的数据
hmset userInfo age 18 addr henan # 添加多条数据
hmget userInfo age addr # 获取多个数据
hgetall userInfo # 获取全部数据
hdel userInfo addr # 删除指定数据
这里的List类型比较特殊,分为左右两种,一种是从左边添加,一种是从右边添加。左边添加就类似于一种压栈效果,先进的排到后面,右边添加就是常规的追加。
lpush students zhangsan lisi # 左边添加
lrange students 0 2 # 遍历list,0起始索引,2结束索引
可以看到zhangsan排在了后面,而lisi排在前面,类似于一个压栈的效果
rpush students wangwu zhaoliu # 右边添加
lrange students 0 8 # 遍历,结束索引可以是一个比list长度大的数字
可以看到,右边添加wangwu在zhaoliu前边,就是一个追加的效果
llen students #查看长度
lrem students 1 lisi # 删除指定数量的指定元素
lpop students # 移除左边开始的第一个元素
rpop students # 移除右边开始的第一个元素
这里的set类型和java的一样,无序,不重复
sadd letters aaa bbb ccc ddd # 添加set类型数据
smembers letters # 查看set类型数据
scard letters # 获取set类型数据条数
srem letters aaa ccc # 删除set类型里的指定元素
一个有序的Set类型
zadd score 5 zhangsan 6 lisi 3 wangwu 9 zhaoliuclear # 添加参数,数值代表等级,数值越低,排名越靠前
zrange score 0 8 # 遍历数据
zcard score # 数据元素条数
zrem score lisi # 删除指定元素
mset user:01 ahh # 以层级关系存储数据,:分割层级
mget user:01 # 以层级关系查找数据,:分割层级
有几种不同的命令可以设置失效时间
方式一:
set code test ex 10 # 10秒后失效
方式二:
set code test # 添加数据
expire code 10 # 设置10秒后失效
方式三:
set code test nx ex 10 # code存在时才设置失效时间
xx
,存在时设置失效时间set code test # 添加数据
del code # 删除数据
Redis是内存中的数据结构储存系统,不是直接存储在磁盘上的,所有就有可能会在机器宕机的时候无法有效的储存数据,所以就需要持久化储存。
Redis有三种方式可以用于持久化储存数据
使用bgsave手动持久化储存数据
set test test
bgsave
这种方式后进行持久化操作很方便,但是很麻烦,需要重复多个的执行bgsave
命令
Redis还自带了一种save的持久化储存方式,这是一种自动化的储存方式(默认开启)。可以在redis.conf文件中看到(大概220行左右),这种方式会在指定时间内,进行自动保存
默认配置如图,表示在
900秒内,有一个key被改动,则在900秒后被自动持久化保存
300秒内,有10个key被改动,则在300秒后被自动持久化保存
60秒内,有10000个key被改动,则在60秒后被自动持久化保存
但是这种情况也不是很完善,比如在60秒内有10000的数据被改动,但是在58秒的时候机器断电了,不到低60秒,它也不会自动持久化储存。
默认情况就该配置是关闭的,需要手动开启,设置on为yes(redis.conf文件的大概700行左右),该配置开启后会自动关闭上面的save持久化方式。该方式的特点就是会保存写过的所有的命令,会把所有的命令保存到一个appendonly.aof文件中,每次启动redis都会先执行该文件,以达到持久化储存的效果。
但是这种方式也有弊端,那就是会保存很多没用的代码,后续文件会越来越大,影响redis的运行效果。
1、修改Test依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
2、修改Redis依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
<exclusions>
<exclusion>
<groupId>io.lettucegroupId>
<artifactId>lettuce-coreartifactId>
exclusion>
exclusions>
dependency>
3、添加Jedis依赖
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
dependency>
完整文件
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.7.RELEASEversion>
<relativePath/>
parent>
<groupId>cn.yanghuisengroupId>
<artifactId>redisdemoartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>redisdemoname>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
<exclusions>
<exclusion>
<groupId>io.lettucegroupId>
<artifactId>lettuce-coreartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
application.yml
spring:
redis:
# Redis服务器地址
host: 192.168.10.100
# Redis服务器端口
port: 6379
# Redis服务器认证密码
password: root
# 要连接的数据库,默认就是0
database: 0
# 连接超时时间
timeout: 10000ms
jedis:
pool:
# 最大连接数,默认是8
max-active: 1024
# 最大连接组设时间,单位毫秒,默认-1ms
max-wait: 10000ms
# 最大空闲连接,默认8
max-idle: 200
# 最小连接,默认0
min-idle: 5
package cn.yanghuisen.redisdemo.config;
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 admin
* @version 1.0
* @date 2020/5/13 19:22
* @Description Jedis配置类
*/
@Configuration // 配置类
public class JedisConfig {
// 服务器地址
@Value("${spring.redis.host}")
private String host;
// 服务器端口
@Value("${spring.redis.port}")
private Integer port;
// 认证密码
@Value("${spring.redis.password}")
private String password;
// 连接超时时间
@Value("${spring.redis.timeout}")
private String timeout;
// 最大链接数
@Value("${spring.redis.jedis.pool.max-active}")
private Integer maxActive;
// 最大链接数
@Value("${spring.redis.jedis.pool.max-wait}")
private String maxWait;
// 最大空闲连接
@Value("${spring.redis.jedis.pool.max-idle}")
private Integer maxIdle;
// 最小空闲连接
@Value("${spring.redis.jedis.pool.min-idle}")
private Integer minIdle;
@Bean
public JedisPool redisPoolFactory(){
// 连接池配置类
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// 设置最大连接阻塞时间
jedisPoolConfig.setMaxWaitMillis(Long.parseLong(maxWait.substring(0,maxWait.length()-2)));
// 最大链接数
jedisPoolConfig.setMaxTotal(maxActive);
// 最大空闲连接
jedisPoolConfig.setMaxIdle(maxIdle);
// 最小空闲连接
jedisPoolConfig.setMinIdle(minIdle);
// 连接池,连接池配置类,服务器地址,端口,连接超时时间。认证密码
return new JedisPool(jedisPoolConfig,host,port,Integer.parseInt(timeout.substring(0,timeout.length()-2)),password);
}
}
@Resource
private JedisPool jedisPool;
private Jedis jedis = null;
@BeforeEach
public void initConn(){
jedis = jedisPool.getResource();
}
@AfterEach
public void closeCinn(){
if (null!=jedis){
jedis.close();
}
}
/**
* 操作String
*/
@Test
void testString(){
// 添加单条数据
jedis.set("ahh","ahh");
jedis.set("age","18");
// 添加多条数据,参数:奇数-key,偶数-value
jedis.mset("addr","henan","sex","男");
// 获取一条数据
System.out.println(jedis.get("ahh"));
// 获取多条数据
jedis.mget("age","addr","sex").forEach(System.out::println);
// 删除
jedis.del("sex");
}
/**
* 操作Hash
*/
@Test
public void testHash(){
// 添加一条数据
jedis.hset("userInfo","name","ahh");
// 添加多条数据
Map<String,String> map = new HashMap<>();
map.put("age","20");
map.put("sex","男");
jedis.hmset("userInfo",map);
// 获取一条数据
System.out.println(jedis.hget("userInfo","name"));
// 获取多条数据
jedis.hmget("userInfo","age","sex").forEach(System.out::println);
// 获取Hash类型多有的数据
jedis.hgetAll("userInfo").forEach((k, v) -> {
System.out.println(k+"----"+v);
});
// 删除
jedis.hdel("userInfo","sex");
}
/**
* 操作List
*/
@Test
void testList(){
// 左添加
jedis.lpush("aabc","a","b","c");
// 右添加
jedis.rpush("aabc","1","2","3");
// 获取数据
jedis.lrange("aabc",0,10).forEach(System.out::println);
// 获取总条数
System.out.println("总条数:"+jedis.llen("aabc"));
// 删除list元素
jedis.lrem("aabc",1,"c");
// 左移除
jedis.lpop("aabc");
// 右移除
jedis.rpop("aabc");
// 删除
jedis.del("aabc");
}
/**
* 操作Set
*/
@Test
void testSet(){
// 添加数据
jedis.sadd("letters","aa","bb","cc","dd");
// 获取数据
jedis.smembers("letters").forEach(System.out::println);
// 获取条数
System.out.println("总条数:"+jedis.scard("letters"));
// 删除
jedis.srem("letters","dd");
}
/**
* 操作SortedSet
*/
@Test
void testSortedSet(){
// 添加数据
Map<String,Double> map = new HashMap<>();
map.put("张三",5d);
map.put("李四",2d);
map.put("王五",8d);
map.put("赵六",6d);
jedis.zadd("students",map);
// 获取数据
jedis.zrange("students",0,8).forEach(System.out::println);
// 总条数
System.out.println("总条数:"+jedis.zcard("students"));
// 删除
jedis.zrem("students","赵六");
}
/**
* 层级关系|目录形式存储数据
*/
@Test
public void testDir(){
jedis.set("users:user:ahh","ahh");
jedis.set("users:user:zhangsan","zhangsan");
System.out.println(jedis.get("users:user:ahh"));
System.out.println(jedis.get("users:user:zhangsan"));
}
/**
* 设置失效时间
*/
@Test
public void testExpire(){
// 方法1
jedis.set("code","test");
jedis.expire("code",10); // 10秒
jedis.pexpire("code",10000L); // 10000毫秒
System.out.println(jedis.ttl("code")); // 获取剩余秒数据
// 方法2
jedis.setex("code",10,"test"); // 10秒
jedis.psetex("code",10000L,"test"); // 10000毫秒
System.out.println(jedis.pttl("code")); // 获取剩余毫秒
// 方法3
SetParams setParams = new SetParams();
// 不存在时
// setParams.nx();
// 存在时
setParams.xx();
// 设置失效时间-秒
// setParams.ex(10);
// 设置失效时间-毫秒
setParams.px(10000L);
jedis.set("code","test",setParams);
}
/**
* 获取所有的key
*/
@Test
void testKeyAll(){
// 获取key的数量
System.out.println(jedis.dbSize());
// 获取所有的key
jedis.keys("*").forEach(System.out::println);
}
/**
* 删除
*/
@Test
void testDel(){
jedis.del("userInfo");
}
/**
* 事务
*/
@Test
void testMulti(){
Transaction tx = jedis.multi();
// 开启事务
tx.set("tel","1010");
// 提交事务
tx.exec();
// 事务回滚
// tx.discard();
}
序列化和反序列化工具
package cn.yanghuisen.redisdemo.utils;
import java.io.*;
/**
* @author admin
* @version 1.0
* @date 2020/5/13 20:40
* @Description 序列化和反序列化工具
*/
public class SerializeUtil {
// 序列化
public static byte[] serialize(Object obj){
// 对象输出流
ObjectOutputStream oos = null;
// 字节数组输出流
ByteArrayOutputStream baos = null;
try {
// 序列化
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
// 反序列化
public static Object unserialize(byte[] bytes){
if (null==bytes){
return null;
}
ByteArrayInputStream bais = null;
try {
// 反序列化
bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
用户类
package cn.yanghuisen.redisdemo.pojo;
import java.io.Serializable;
/**
* @author admin
* @version 1.0
* @date 2020/5/13 20:51
* @Description TODO
*/
public class User implements Serializable {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
数据储存和获取
/**
* byte序列化
*/
@Test
void testByte(){
User user = new User();
user.setId(1);
user.setName("ahh");
// 序列化
byte[] userkey = SerializeUtil.serialize("user:"+user.getId());
byte[] uservalue = SerializeUtil.serialize(user);
jedis.set(userkey,uservalue);
// 获取数据
byte[] values = jedis.get(userkey);
// 反序列化
System.out.println(SerializeUtil.unserialize(values));
}
单机版的Redis可能会出现一个问题就是,一旦服务器宕机就无法使用,所以Redis有一个主从复用的概念,就是说有一个主服务器和从服务器,主服务器提供写入和读取,从服务器提供取的功能,一旦其中一个从服务器出现了问题,还有其它的从服务区提供使用,一旦主服务器出现了问题,可以把其中的一个从服务器变为主服务器。
把Redis分为主从关系,一个主服务器,和两个从服务器。
主服务器提供写入和获取,从服务器只提供获取功能。
1、创建三个目录
2、复制redis.conf配置文件到创建的配置文件目录下
3、配置配置文件,作为公共配置文件
注释掉bind,运行外界连接
关闭保护模式,否则外界无法连接
注释公共配置端口,后期需要运行三个redis,每个的端口不一样,需要针对每个redis进行私有的配置
修改为后台启动
注释进程编号,三个redis的进程编号都是不一样的,需要针对的进行私有配置
注释公共配置日志文件,三个redis有三个日志文件,私有配置
注释公共配置数据文件,修改数据文件路径为创建的数据文件路径
添加从数据库访问主服务器认证密码
设置访问认证,三个redis的认证密码都是一个,设置为公用的
注释公共配置追加文件,这个根据需求选择是否使用,这里关闭
设置从服务器只能读取,不能写入
保存退出
1、创建三个配置文件
redis-xxxx:xxxx表示的每个的端口,为了方便区分
2、修改每个的配置文件
redis-6379.conf
# 引用公共配置文件
include /opt/redis/conf/redis-common.conf
# 进程编号记录文件
pidfile /var/run/redis-6379.pid
# 进程端口号
port 6379
# 日志记录文件
logfile "/opt/redis/log/redis-6379.log"
# 数据记录文件
dbfilename dump-6379.rdb
# 追加文件名称
appendfilename "appendonly-6379.aof"
redis-6380.conf
# 引用公共配置文件
include /opt/redis/conf/redis-common.conf
# 进程编号记录文件
pidfile /var/run/redis-6380.pid
# 进程端口号
port 6380
# 日志记录文件
logfile "/opt/redis/log/redis-6380.log"
# 数据记录文件
dbfilename dump-6380.rdb
# 追加文件名称
appendfilename "appendonly-6380.aof"
# 设置主服务器的IP地址
slaveof 192.168.10.100 6379
redis-6381.conf
# 引用公共配置文件
include /opt/redis/conf/redis-common.conf
# 进程编号记录文件
pidfile /var/run/redis-6381.pid
# 进程端口号
port 6381
# 日志记录文件
logfile "/opt/redis/log/redis-6381.log"
# 数据记录文件
dbfilename dump-6381.rdb
# 追加文件名称
appendfilename "appendonly-6381.aof"
# 设置主服务器的IP地址
slaveof 192.168.10.100 6379
1、运行三个redis进程
2、查看每个服务器的主从状态
主服务器
从服务器1
从服务器2
3、测试主服务下添加数据,在从服务器下查询是否正常
4、从服务器不能写入测试
上面已经进行了读写分离,完成了主从服务器,但是这时还有一个问题,如果主服务器出现了问题,那么就不能再进行写入操作,只能使用从服务器获取主服务宕机之前的数据,为了解决这一问题,就引入了一个叫做哨兵的方案,通过哨兵,可以检测到主服务是否宕机,如果主服务器宕机了,就会在满足一定的要求后选举一个从服务器变为主服务,如果之前宕机的主服务恢复了,则被宕机的服务变为从服务。以此来解决,主服务宕机后,无法在提供写入操作的问题。
cp /root/redis-5.0.3/sentinel.conf /opt/redis/conf/
注释哨兵监听进程端口号
指示哨兵监听一个主服务器,主服务器地址为192.168.10.100,端口为6379,2表示判断失败的要求,配置三个哨兵,一半(3个的一半为1.5,取整为2)以上都监听主服务状态为不达标就更换主服务器
设置密码
设置哨兵认为服务器断线所需的毫秒数。
默认是30000毫秒,也就是30秒,为了测试改为10000毫秒
设置主服务和从服务器切换时间
默认是3分钟,如果三分钟内没有切换成功,则本次切换失败
关闭哨兵的保护模式
修改哨兵为后台启动
私有哨兵配置1
# 引用公共配置
include /opt/redis/conf/sentinel.conf
# 进程端口号
port 26379
# 进程编号近路文件
pidfile /var/run/sentinel-26379.pid
# 日志文件
logfile "/opt/redis/log/sentinel-26379.log"
私有哨兵配置2
# 引用公共配置
include /opt/redis/conf/sentinel.conf
# 进程端口号
port 26380
# 进程编号近路文件
pidfile /var/run/sentinel-26380.pid
# 日志文件
logfile "/opt/redis/log/sentinel-26380.log"
私有哨兵配置2
# 引用公共配置
include /opt/redis/conf/sentinel.conf
# 进程端口号
port 26381
# 进程编号近路文件
pidfile /var/run/sentinel-26381.pid
# 日志文件
logfile "/opt/redis/log/sentinel-26381.log
1、启动三个哨兵
2、查看主服务器和从服务状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RG5Gs2Gg-1589464617789)(http://images.yanghuisen.cn/FitC_MDwSArK5ra8zhbOFGQStflB)]
3、杀死主服务器,模拟主服务宕机
杀死主服务器后等待10秒,让哨兵选择新的主服务器,并重新启动被杀死的服务器
4、查看哨兵选重新选举后的服务器状态
SpringBoot1.X版本默认使用的是jedis作为Redsi的Java客户端,而2.X版本使用的是lettuce作为Redis的Java客户端。
两者的区别为:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
spring:
redis:
# Redis服务器地址
host: 192.168.10.100
# Redis服务器端口
port: 6379
# Redis服务器认证密码
password: root
# Redis数据库
database: 0
# 连接超时时间
timeout: 10000ms
lettuce:
pool:
# 最大连接数,默认8
max-active: 1024
# 最大阻塞等待时间,单位毫秒,默认-1ms
max-wait: 10000ms
# 最大空闲连接,默认8
max-idle: 200
# 最小空闲连接,默认0
min-idle: 5
@SpringBootTest
class SpringDataRedisDemoApplicationTests {
@Resource
private RedisTemplate redisTemplate; // 默认模板
@Resource
private StringRedisTemplate stringRedisTemplate;
@Test
void initConn() {
ValueOperations<String,String> ops = stringRedisTemplate.opsForValue();
ops.set("userName","ahh");
ValueOperations<String,String> value = redisTemplate.opsForValue();
value.set("name","zhangsan");
System.out.println(ops.get("name"));
}
}
上面的测试程序,虽然成功的把数据写入了,但是写入的是一个二进制字节码,需要处理一下。
RedisTemplate
使用的是JdkSerializationRedisSerializer
进行序列化,会序列化为二进制字节码储存。这时需要自定义模板解决。当自定义模板后,又想储存String字符串可以使用StringRedisTemplate
。自定义的模板和StringRedisTemplate
不冲突。
序列化的选择:
JdkSerializationRedisSerializer
:该序列化为RedisTemplate
的默认序列化工具,是JDK提供的,有点是反序列化时不需要提供类型信息(class),但是缺点就是序列化后的结果非常大,而且看不懂,时json格式的5倍左右,会消耗redis服务器的大量内存。Jackson2JsonRedisSerializer
:使用的时Jackson
库将对象序列化为json字符串,优点是速度快,序列化后的字符串短小精悍,但是缺点也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息,通过查看源代码,发现其只再反序列化过程中用到了类型信息。GenericJackson2JsonRedisSerializer
:通用型序列化,这种序列化方式不用自己手动指定对象的Class。自定义模板
package cn.yanghuisen.springdataredisdemo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author admin
* @version 1.0
* @date 2020/5/14 20:52
* @Description 自定义模板
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory){
// 创建模板
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
// 设置String类型的Key的序列器
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 设置String类型的value的序列器
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
// 设置Hash类型的Key的序列器
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// 设置hash类型的value的序列器
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
// 设置连接方式
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
return redisTemplate;
}
}
序列化测试
@Test
void testSerial(){
User user = new User();
user.setId(1);
user.setName("ahh");
ValueOperations ops = redisTemplate.opsForValue();
ops.set("user",user);
System.out.println(ops.get("user"));
}
Ok
@Test
void testString(){
ValueOperations vos = redisTemplate.opsForValue();
// 添加一条数据
vos.set("userName","zhangsan");
// 以层级关系|目录形式存储数据
vos.set("user:01","ahh");
// 添加多条数据
Map<String,String> userMap = new HashMap<>();
userMap.put("age","18");
userMap.put("sex","男");
vos.multiSet(userMap);
// 获取一条数据
System.out.println(vos.get("userName"));
// 获取多条数据
List<String> keys = Arrays.asList("userName","age","sex");
vos.multiGet(keys).forEach(System.out::println);
}
@Test
void testHash(){
HashOperations<String,String,String> hos = redisTemplate.opsForHash();
// 添加一条数据
hos.put("userInfo","name","zhangsan");
// 添加多条数据
Map<String,String> map = new HashMap<>();
map.put("age","20");
map.put("sex","男");
hos.putAll("userInfo",map);
// 获取一条数据
System.out.println(hos.get("userInfo","name"));
// 获取多条数据
List<String> keys = Arrays.asList("age","sex");
hos.multiGet("userInfo",keys).forEach(System.out::println);
// 获取Hash类型的所有的数据
hos.entries("userInfo").forEach((k,v)->{
System.out.println(k+"---"+v);
});
// 删除数据
hos.delete("userInfo","name");
}
@Test
void testList(){
ListOperations los = redisTemplate.opsForList();
// 左添加
los.leftPush("students","张三");
los.leftPush("students","李四");
// 指定位置前面左添加
los.leftPush("students","李四","王五");
// 右添加
los.rightPush("students","赵六");
// 获取
los.range("students",1,10).forEach(System.out::println);
// 根据下标获取
System.out.println(los.index("students",1));
// 获取总条数
System.out.println(los.size("students"));
// 删除单条数据
los.remove("students",1,"张三");
}
@Test
void testSer(){
SetOperations sos = redisTemplate.opsForSet();
// 添加数据
sos.add("letters",new String[]{"aaa","bbb","ccc","ddd"});
sos.members("letters").forEach(System.out::println);
sos.remove("letters","aaa","bbb");
}
@Test
void testSoredSet(){
ZSetOperations zsos = redisTemplate.opsForZSet();
// 添加数据
zsos.add("score","zhangsan",5D);
zsos.add("score","lisi",2D);
zsos.add("score","wangwu",9D);
zsos.add("score","zhaoliu",3D);
// 获取
zsos.range("score",0,5).forEach(System.out::println);
// 获取总条数
System.out.println(zsos.size("score"));
// 删除
zsos.remove("score","wangwu","zhangsan");
}
@Test
void testAllKeys(){
redisTemplate.keys("*").forEach(System.out::println);
}
@Test
void testDelete(){
// 删除
redisTemplate.delete("score");
}
@Test
void testEx(){
ValueOperations vos = redisTemplate.opsForValue();
// 添加一条数据,并设置失效时间
vos.set("code","abcd",180, TimeUnit.SECONDS);
// 给已经存在的key设置失效时间
redisTemplate.expire("code",180,TimeUnit.SECONDS);
// 获取指定key的失效时间
System.out.println(redisTemplate.getExpire("code"));
}
配置文件
spring:
redis:
# Redis服务器地址
host: 192.168.10.100
# Redis服务器端口
port: 6380
# Redis服务器认证密码
password: root
# Redis数据库
database: 0
# 连接超时时间
timeout: 10000ms
lettuce:
pool:
# 最大连接数,默认8
max-active: 1024
# 最大阻塞等待时间,单位毫秒,默认-1ms
max-wait: 10000ms
# 最大空闲连接,默认8
max-idle: 200
# 最小空闲连接,默认0
min-idle: 5
# 哨兵模式
sentinel:
# 主节点
master: mymaster
nodes: 192.168.10.100:26379,192.168.10.100:26380,192.168.10.100:26381
配置类
@Bean
RedisSentinelConfiguration redisSentinelConfiguration(){
RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration().
// 主节点名称
master("mymaster")
// 服务器地址
.sentinel("192.168.10.100",26379)
.sentinel("192.168.10.100",26380)
.sentinel("192.168.10.100",26381);
// 设置密码
sentinelConfiguration.setPassword("root");
return sentinelConfiguration;
}