Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
是运行在内存中的数据结构存储系统
String、散列、list、set 、sSet
读:11.2万/秒 写:8.6万/秒 平均10万/秒 单台
6379,来源于一个意大利女演员的名字对应的手机按键数字
国外:redis.io
国内:redis.cn
先要在Linux系统中进行以下两项操作
yum -y install gcc #安装gcc编译器
yum -y install Irzsz #安装文件上传工具
方式一、使用Linux系统中的指令访问网址下载压缩包
点击 链接另存为...
之后,在Linux中使用以下指令直接在线下载
wget (复制链接路径)
方式二、将本地压缩包拖拽上传
演示略.....
tar -xvf (redis压缩包名)
注意:请进入redis目录中执行
make
make install
vim redis.conf
如果我们只配置一台redis缓存服务器,那么将会导致redis的内存开销很大,所以,就出现了分片机制,就是配置多台redis缓存服务器,把缓存数据分别由多台缓存服务器承担,以提高读写效率。
1.创建目录
2.将redis.conf文件复制多份到新建的目录中
4.编写启动脚本
5.关闭脚本
6.测试类测试(注意一定要关闭防火墙或者开放端口)
@Test
public void test() {
List shards=new ArrayList<>();
shards.add(new JedisShardInfo("192.168.126.129",6379));
shards.add(new JedisShardInfo("192.168.126.129",6380));
shards.add(new JedisShardInfo("192.168.126.129",6381));
ShardedJedis shardedJedis = new ShardedJedis(shards);
shardedJedis.set("a", "北京烤鸭");
}
7.Spring boot整合redis分片
#新建redis.properties,配置redis分片
redis.shards=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381
编写配置类
将ShardedJedis 对象交给spring去管理
@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class redisConfig {
@Value("${redis.shards}")
private String shards;
@Bean
public ShardedJedis shardedJedis() {
List list = new ArrayList();
//1.将shards字符串转化为node数组
String[] nodes=shards.split(",");
for(String str:nodes) {
String host=str.split(":")[0];
int port = Integer.valueOf((str.split(":")[1]));
//实现节点封装
list.add(new JedisShardInfo(host, port));
}
return new ShardedJedis(list);
}
}
最后,DI注入即可使用
采用分片机制后,如果redis服务器宕机了几台,会影响我们程序的正常运行,因为服务器依然会访问宕机的redis
可以配置多个备用的redis服务器,如果主服务器宕机,备用服务器将会立即补位,并且要求备用服务器中的数据与主服务器中的数据一致。
先将redis.conf文件复制多份,修改端口等操作
slaveof 主机IP 主机端口 #在从机中配置主机IP+端口
选定目标从机后,在其客户端,使用以上命令挂载主机
info replication #查看当前服务器信息
1.当主从结构搭建成功之后,主机的数据,从机中可以同步
2.搭建主从结构之后,从机属于只读状态,不可写入数据
3.这种主从配置的有效期是基于内存的,如果服务器关机,结构失效
4.如果想长期有效,可以使用哨兵机制、集群机制完成
我们可以配置一个哨兵来检测主服务器的状态,利用心跳检测机制实现,如果超过三次没有PING通,则判断主服务器宕机,之后,通过算法选举出新的主服务器。当哨兵选举出新的主机之后,为了保证主从的关系,则会动态的修改各自的redis.conf配置文件.并且将其他的节点标识为新主机的从机。
cp sentinel.conf sentinel
关闭保护模式
开启后台启动
修改哨兵监控
sentinel monitor mymaster 192.168.126.129 6379 1
哨兵 监控 监控主机名 IP+端口 表示1票同意
调整选举时间
选举最大时长,如果到达没能选举出,则开启新的选举
redis-sentinel sentinel.conf
redis-cli -p 6379 shutdown
public class TestRedisSentinel {
@Test
public void test01() {
//定义哨兵的集合信息 host:port
Set sentinels = new HashSet();
sentinels.add("192.168.126.129:26379");
JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels);
Jedis jedis = pool.getResource();
jedis.set("a", "你好我是哨兵机制!!!!");
System.out.println(jedis.get("a"));
}
}
分片机制与哨兵机制的优缺点
1.分片机制实现了内存数据的扩容
2.分片机制缺失高可用的实现,如果一台服务器宕机,则用户将不能正常访问数据
3.Redis分片机制中,是业务服务器进行一致性hash的计算.而redis服务器只需要负责数据的存储即可.所以redis分片机制性能更高.
3.哨兵实现了高可用机制,主机宕机后,从机立即补位
4.哨兵机制缺少对内存的合理扩容
6.哨兵本身没有实现高可用.哨兵如果宕机,则直接影响用户使用
集群策略其实就是将哨兵机制与分片机制的优点进行了实现。
让服务器即实现高可用机制又具备内存的扩容
mkdir cluster
mkdir 5000 5001 ......
绝对路径配置,redis.pid文件会在当前目录下生成
绝对路径配置,持久化文件会在当前目录下生成
说明:将5000文件夹下配置完毕的redis.conf文件分别复制到5001-5008中
[root@localhost cluster]# cp 5000/redis.conf 5001/
[root@localhost cluster]# cp 5000/redis.conf 5002/
[root@localhost cluster]# cp 5000/redis.conf 5003/
[root@localhost cluster]# cp 5000/redis.conf 5004/
....
说明:分别将5001-5008文件中的5000改为对应的端口号的名称
:%s/5000/5001/g
5000 #需要替换的数据
5001 #替换的数据
g #全部替换
#5.0版本执行 使用C语言内部管理集群
redis-cli --cluster create --cluster-replicas 1
192.168.126.129:5000 192.168.126.129:5001
192.168.126.129:5002 192.168.126.129:5003
(IP+端口)......
#1的意思是一台主机后面有几台从机
#一般redis会将前面几台作为主机
在集群机制中,Redis的所有节点都会保存当前redis集群中的全部主从状态信息,各个节点能够相互通信,通过心跳监测机制相互监控,如果超过一半的节点认为某节点的主机已经宕机,则会通过选举机制,投票选举链接宕机的备用机。
#定义redis节点信息 (redis集群的IP+端口)
redis.cluster=192.168.126.129:5000,192.168.126.129:5001...
#如果在pro文件中配置了以下属性,则在配置类中无需再次添加
#最小空闲数
redis.minIdle=100
#最大空闲数
redis.maxIdle=300
#最大连接数
redis.maxTotal=1000
#=====================
package com.jt.config;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class redisConfig {
/** 单体Redis的整合配置
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private int port;
//将那个对象交给容器,返回值就是谁
@Bean
public Jedis jedis() {
Jedis jedis=new Jedis(host,port);
return jedis;
}*/
/** 分R片edis的整合配置
@Value("${redis.shards}")
private String shards;
@Bean
public ShardedJedis shardedJedis() {
List list = new ArrayList();
//1.将shards字符串转化为node数组
String[] nodes=shards.split(",");
for(String str:nodes) {
String host=str.split(":")[0];
int port = Integer.valueOf((str.split(":")[1]));
//实现节点封装
list.add(new JedisShardInfo(host, port));
}
return new ShardedJedis(list);
}
*/
//Redis集群的配置
@Value("${redis.cluster}")
private String cluster;
@Bean
public JedisCluster Jediscluster() {
String[] split = cluster.split(",");
Set nodes=new HashSet<>();
for (String str : split) {
String[] HostAndPort = str.split(":");
String host=HostAndPort[0];
int port=Integer.valueOf(HostAndPort[1]);
nodes.add(new HostAndPort(host, port));
}
//定义redis链接池
JedisPoolConfig pool=new JedisPoolConfig();
pool.setMaxTotal(1000);
pool.setMaxIdle(20);//允许空闲链接最大数
return new JedisCluster(nodes,pool);
}
}
HASH槽是redis集群中的数据存储策略,所有的键根据哈希函数(CRC16(key)%16384),将键映射到到0-16383槽内,而这些槽位由不同的节点所管理,每个节点维护部分槽及槽所映射的键值数据,槽的分区会根据节点的数量均匀分配。
redis会先将键进行哈希函数运算,计算的结果会匹配到对应的槽的区间,之后再set数据,将数据保存到管理改槽的节点中。
它是一种特殊的哈希算法,目的是解决分布式缓存的问题,在新增/移除节点.要求尽可能小的改变原有的映射关系,解决了分布式环境下的存储动态伸缩性(弹性)问题.
该特点是说:hash的节点应该互相均衡,这样才能达到负载均衡的目的
因为,节点是通过hash得到的所以会出现节点不均匀的现象,所以,出现了虚拟节点,虚拟节点可以解决此类问题,它会为负载小的节点创建虚拟节点
单调性是指在删除或者新增节点的时候,不会影响系统正常运行,数据会随着迁移
分散性是指数据应该分散地存放在分布式集群中的各个节点(节点自己可以有备份),不必每个节点都存储所有的数据
因为redis是基于内存工作的,如果遇到宕机或者意外情况造成服务器关闭,则数据就会流失,这是我们不想看到的,为了解决这个问题,就需要定期将数据持久化保存,假如发生意外情况,可以根据持久化文件恢复数据。
redis默认情况下使用的持久化模式,该模式会定期将数据以快照的形式保存在文件中,RDB模式由于记录的是内存数据的快照所以持久化的效率较高,新的快照会覆盖旧的快照.每次保留的都是最新的数据. 持久化文件的大小相对固定.
持久化文件名配置
持久化文件的路径(./表示的是当前路径,也就是conf文件的当前路径)
相关指令
save #表示立即持久化,属于同步操作,持久化的过程中阻塞其他线程
bgsave #表示立即持久化,属于异步操作
save 900 1 如果在900秒内,用户执行了1次更新操作,则持久化1次
save 300 10 如果在300秒内,用户执行了10次更新操作,则持久化1次
save 60 10000 如果在60秒内,用户执行了10000次更新操作,则持久化1次
设置save 1 1 可以吗?
答:由于save指令是阻塞的,所以该操作会极大的影响执行效率.
设置bgsave 1 1 可以吗?
答:异步操作 有可能导致内存数据与持久化文件中的数据不一致.
AOF模式默认是关闭状态,需要手动开启,此模式记录的是用户的操作,持久化文件大,持久效率慢
开启AOF(开启之后需要重启redis)
默认文件名
AOF持久化策略
appendfsync always 当用户更新了redis,则追加用户操作记录到aof文件中
appendfsync everysec 每秒持久化一次. 略低于rdb模式.
appendfsync no 不持久化(不主动持久化,由操作系统决定什么时候持久化) 该操作一般不配.
1.首先关闭所有的redis
2.找到redis的AOF文件. 将FLUSHALL命令删除.之后重启redis服务器即可.
如何合理选择:
1.如果内存数据可以允许少量的数据丢失,则首选rdb
2.如果内存数据存储的是业务数据不允许丢失,则选用aof模式.(AOF文件相对较大,所以定期维护.)
3.redis中所有的操作都是单进程单线程操作.(串行的)所以不会造成线程并发性问题
在redis中内存是有限的,如果数据过多,redis会根据相关算法清除数据,redis中提供了9种内存机制,可以根据其中不同的算法,实现内存数据的优化。
1.volatile-lru 在设定了超时时间的数据中采用 lru算法
2.allkeys-lru . 所有数据采用lru算法
3.volatile-lfu 在设定了超时时间的数据中采用lfu算法
4.allkeys-lfu 所有数据采用lfu算法
5.volatile-random 设定了超时时间的数据采用随机算法
6.allkeys-random ->随机删除数据
7.volatile-ttl 设定了超时时间的数据,采用ttl算法
8.noeviction 不删除任何数据,只是报错返回.
如果用户一直在访问数据库中压根不存在的数据,请求不会走redis缓存,而是都发送到了服务器上,导致服务器压力过大,存在宕机风险,这种现象称为缓存穿透。
解决方案:
一般做数据的有效性的校验.
由于某一个热点数据超时后,大量用户访问此数据,短时间内数据库访问压力加大,这种现象称为缓存击穿。
如何解决:
1.将热点数据永久保存.
2.添加互斥(排它)锁(每次只能有一个用户访问数据库/第二次走缓存) lock操作
在redis内存中的数据,在一定的时间内,有大量的缓存数据失效(多个),这时用户大量的访问缓存服务器,但是缓存中没有指定数据,则访问数据库.容易出现数据库宕机,这种现象称为缓存雪崩。
如何解决:
1.让热点数据永久有效
2.设定超时时间采用随机数. 让超时的数据不要在同一时间发生
3.设定多级缓存
相关文章推荐:https://blog.csdn.net/zeb_perfect/article/details/54135506
1.默认条件下redis采用rdb模式,如果开启了AOF模式,则以aof模式为主
2.同时可以通过save指令,实现rdb模式的持久化操作
说明:虽然redis提供了事务操作.但是该事务是一种弱事务.
只对单台redis有效.
如果有多台redis,如果需要使用事务控制,则一般使用队列的形式.
@Test
public void testTX() {
Transaction transaction = jedis.multi(); //开始事务
try {
transaction.set("a", "a");
transaction.set("b", "b");
//int a = 1/0;
transaction.exec(); //提交事务
} catch (Exception e) {
e.printStackTrace();
transaction.discard(); //事务回滚
}
}
节点的数量要满足以下公式
公式:剩余存活节点 > 总节点/2
根据公式我们得知最小的节点个数为3
假如准备3个节点
3台宕机1台
2 > 3/2=1.5
如果再次宕机
1 < 3/2=1.5
显然,如果是3个节点,允许最多宕机1台
假如我们多搭建一个节点,允许宕机的个数会不会增长呢?
假如准备4个节点
4台宕机1台
3 > 4/2=2
如果再次宕机
2 = 4/2=2
如果搭建4个节点,发现允许宕机的最大节点数依然是1台
3台与4台的结果相同,在节省资源的前提下,选择前者