1.Redis分片
1.1为什么用分片
1- 说明: 虽然redis可以扩展内存空间大小,但是如果需要存储海量数据一味地扩大内存其实效率不高.
2- 分片介绍: 准备多态redis,共同为用户提供缓存服务,在保证效率的前提下,–实现了内存的扩容,
用户在使用分片机制时,将多台redis当一台使用
准备端口号分别为6379/6380/6381的三个Redis
[root@localhost redis5.5]# mkdir shards
[root@localhost redis5.5]# ls
00-RELEASENOTES CONTRIBUTING deps INSTALL MANIFESTO redis.conf runtest-cluster runtest-sentinel shards tests
BUGS COPYING dump.rdb Makefile README.md runtest runtest-moduleapi sentinel.conf src utils
[root@localhost redis5.5]# cp redis.conf shards/6379.conf
[root@localhost redis5.5]# cp redis.conf shards/6380.conf
[root@localhost redis5.5]# cp redis.conf shards/6381.conf
- 修改6380和6381的路径
vim 6380.conf
vim 6381.conf
修改port
- 启动6379、6380、6381
redis-server 6379.conf & redis-server 6380.conf & redis-server 6381.conf
- 测试类TestRedisShards
package com.jt.test;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
public class TestRedisShards {
@SuppressWarnings("resource")
@Test
public void test01() {
//1.准备list集合 之后添加节点信息
List<JedisShardInfo> 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));
//2.创建分片对象
ShardedJedis shardedJedis =new ShardedJedis(shards);
shardedJedis.set("shards", "准备分片爆炸");
System.out.println(shardedJedis.get("shards"));
}
}
结果随机存储到了6381里面
127.0.0.1:6379> KEYS *
(empty list or set)
127.0.0.1:6380> KEYS *
(empty list or set)
127.0.0.1:6381> KEYS *
1) "shards"
1.2一致性hash算法
一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题。
- 原理:百度百科
哈希算法作为分布式存储领域的一个重要算法
目的:
- 解决数据如何在分布式环境下进行存储
- hash取值区间 : 8位16进制数 共有2^32种可能性
以下部分有关hash一致性内容来源于:链接
- 现在一致性hash算法在分布式系统中也得到了广泛应用,研究过memcached缓存数据库的人都知道,memcached服务器端本身不提供分布式cache的一致性,而是由客户端来提供,具体在计算一致性hash时采用如下步骤:
- 首先求出memcached服务器(节点)的哈希值,并将其配置到0~232的圆(continuum)上。
- 然后采用同样的方法求出存储数据的键的哈希值,并映射到相同的圆上。
- 然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。如果超过232仍
然找不到服务器,就会保存到第一台memcached服务器上。
- 一致性哈希概述图:
从上图的状态中添加一台memcached服务器。余数分布式算法由于保存键的服务器会发生巨大变化而影响缓存的命中率,但Consistent Hashing中,只有在园(continuum)上增加服务器的地点逆时针方向的第一台服务器上的键会受到影响,如下图所示:
(ps:新增了一个node5节点,节点中的数据可以动态迁移)
- 哈希算法应该满足的几个条件:平衡性、单调性和分散性。
①平衡性是指hash的结果应该平均分配到各个节点,这样从算法上解决了负载均衡问题 。
②单调性是指在新增或者删减节点时,不影响系统正常运行 。
③分散性是指数据应该分散地存放在分布式集群中的各个节点(节点自己可以有备份),不必每个节点都存储所有的.
1.3SpringBoot整合Redis分片
1.3.1修改redis.properties
#redis.host=192.168.126.129
#redis.port=6379
redis.nodes=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381
1.3.2编辑配置类RedisConfig 实现redis整合
package com.jt.config;
import java.util.ArrayList;
import java.util.List;
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.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
@Configuration //我是一个配置类 一般都会与@Bean联用
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {
@Value("${redis.nodes}")
private String redisNodes; //node,node,node
/*整合分片实现Redis内存扩容*/
@Bean
public ShardedJedis shardedJedis() {
String[] nodes =redisNodes.split(",");//节点数组分隔
//动态获取Redis节点信息
List<JedisShardInfo> list=new ArrayList<JedisShardInfo>();
for (String node : nodes) {//node= host:port ---->[host,port]
String host=node.split(":")[0];
int port =Integer.parseInt(node.split(":")[1]);
list.add(new JedisShardInfo(host,port));
}
return new ShardedJedis(list);
}
/**
* 单台测试
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private Integer port;
//将返回值的结果交给spring容器管理,如果以后想要使用该对象则可以直接注入
@Bean
public Jedis jedis() {
return new Jedis(host,port);
}
*/
}
1.3.3 修改CacheAOP中的注入
@Autowired(required=false)
//private Jedis jedis; //单台注入
private ShardedJedis jedis; //多台注入
1.3.4 关于Redis分片总结
1.当Redis节点宕机之后,用户访问必然受到影响
2.当redis服务宕机之后,该节点中的数据肯丢失
3.redis分片可以实现内存数据的扩容
4.redis分片机制中hash运算发生在业务服务器中,redis只负责存取,不负责计算,所以效率更高
2.Redis属性说明
2.1Redis持久化策略
2.1.1说明
说明: Redis的数据都保存在内存中,如果断电或者宕机,则内存数据将擦除,导致数据的丢失.为了防止数据丢失,Redis内部有持久化机制.
当第一次Redis服务启动时,根据配置文件中的持久化要求.进行持久化操作.如果不是第一次启动,则在服务启动时会根据持久化文件的配置,读取指定的持久化文件.实现内存数据的恢复.
- 持久化文件名字
- 文件路径(./)为当前路径
2.1.2 RDB模式
特点:
1.rdb模式是redis中默认的持久化策略.
2.rdb模式定期持久化.保存的是Redis中的内存数据快照.持久化文件占用空间较小.
3.rdb模式可能导致内存数据丢失!!!
命令:
前提:需要在redis的客户端中执行.
- save 命令 立即持久化 会导致其他操作陷入阻塞.
- bgsave 命令 开启后台运行. 以异步的方式进行持久化. 不会造成其他操作的阻塞.
持久化周期:
save 900 1 900秒内,如果用户执行的1次更新操作,则持久化一次
save 300 10 300秒内,如果用户执行的10次更新操作,则持久化一次
save 60 10000 60秒内,如果用户执行的10000次更新操作,则持久化一次
save 1 1 1秒内,如果用户执行的1次更新操作,则持久化一次 set 阻塞!!!!
2.1.3 AOF模式
特点:
1). AOF模式默认条件下是关闭状态. 如果需要开启则需要修改配置文件.
2). AOF模式可以实现数据的实时持久化操作,AOF模式记录的是用户的操作过程.
3). 只要开启了AOF模式,则持久化方式以AOF模式为主.
配置:
开启AOF持久化方式
699 --改成yes
703 --持久化文件名称配置:
appendfsync always 只要用户执行一次操作,则持久化一次.
**appendfsync everysec 每秒持久化一次 默认策略** 效率略低于RDB
appendfsync no 不主动持久化.
2.1.5 持久化总结
1.如果用户允许少量的数据丢失,则可以选用RDB模式. 效率更高
2.如果用户不允许数据丢失,则选用AOF模式.
3.可以2种方式都选, 需要搭建组从结构 , 主机选用RDB模式, 从机选用AOF模式,可以保证业务允许.
2.1.6 RDB和AOF两种持久化的区别
RDB | AOF | |
---|---|---|
存储数据 | 保存键空间的所有键值对(包括过期字典中的数据),并以二进制形式保存,符合rdb文件规范,根据不同数据类型会有不同处理 | 保存redis服务器所执行的所有写命令来记录数据库状态,在写入之前命令存储在aof_buf缓冲区。记录的是用户的操作过程. |
持久化时间选择 | 通过conf的save选项设置持久化行为(单位时间内的修改次数)。 | 通过conf的appendfsync选项设置持久化行为 |
数据还原 | 服务器载入rdb文件,阻塞线程,在载入完成之前不接受任何命令。 | 服务器创建不带网络连接的伪客户端,读取aof文件中的所有命令并执行(redis服务开启aof持久化在服务器启动时会选择aof文件恢复数据库状态) |
过期键 | 在写入或读取时会忽略过期键 | 不会忽略过期键,在键被惰性删除或定期删除时向aof文件追加一条删除命令 |
文件大小 | 随着存储数据量的变化而变化(根据不同数据类型有不同的数据压缩优化) | 随着执行命令的增加而增加(通过aof重写进行优化) |
执行命令 | SAVE和BGSAVE | – |
2.1.7配置主从挂载
一般主机用RDB模式,从机用AOF模式,效率更高
6379.conf
250--rdbchecksum yes
253--dbfilename dump6379.rdb
6380.conf
700 appendonly yes
704 appendfilename "appendonly6380.aof"
- 从机向主机挂载
slaveof 192.168.126.129 6379
info replication
127.0.0.1:6380> SLAVEOF 192.168.126.129 6379
127.0.0.1:6379> info replication
# Replication
role:master //默认都是自己为主
connected_slaves:0 //还没有从机
master_replid:3e9709156b65b4d74657e89c47c1c612c97d2ae9
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
[root@localhost shards]# redis-cli -p 6380
127.0.0.1:6380> SLAVEOF 192.168.126.129 6379
OK
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:192.168.126.129
master_port:6379
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_repl_offset:28
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:424bd600f76d088eabf77b74e27d42f0b7007d50
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:28
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:28
- 主从测试
- 主机中添加测试数据.
- 检查从机中是否有数据
- 检查持久化文件是否有数据.
#redis.host=192.168.126.129
#redis.port=6379
#redis.nodes=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381
redis.nodes=192.168.126.129:6379,192.168.126.129:6380
2.2Redis内存策略
2.2.1说明
2.2.2 Redis的过期策略
我们都知道,Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理。
过期策略通常有以下三种:
定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
(expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)
Redis中同时使用了惰性过期和定期过期两种过期策略。
2.2.3 Redis的内存淘汰策略 --(具体八种算法)
Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据。
所有数据
- noeviction(默认的策略):当内存不足以容纳新写入数据时,,即当内存使用达到阈值的时候,所有引起申请内存的命令都会报错;
- allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
- allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
- allkeys-lfu 所有的数据采用LFU算法实现数据删除.
设定了超时时间的数据 - volatile-lfu 设定了超时时间的数据,采用LFU算法进行删除
- volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
- volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
- volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
2.2.4 LRU算法
从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰 。
适用场景: 如果我们的应用对缓存的访问都是相对热点数据,就可以选择这个策略;
2.2.5 LFU算法
LFU(least frequently used (LFU) page-replacement algorithm)。即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
2.2.6 随机算法(random)
随机
2.2.7 TTL算法
设定了超时时间的数据提前删除
2.3Redis集群
2.3.1为什么要搭建集群
通常,为了提高网站响应速度,总是把热点数据保存在内存中而不是直接从后端数据库中读取。
Redis是一个很好的Cache工具。大型网站应用,热点数据量往往巨大,几十G上百G是很正常的事儿。
由于内存大小的限制,使用一台 Redis 实例显然无法满足需求,这时就需要使用多台 Redis作为缓存数据库。但是如何保证数据存储的一致性呢,这时就需要搭建redis集群.采用合理的机制,保证用户的正常的访问需求.
采用redis集群,可以保证数据分散存储,同时保证数据存储的一致性.并且在内部实现高可用的机制.实现了服务故障的自动迁移.
2.3.2集群搭建计划
-
主从划分:
3台主机 3台从机共6台 端口划分7000-7005 -
准备集群文件夹cluster
cd /usr/local/src/redis5.5
mkdir cluster
- 在cluster文件夹中分别创建7000-7005文件夹
cd /usr/local/src/redis5.5/cluster
mkdir 7000 7001 7002 7003 7004 7005
- 将redis根目录中的redis.conf文件复制到cluster/7000/ 并以原名保存
cd cd /usr/local/src/redis5.5
cp redis.conf cluster/7000/
- 编辑配置文件Redis.conf
cd /usr/local/src/redis5.5/cluster/7000
[root@localhost 7000]# vim redis.conf
//1.注释本地绑定IP地址
69 #bind 127.0.0.1
//2.关闭保护模式
88 protected-mode no
//3.修改端口号
92 port 7000
//4.启动后台启动
136 daemonize yes
//5.修改pid文件
158 pidfile /usr/local/src/redis5.5/cluster/7000/redid.pid
//6.修改持久化文件路径
263 dir /usr/local/src/redis5.5/cluster/7000
//7.设定内存优化策略
597 maxmemory-policy volatile-lru
//8.关闭AOF模式
699 appendonly no
//9.开启集群配置
832 cluster-enabled yes
//10.开启集群配置文件
840 cluster-config-file nodes.conf
//11.修改集群超时时间
846 cluster-node-timeout 15000
- 说明:将7000文件夹下的redis.conf文件分别复制到7001-7005中
- 修改7001到7005的端口号
%s/7000/7001/g
%s/7000/7002/g
%s/7000/7003/g
%s/7000/7004/g
%s/7000/7005/g
- 创建启动脚本 vim start.sh
#!/bin/sh
redis-server 7000/redis.conf &
redis-server 7001/redis.conf &
redis-server 7002/redis.conf &
redis-server 7003/redis.conf &
redis-server 7004/redis.conf &
redis-server 7005/redis.conf &
- 编辑关闭的脚本 vim shutdown.sh
#!/bin/sh
redis-cli -p 7000 shutdown &
redis-cli -p 7001 shutdown &
redis-cli -p 7002 shutdown &
redis-cli -p 7003 shutdown &
redis-cli -p 7004 shutdown &
redis-cli -p 7005 shutdown &
//启动redis节点
sh start.sh
//检查redis节点启动是否正常
ps -ef | grep redis
//创建redis集群
redis-cli --cluster create --cluster-replicas 1 192.168.126.129:7000 192.168.126.129:7001 192.168.126.129:7002 192.168.126.129:7003 192.168.126.129:7004 192.168.126.129:7005
//yes
2.3.3Redis集群高可用测试
- 关闭redis主机.检查是否自动实现故障迁移.
- 再次启动关闭的主机.检查是否能够实现自动的挂载.
一般情况下 能够实现主从挂载
个别情况: 宕机后的节点重启,可能挂载到其他主节点中(7001-7002) 正确的
2.3.4Redis集群原理
原理说明:
Redis的所有节点都会保存当前redis集群中的全部主从状态信息.并且每个节点都能够相互通信.当一个节点发生宕机现象.则集群中的其他节点通过PING-PONG检测机制检查Redis节点是否宕机.当有半数以上的节点认为宕机.则认为主节点宕机.同时由Redis剩余的主节点进入选举机制.投票选举链接宕机的主节点的从机.实现故障迁移
2.3.4Redis集群宕机条件
特点:集群中如果主机宕机,那么从机可以继续提供服务,
当主机中没有从机时,则向其它主机借用多余的从机.继续提供服务.如果主机宕机时没有从机可用,则集群崩溃.
答案:9个redis节点,节点宕机5-7次时集群才崩溃.
2.3.5 redis入门案例
package com.jt.test;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
public class TestCluster {
/**
* 通过程序操作redis 3主机 读写 3从机 数据备份
*/
@Test
public void test01() {
Set<HostAndPort> nodes = new HashSet<HostAndPort>();
nodes.add(new HostAndPort("192.168.126.129", 7000));
nodes.add(new HostAndPort("192.168.126.129", 7001));
nodes.add(new HostAndPort("192.168.126.129", 7002));
nodes.add(new HostAndPort("192.168.126.129", 7003));
nodes.add(new HostAndPort("192.168.126.129", 7004));
nodes.add(new HostAndPort("192.168.126.129", 7005));
//利用程序操作redis集群
JedisCluster jedisCluster = new JedisCluster(nodes);
jedisCluster.set("AAAAA", "redis集群测试");
System.out.println(jedisCluster.get("AAAAA"));
}
}
二进制
1和0
从右往左(下表中第一个开始表示最左边往右算)
2* | 101011 | =42 |
---|---|---|
20 | 1 | =1 |
21 | 1 | =2 |
22 | 0 | =0 |
23 | 1 | =8 |
24 | 0 | =0 |
25 | 1 | =32 |
八进制
0-7
8* | 7786 | =4046 |
---|---|---|
80 | 6 | =6 |
81 | 8 | =8 |
82 | 7 | =64*7(448) |
83 | 7 | =512*7(3584) |
十六进制
0 -9、a-f
16* | a78e | =42894 |
---|---|---|
160 | e | =14 |
161 | 8 | =128 |
162 | 7 | =256*7(1792) |
163 | a | =4096*a(40960) |
练习110110110110是16进制数的多少
二进制 | 1101 | 1011 | 0110 |
---|---|---|---|
十进制 | 13 | 11 | 6 |
十六进制 | d | b | 6 |
Redis hash槽存储数据原理
说明: RedisCluster采用此分区,所有的键根据哈希函数(CRC16[key]%16384)映射到0-16383槽内,共16384个槽位,每个节点维护部分槽及槽所映射的键值数据.根据主节点的个数,均衡划分区间.
算法:哈希函数: Hash()=CRC16[key]%16384
当向redis集群中插入数据时,首先将key进行计算.之后将计算结果匹配到具体的某一个槽的区间内,之后再将数据set到管理该槽的节点中.