0、Redis
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支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
Redis支持数据的备份,即master-slave模式的数据备份。
Redis的优势:
性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
官网:https://redis.io/
中文官网:http://www.redis.cn/
1、Linux下的Redis的安装 1.1、下载 下载地址:https://redis.io/download
或者
1 wget http://download.redis.io/releases/redis-5.0.3.tar.gz
1.2、上传文件到服务器 如果是直接使用wget下载的就不用再上传
1.3、解压文件 1 tar -zxvf redis-5.0.3.tar.gz
解压后目录
1.4、安装依赖
redis 是基于C++开发的,所有要安装C++的环境
1 yum -y install gcc-c++ autoconf automake
1.5、预编译 1 2 cd redis-5.0.3 # 进入redis目录 make # 预编译
1.6、安装 创建一个安装目录,不然会安装到一个默认路径,后期路径不好找
1 mkdir -p /usr/local/redis
安装到指定路径
1 make PREFIX=/usr/local/redis/ install
Redis-cli :客户端
Redis-server :服务器端
1.7、前台启动
默认就是前台启动,但是前台启动有一个不好的地方,就是Redis会一致占用前台输入,无法进行别的从操作。这是为了方便就需要把Redis设置为后台启动,让它在后台运行,不耽误前台进行别的操作。
1.8、后台启动 1、ctrl+c,结束前台程序运行
2、复制解压的redis目录下的redis.conf文件到安装的路径
1 cp /root/redis-5.0.3/redis.conf /usr/local/redis/bin
3、修改bin目录下的redis.conf文件
修改daemonize on
为 daemonize yes
4、再次运行查看
再次启动程序,需要戴胜redis.conf文件,否则还是前台运行
1 2 ./redis-server redis.conf # 启动程序 ps -ef |grep redis # 查看和redis相关的进程
5、后台进程关闭
前台启动的话只需要ctrl+c就可以关闭,但是后台启动相比就麻烦了点,需要直接关闭进程
5977:进程号
2、windows客户端方法Redis 2.1、安装客户端软件 下载地址:https://lanzous.com/ickc0yd
常规安装,选择安装路径,下一步就可以了
2.2、配置服务器端Redis程序
默认情况下,Redis是不允许别的客户端连接访问的,只能是本机(127.0.0.1)访问,需要设置一下,否则windows客户端连接不上redis
1、注释掉bind 127.0.0.1
如果注释掉bind就表示可以是任何ip访问,如果是想要指定的IP访问,直接在127.0.0.1 后添加指定IP就可以
2、关闭保护模式,设置protected-mode为no,否则依然无法连接
3、设置访问认证(密码)
默认状态下是没有开启认证的,需要手动取消#
注释(该命令大概在500行左右)
4、保存退出,结束进程,重新启动redis
1 ./redis-server redis.conf
2.3、windows客户端连接redis
3、Redis-cli连接操作Redis
Redis-cli就是Redis自带的一个客户端程序,可以用来操作Redis,在安装的路径里
3.1、redis-clid连接redis 1 ./redis-cli -p 6379 -a root
6379:redis的默认端口
root:认证密码
3.2、操作redis 3.2.1、String类型 1 2 3 4 set name zhangsan # 添加一个名为name的String类型数据,值为zhangsan get name # 获取名为name的String类型数据 mset age 18 addr henan # 批量添加String类型数据 mget age addr # 批量获取String类型数据,获取名为age和addr的数据
3.2.2、Hash类型 1 2 3 4 5 6 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 # 删除指定数据
3.2.3、List类型
这里的List类型比较特殊,分为左右两种,一种是从左边添加,一种是从右边添加。左边添加就类似于一种压栈效果,先进的排到后面,右边添加就是常规的追加。
1 2 lpush students zhangsan lisi # 左边添加 lrange students 0 2 # 遍历list,0起始索引,2结束索引
可以看到zhangsan排在了后面,而lisi排在前面,类似于一个压栈的效果
1 2 rpush students wangwu zhaoliu # 右边添加 lrange students 0 8 # 遍历,结束索引可以是一个比list长度大的数字
可以看到,右边添加wangwu在zhaoliu前边,就是一个追加的效果
1 2 llen students #查看长度 lrem students 1 lisi # 删除指定数量的指定元素
lrem students 1 lisi:删除students中的1个lisi
1 2 lpop students # 移除左边开始的第一个元素 rpop students # 移除右边开始的第一个元素
3.2.4、Set类型
这里的set类型和java的一样,无序,不重复
1 2 3 4 sadd letters aaa bbb ccc ddd # 添加set类型数据 smembers letters # 查看set类型数据 scard letters # 获取set类型数据条数 srem letters aaa ccc # 删除set类型里的指定元素
3.2.5、Sorted Set类型
一个有序的Set类型
1 2 3 4 zadd score 5 zhangsan 6 lisi 3 wangwu 9 zhaoliuclear # 添加参数,数值代表等级,数值越低,排名越靠前 zrange score 0 8 # 遍历数据 zcard score # 数据元素条数 zrem score lisi # 删除指定元素
3.2.6、层级关系|目录结构存储数据 1 2 mset user:01 ahh # 以层级关系存储数据,:分割层级 mget user:01 # 以层级关系查找数据,:分割层级
3.2.7、设置Key的失效时间 有几种不同的命令可以设置失效时间
方式一:
1 set code test ex 10 # 10秒后失效
方式二:
1 2 set code test # 添加数据 expire code 10 # 设置10秒后失效
expire:秒
pexpire:毫秒
expireat:秒的时间戳
pexpireat:毫秒的时间戳
方式三:
1 set code test nx ex 10 # code存在时才设置失效时间
code:key
test:value
nx:不存在时设置时间,还有一个可选值xx
,存在时设置失效时间
ex:秒[px:毫秒]
3.2.8、删除 1 2 set code test # 添加数据 del code # 删除数据
4、Redis持久化储存
Redis是内存中的数据结构储存系统,不是直接存储在磁盘上的,所有就有可能会在机器宕机的时候无法有效的储存数据,所以就需要持久化储存。
Redis有三种方式可以用于持久化储存数据
4.1、bgsave 使用bgsave手动持久化储存数据
这种方式后进行持久化操作很方便,但是很麻烦,需要重复多个的执行bgsave
命令
4.2、save配置 Redis还自带了一种save的持久化储存方式,这是一种自动化的储存方式(默认开启)。可以在redis.conf文件中看到(大概220行左右),这种方式会在指定时间内,进行自动保存
默认配置如图,表示在
900秒内,有一个key被改动,则在900秒后被自动持久化保存
300秒内,有10个key被改动,则在300秒后被自动持久化保存
60秒内,有10000个key被改动,则在60秒后被自动持久化保存
但是这种情况也不是很完善,比如在60秒内有10000的数据被改动,但是在58秒的时候机器断电了,不到低60秒,它也不会自动持久化储存。
4.3、appendonly 默认情况就该配置是关闭的,需要手动开启,设置on为yes(redis.conf文件的大概700行左右),该配置开启后会自动关闭上面的save持久化方式。该方式的特点就是会保存写过的所有的命令,会把所有的命令保存到一个appendonly.aof文件中,每次启动redis都会先执行该文件,以达到持久化储存的效果。
但是这种方式也有弊端,那就是会保存很多没用的代码,后续文件会越来越大,影响redis的运行效果。
5、Java操作Redis 5.1、创建SpringBoot项目
5.2、修改POM配置文件 1、修改Test依赖
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency >
2、修改Redis依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > <exclusions > <exclusion > <groupId > io.lettuce</groupId > <artifactId > lettuce-core</artifactId > </exclusion > </exclusions > </dependency >
3、添加Jedis依赖
1 2 3 4 <dependency > <groupId > redis.clients</groupId > <artifactId > jedis</artifactId > </dependency >
完整文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 <?xml version="1.0" encoding="UTF-8"?> <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.0</modelVersion > <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.2.7.RELEASE</version > <relativePath /> </parent > <groupId > cn.yanghuisen</groupId > <artifactId > redisdemo</artifactId > <version > 0.0.1-SNAPSHOT</version > <name > redisdemo</name > <description > Demo project for Spring Boot</description > <properties > <java.version > 1.8</java.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > <exclusions > <exclusion > <groupId > io.lettuce</groupId > <artifactId > lettuce-core</artifactId > </exclusion > </exclusions > </dependency > <dependency > <groupId > redis.clients</groupId > <artifactId > jedis</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > </project >
5.3、Jedis的的配置文件 application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 spring: redis: host: 192.168 .10 .100 port: 6379 password: root database: 0 timeout: 10000ms jedis: pool: max-active: 1024 max-wait: 10000ms max-idle: 200 min-idle: 5
5.4、创建连接池配置类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 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;@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); } }
5.5、注入连接池,切入点拦截 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Resource private JedisPool jedisPool;private Jedis jedis = null ;@BeforeEach public void initConn () { jedis = jedisPool.getResource(); } @AfterEach public void closeCinn () { if (null !=jedis){ jedis.close(); } }
5.5、操作String 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Test void testString () { jedis.set("ahh" ,"ahh" ); jedis.set("age" ,"18" ); jedis.mset("addr" ,"henan" ,"sex" ,"男" ); System.out.println(jedis.get("ahh" )); jedis.mget("age" ,"addr" ,"sex" ).forEach(System.out::println); jedis.del("sex" ); }
5.6、操作Hash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @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); jedis.hgetAll("userInfo" ).forEach((k, v) -> { System.out.println(k+"----" +v); }); jedis.hdel("userInfo" ,"sex" ); }
5.7、操作List 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @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" )); jedis.lrem("aabc" ,1 ,"c" ); jedis.lpop("aabc" ); jedis.rpop("aabc" ); jedis.del("aabc" ); }
5.8、操作Set 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @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" ); }
5.9、操作Sorted Set 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @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" ,"赵六" ); }
5.10、层级关系|目录形式存储数据 1 2 3 4 5 6 7 8 9 10 @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" )); }
5.11、设置失效时间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Test public void testExpire () { jedis.set("code" ,"test" ); jedis.expire("code" ,10 ); jedis.pexpire("code" ,10000L ); System.out.println(jedis.ttl("code" )); jedis.setex("code" ,10 ,"test" ); jedis.psetex("code" ,10000L ,"test" ); System.out.println(jedis.pttl("code" )); SetParams setParams = new SetParams(); setParams.xx(); setParams.px(10000L ); jedis.set("code" ,"test" ,setParams); }
5.12、获取所有的key 1 2 3 4 5 6 7 8 9 10 @Test void testKeyAll () { System.out.println(jedis.dbSize()); jedis.keys("*" ).forEach(System.out::println); }
5.13、删除 1 2 3 4 5 6 7 @Test void testDel () { jedis.del("userInfo" ); }
5.14、事务 1 2 3 4 5 6 7 8 9 10 11 12 13 @Test void testMulti () { Transaction tx = jedis.multi(); tx.set("tel" ,"1010" ); tx.exec(); }
5.15、byte序列化 序列化和反序列化工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package cn.yanghuisen.redisdemo.utils;import java.io.*;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 ; } }
用户类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package cn.yanghuisen.redisdemo.pojo;import java.io.Serializable;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 + '\'' + '}' ; } }
数据储存和获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @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)); }
6、Redis搭建主从复用 单机版的Redis可能会出现一个问题就是,一旦服务器宕机就无法使用,所以Redis有一个主从复用的概念,就是说有一个主服务器和从服务器,主服务器提供写入和读取,从服务器提供取的功能,一旦其中一个从服务器出现了问题,还有其它的从服务区提供使用,一旦主服务器出现了问题,可以把其中的一个从服务器变为主服务器。
6.1、读写分离 把Redis分为主从关系,一个主服务器,和两个从服务器。
主服务器提供写入和获取,从服务器只提供获取功能。
1、创建三个目录
data:数据文件
log:日志文件
conf:配置文件
2、复制redis.conf配置文件到创建的配置文件目录下
3、配置配置文件,作为公共配置文件
注释掉bind,运行外界连接
关闭保护模式,否则外界无法连接
注释公共配置端口,后期需要运行三个redis,每个的端口不一样,需要针对每个redis进行私有的配置
修改为后台启动
注释进程编号,三个redis的进程编号都是不一样的,需要针对的进行私有配置
注释公共配置日志文件,三个redis有三个日志文件,私有配置
注释公共配置数据文件,修改数据文件路径为创建的数据文件路径
添加从数据库访问主服务器认证密码
设置访问认证,三个redis的认证密码都是一个,设置为公用的
注释公共配置追加文件,这个根据需求选择是否使用,这里关闭
设置从服务器只能读取,不能写入
保存退出
6.2、创建配置三个私有配置文件 1、创建三个配置文件
redis-xxxx :xxxx表示的每个的端口,为了方便区分
2、修改每个的配置文件
redis-6379.conf
1 2 3 4 5 6 7 8 9 10 11 12 # 引用公共配置文件 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 引用公共配置文件 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 引用公共配置文件 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
6.3、运行三个redis进程,并测试主从 1、运行三个redis进程
2、查看每个服务器的主从状态
主服务器
从服务器1
从服务器2
3、测试主服务下添加数据,在从服务器下查询是否正常
4、从服务器不能写入测试
6.4、主备切换 上面已经进行了读写分离,完成了主从服务器,但是这时还有一个问题,如果主服务器出现了问题,那么就不能再进行写入操作,只能使用从服务器获取主服务宕机之前的数据,为了解决这一问题,就引入了一个叫做哨兵的方案,通过哨兵,可以检测到主服务是否宕机,如果主服务器宕机了,就会在满足一定的要求后选举一个从服务器变为主服务,如果之前宕机的主服务恢复了,则被宕机的服务变为从服务。以此来解决,主服务宕机后,无法在提供写入操作的问题。
6.4.1、复制哨兵配置文件到创建的配置文件目录下 1 cp /root/redis-5.0.3/sentinel.conf /opt/redis/conf/
6.4.2、修改哨兵配置文件 注释哨兵监听进程端口号
指示哨兵监听一个主服务器,主服务器地址为192.168.10.100,端口为6379,2表示判断失败的要求,配置三个哨兵,一半(3个的一半为1.5,取整为2)以上都监听主服务状态为不达标就更换主服务器
设置密码
设置哨兵认为服务器断线所需的毫秒数。
默认是30000毫秒,也就是30秒,为了测试改为10000毫秒
设置主服务和从服务器切换时间
默认是3分钟,如果三分钟内没有切换成功,则本次切换失败
关闭哨兵的保护模式
修改哨兵为后台启动
6.4.3、添加三个私有哨兵配置文件
私有哨兵配置1
1 2 3 4 5 6 7 8 # 引用公共配置 include /opt/redis/conf/sentinel.conf # 进程端口号 port 26379 # 进程编号近路文件 pidfile /var/run/sentinel-26379.pid # 日志文件 logfile "/opt/redis/log/sentinel-26379.log"
私有哨兵配置2
1 2 3 4 5 6 7 8 # 引用公共配置 include /opt/redis/conf/sentinel.conf # 进程端口号 port 26380 # 进程编号近路文件 pidfile /var/run/sentinel-26380.pid # 日志文件 logfile "/opt/redis/log/sentinel-26380.log"
私有哨兵配置2
1 2 3 4 5 6 7 8 # 引用公共配置 include /opt/redis/conf/sentinel.conf # 进程端口号 port 26381 # 进程编号近路文件 pidfile /var/run/sentinel-26381.pid # 日志文件 logfile "/opt/redis/log/sentinel-26381.log
6.4.4、哨兵启动测试 1、启动三个哨兵
2、查看主服务器和从服务状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RG5Gs2Gg-1589464617789)(http://images.yanghuisen.cn/FitC_MDwSArK5ra8zhbOFGQStflB)]
3、杀死主服务器,模拟主服务宕机
杀死主服务器后等待10秒,让哨兵选择新的主服务器,并重新启动被杀死的服务器
4、查看哨兵选重新选举后的服务器状态
7、SpringDataRedis SpringBoot1.X版本默认使用的是jedis作为Redsi的Java客户端,而2.X版本使用的是lettuce作为Redis的Java客户端。
两者的区别为:
Jedis:在实现上直接连接的Redis-Server,在多个线程间共享一个Jedis实例。线程不安全。如果要在多线程的场景下使用Jedis,需要使用连接池,每个线程都使用自己的Jedis实例,当连接数量增多时,会小号大量的物理资源。
lettuce:基于Netty的连接,是一个可伸缩的线程安全的Redis客户端,支持同步、一步和响应式模式。多个线程可以共享一个连接实例,而不必安全多线程并发的问题。它基于NettyNIO框架构建,支持redis的高级功能,如Sentinel(哨兵)、集群、流水线、自动连接和Redis数据模型。
7.1、创建SpringBoot项目
7.2、添加依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <dependencies > <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 > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > <exclusions > <exclusion > <groupId > org.junit.vintage</groupId > <artifactId > junit-vintage-engine</artifactId > </exclusion > </exclusions > </dependency > </dependencies >
7.3、添加application.yml配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 spring: redis: host: 192.168 .10 .100 port: 6379 password: root database: 0 timeout: 10000ms lettuce: pool: max-active: 1024 max-wait: 10000ms max-idle: 200 min-idle: 5
7.4、测试环境是否搭建成功 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @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" )); } }
7.5、序列化问题 上面的测试程序,虽然成功的把数据写入了,但是写入的是一个二进制字节码,需要处理一下。
RedisTemplate
使用的是JdkSerializationRedisSerializer
进行序列化,会序列化为二进制字节码储存。这时需要自定义模板解决。当自定义模板后,又想储存String字符串可以使用StringRedisTemplate
。自定义的模板和StringRedisTemplate
不冲突。
序列化的选择:
JdkSerializationRedisSerializer
:该序列化为RedisTemplate
的默认序列化工具,是JDK提供的,有点是反序列化时不需要提供类型信息(class),但是缺点就是序列化后的结果非常大,而且看不懂,时json格式的5倍左右,会消耗redis服务器的大量内存。
Jackson2JsonRedisSerializer
:使用的时Jackson
库将对象序列化为json字符串,优点是速度快,序列化后的字符串短小精悍,但是缺点也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息,通过查看源代码,发现其只再反序列化过程中用到了类型信息。
GenericJackson2JsonRedisSerializer
:通用型序列化,这种序列化方式不用自己手动指定对象的Class。
自定义模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 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;@Configuration public class RedisConfig { @Bean public RedisTemplate<String,Object> redisTemplate (LettuceConnectionFactory lettuceConnectionFactory) { RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setConnectionFactory(lettuceConnectionFactory); return redisTemplate; } }
序列化测试
1 2 3 4 5 6 7 8 9 @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
7.6、操作String 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @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); }
7.7、操作Hash 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @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); hos.entries("userInfo" ).forEach((k,v)->{ System.out.println(k+"---" +v); }); hos.delete("userInfo" ,"name" ); }
7.8、操作List 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @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 ,"张三" ); }
7.9、操作oSet 1 2 3 4 5 6 7 8 @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" ); }
7.10、操作SortedSet 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @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" ); }
7.11、获取所有的Key 1 2 3 4 @Test void testAllKeys () { redisTemplate.keys("*" ).forEach(System.out::println); }
7.12、删除 1 2 3 4 5 @Test void testDelete () { redisTemplate.delete("score" ); }
7.13、设置Key的失效时间 1 2 3 4 5 6 7 8 9 10 @Test void testEx () { ValueOperations vos = redisTemplate.opsForValue(); vos.set("code" ,"abcd" ,180 , TimeUnit.SECONDS); redisTemplate.expire("code" ,180 ,TimeUnit.SECONDS); System.out.println(redisTemplate.getExpire("code" )); }
7.14、整合使用哨兵机制 配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 spring: redis: host: 192.168 .10 .100 port: 6380 password: root database: 0 timeout: 10000ms lettuce: pool: max-active: 1024 max-wait: 10000ms max-idle: 200 min-idle: 5 sentinel: master: mymaster nodes: 192.168 .10 .100 :26379,192.168.10.100:26380,192.168.10.100:26381
配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 @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; }