1.redis实际开发的应用场景
1、热点数据的缓存: 减少对数据库的访问频率和减轻数据库的压力。 2. 限时业务的运用: 秒杀 存储登录者用户信息 存储短信验证码 3. 计数器相关问题: 点赞数 收藏数 播放量。 4. 排行榜相关问题: sort set 5. 分布式锁: ---同步锁: 6. 限量秒杀: ---decr key:
2.redis的持久化
把内存中的数据持久化到磁盘中,防止数据丢失。---当redis服务器开启时,会把磁盘中的数据加载到内存中进行计算。
提高了两种持久化方式:
RDB和AOF.
2.1 RDB持久化
它是每隔一段时间进行快照存储。--它是一个二进制文件,里面的内容打开后无法看懂。
触发机制:
[1]save---手动触发
[2]bgsave--手动触发
[3]通过配置文件--自动触发
2.1.1save触发
该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。具体流程如下
执行完成时候如果存在老的RDB文件,就把新的替代掉旧的。我们的客户端可能都是几万或者是几十万,这种方式显然不可取。
2.1.2 bgsave:
执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体流程如下
bgsave在执行该命令时会fork出一个新的线程,单独执行rdb持久化操作,而不影响其他客户对redis服务的操作。
不管使用save还是bgsave都需要手动输入。我们也可以通过配置文件完成自动化rdb操作。
RDB快照持久化优缺点:
优点: ----数据恢复速度快。
缺点: ----数据完整性差--会丢失最后一段时间的数据。
2.2 AOF持久化
日志追加持久化,当我们执行写操作,会触发一个函数write,把会把写操作的命令追加到一个日志文件appendfile中。当服务器启动时会把appendfile中的命令从新执行一遍。默认不开启。
手动开启aof模式。
AOF优缺点:
优点: 数据完整性高---丢失最后一条指令。
缺点: 数据恢复慢。
如果rdb和aof都使用,当服务器重启时会加载哪个文件?
先加载AOF的文件【它以数据完整性为主】。
3.redis集群模式
提供了三种集群模式.
[1]主从模式
[2]哨兵模式
[3]集群模式
为什么要使用集群模式:
[1]解决单机故障问题 [2]解决单机压力问题
3.1主从模式
配从不配主:非常简单。
准备: 一台linux服务。 开三个redis服务----通过修改port----6380 [主] 6381 6382[从]。
修改:
1.port端口号
2.rdb文件名dump638X.rbd
3.关闭aof
启动上面三个redis服务
redis-server redis6380.conf
redis-server redis6381.conf
redis-server redis6382.conf
进入相应的客户端
查看每个redis服务的角色:
发现他们都是master角色,如何分配主从关系:
配从不配主: slaveof 主节点ip 主节点port
主节点修改数据时,主节点的数据会同步到所有的从节点。
从节点只能进行读操作,不能执行写操作。
如果主节点宕机,从节点会不会篡位-----不会自动上位
如果增加一个从节点,该节点会不会把之前主节点的数据同步过来----一定同步
4.哨兵模式
命令:cd /usr/soft/redis6.版本/master
由于上面主从模式,主节点宕机后,从节点不会自动上位,这段时间内无法进行写操作
解决上面的问题---哨兵模式--->1.监控功能 2.故障恢复 3.选举一个master主节点
4.1监控功能
4.2master节点的选举
4.3启动哨兵
修改哨兵的配置 vi sentinel.conf
启动哨兵模式 :redis-sentinel sentinel.conf
测试:使用shutdown让主节点宕机 观察sentinel的控制台
如果原来的master回来(重新启用6380),跟着现在的master走
5.集群模式
不管是主从模式还是哨兵模式,始终只有一个master主节点。如果写操作并发高. 势必会导致master节点的压力过大。
真正的集群:
存储原理:
redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个整数结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
当你往Redis Cluster中加入一个Key时,会根据crc16(key) mod 16384计算这个key应该分布到哪个hash slot中,一个hash slot中会有很多key和value。你可以理解成表的分区,使用单节点时的redis时只有一个表,所有的key都放在这个表里;改用Redis Cluster以后会自动为你生成16384个分区表,你insert数据时会根据上面的简单算法来决定你的key应该存在哪个分区,每个分区里有很多key。
搭建三主三从:
7001~7006: 6台redis服务。
创建一个文件夹cluster存放6个redis的配置文件
修改6台redis配置文件--必须redis服务是空的
(1)端口号
(2) bind绑定---任意
(3)必须开启aof模式并修改文件名
(4)开启集群模式
开启上面6台redis服务
redis-server redisXXX.conf
ps -ef | grep redis (查看)
cluster(集群模式)
分槽,以及设置主从关系
redis-cli --cluster create --cluster-replicas 1 192.168.223.147:7001 192.168.223.147:7002 192.168.223.147:7003 192.168.223.147:7004 192.168.223.147:7005 192.168.223.147:7006
访问:
redis-cli -c -h 192.168.245.223 -p 7001
6.java连接redis
6.1java连接单机redis
6.1.1创建一个普通的maven工程
1)引入redis的依赖
<!--导入redis的依赖-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.8.0</version>
</dependency>
2)测试
package demo01;
import redis.clients.jedis.Jedis;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test01 {
public static void main(String[] args) {
//把所有关于对redis的操作都封装到一个类中Jedis
//Jedis jedis = new Jedis();//jedis 依赖架包 无参构造函数,默认连接的是本地的redis。端口默认6379
Jedis jedis = new Jedis("192.168.245.223", 6379);//有参函数
/*String ping = jedis.ping();//测试连通性
System.out.println(ping);*/
Set<String> keys = jedis.keys("*");//查看端口6379的所有内容
System.out.println(keys);
Long del = jedis.del("k1","k2","k3");//删除
System.out.println("删除的个数:"+del);
//jedis.expire("k4",20);//k4过时20秒
//关于字符串的操作
/* jedis.setex("k5",20L,"霍梦齐");
String k5 = jedis.get("k5");
System.out.println(k5);
Long setnx = jedis.setnx("k6","hmq");//k5有值是霍梦齐,结果是0没有存进去,改成k6就存进去了,因为k6没有东西。结果为1
System.out.println(setnx);*/
//关于hash的操作
jedis.hset("k7","name","霍梦齐");
String hget = jedis.hget("k7","name");
System.out.println(hget);
//存放多个
HashMap<String, String> map = new HashMap<String,String>();
map.put("name","霍梦齐真帅");
map.put("age","24");
map.put("sex","男");
jedis.hset("k8",map);
Map<String, String> map1 = jedis.hgetAll("k8");
System.out.println(map1.get("name"));
jedis.close();//关闭连接对象
}
}
3)报错
没有这个端口号
防火墙处于开启状态
运行redis.配置文件
特点: Jedis把对redis的操作都封装到Jedis类对象中了,而每个命令封装了一个对应的方法。
6.2java通过连接池连接redis
连接池的作用:减少频繁创建和销毁连接对象
package demo01;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class Test02 {
public static void main(String[] args) {
//配置连接对象的信息
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMinIdle(5);//设置最小的空闲数
poolConfig.setMaxIdle(10);//设置最大空闲数,当没人连接redis服务器时,该池子中最多能空闲几个连接对象
poolConfig.setMaxTotal(15);//当有人连接redis服务时,最大能生成多少个连接对象
//poolConfig.setMaxWait();
//创建连接池对象
JedisPool jedisPool = new JedisPool(poolConfig, "192.168.245.223", 6379);
long start = System.currentTimeMillis();
//从连接池获取连接对象
for (int i=0;i<100;i++){
Jedis jedis = jedisPool.getResource();
jedis.ping();
jedis.close();//把该对象还给连接池
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
}
6.3java连接redis集群
package demo01;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import java.util.HashSet;
import java.util.Set;
public class Test04 {
public static void main(String[] args) {
//必须时所有redis服务器
HostAndPort hostAndPort1=new HostAndPort("192.168.245.223",7001);
HostAndPort hostAndPort2=new HostAndPort("192.168.245.223",7002);
HostAndPort hostAndPort3=new HostAndPort("192.168.245.223",7003);
HostAndPort hostAndPort4=new HostAndPort("192.168.245.223",7004);
HostAndPort hostAndPort5=new HostAndPort("192.168.245.223",7005);
HostAndPort hostAndPort6=new HostAndPort("192.168.245.223",7006);
Set<HostAndPort> sets=new HashSet<HostAndPort>();
sets.add(hostAndPort1);
sets.add(hostAndPort2);
sets.add(hostAndPort3);
sets.add(hostAndPort4);
sets.add(hostAndPort5);
sets.add(hostAndPort6);
JedisCluster jedisCluster=new JedisCluster(sets);
jedisCluster.set("k1","v1");
jedisCluster.set("k2","v2");
jedisCluster.set("k3","v3");
jedisCluster.set("k4","v4");
jedisCluster.set("k5","v5");
jedisCluster.set("k6","v6");
jedisCluster.set("k7","v7");
}
}
7.springboot整合redis
springboot对redis的操作封装到模板类中RedisTemplate和StringRedisTemplate。StringRedisTemplate是Redistemplate的子类,它只能往redis中存放字符串类型。
7.1创建一个springboot工程
7.2引入依赖
<?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.3.12.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hmq</groupId>
<artifactId>hmq-redis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>hmq-redis</name>
<description>hmq-redis</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
7.3配置信息
#redis的配置信息--单机
spring.redis.host=192.168.245.223
spring.redis.port=6379
7.4测试
package com.hmq;
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.connection.StringRedisConnection;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.Map;
import java.util.Set;
@SpringBootTest
class HmqRedisApplicationTests {
//springboot创建好该类对象 并交于IOC容器管理
@Autowired
private StringRedisTemplate redisTemplate;
@Test
void contextLoads() {
//操作key
//查看所有的key
/* Boolean k1 = redisTemplate.hasKey("k1");//查看看是否存在
System.out.println("判断k1是否存在:"+k1);
Set<String> keys = redisTemplate.keys("*");//查看所有的keys
System.out.println("所有的key:"+keys);
Boolean k11 = redisTemplate.delete("k1");//删除key
System.out.println("是否删除成功:"+k11);*/
//2.字符串的操作---redisTemplate类中对于每一种数据类型的操作,单独封装了相应的类。
// ValueOperations<String, String> forValue = redisTemplate.opsForValue();
// forValue.set("k1","ldh");
// String value = forValue.get("k1");
// System.out.println("获取k1对于的value值:"+value);
// //setnx----
// forValue.setIfAbsent("k1","张学友");
// forValue.setIfAbsent("k2","刘德华");
// System.out.println(forValue.get("k1"));
// System.out.println(forValue.get("k2"));
//3.hash类型操作
HashOperations<String, Object, Object> forHash = redisTemplate.opsForHash();
forHash.put("k3","name","hmq");
forHash.put("k3","age","18");
Object o = forHash.get("k3", "name");
System.out.println("name的值:"+o);
Map<Object, Object> k3 = forHash.entries("k3");
System.out.println(k3);
}
}
总结:
1. 哨兵模式: 哨兵监控redis集群中每台服务器的状态,通过心跳机制发送ping命令。
2. 集群模式: 把16384个槽平均分配到主节点。
3. java连接redis.----Jedis---把操作redis的方法都封装到Jedis中,而每个方法对应redis中相应的命令。
4. springboot操作redis.封装了俩个类RedisTemplate和StringRedisTemplate,而StringRedistemplate它是redisTemplate的子类,它只能对字符串操作。如果想通过该StringRedistemplate放入一个对象时,需要把该对象转换为json字符串才能放入