Redis作为一个流行的缓存技术,支持非常多的客户端语言,其中java就具备多种不同的客户端api操作redis服务端执行命令,其中jedis,lettuce和redission是比较广发使用的三种,目前springboot2.2.5封装了前2种
启动redis的服务端,外界在默认的配置中不允许访问redis-server的,可以使用redis.conf加载启动9000端口的服务端
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
Redis集群至少需要3个节点,因为投票容错机制要求超过半数节点认为某个节点挂了该节点才是挂了,所以2个节点无法构成集群。
redis的分布式
为什么要用分布式
jedis对象可以实现的功能:用户登录状态的存储和读取
虽然系统功能能够通过jedis操作9000这个端口的客户端实现读写,但是单个节点redis使用存在物理瓶颈。
hash取余就可以应用在redis分布式数据分片计算逻辑后,当有key-alue出现时,先对key做hash取余,n是节点个数(现在是3)所有节点jedis排序(list)1 2 3…n-1使用到取余结果对应到一个固定jedis对象,最终连接固定的redis节点
jedis封装了一种对应分布式的分片计算逻辑----一致性hash,在redis集群做分布式扩容和缩容时,虽然也会影响数据的单调性,但是节点越多,影响的数据量越小
/**
* 上述实现了自定义分片对象,但是由于hash取余不适用于redis
* 所以jedis单独封装了一个算法--一致性hash
* ShardedJedis 构造过程提供所有分片信息
*/
@Test
public void consistentHash(){
String key=UUID.randomUUID().toString();
String value="haha";
//帮助分片对象收集所有节点信息
List<JedisShardInfo> nodes=new ArrayList<>();
nodes.add(new JedisShardInfo("192.168.73.130",8888));
nodes.add(new JedisShardInfo("192.168.73.130",8889));
nodes.add(new JedisShardInfo("192.168.73.130",8890));
//创建出jedis的分片对象
ShardedJedis sJedis=new ShardedJedis(nodes);
//见证分布式 分片计算和单调性保证
sJedis.set(key,value);
//使用集群读取key值
System.out.println(sJedis.get(key));
}
基础:麻省理工大学97年创建一个数学模型,后背应用到分布式领域,实现了一个hash映射计算,可以将任意的内存数据对象,通过hash散列映射到【0,2……32-1】
由于结果是32的二进制,0减去1变成2……32-1最大数加一变成0,这个取值区间称之为hash环
节点手机信息时,每个节点信息作为一个对象映射到这个换上一个整数
扩容缩容时,单调性保证
如果集群节点越多,hash环弧线被分配的越短,对应的数据量越小,根据上述的计算逻辑,新节点加入时,影响的数据单调性范围越小,所以在一个频繁发生扩容缩容的集群中,分布式计算为了保证单调性一致hash比hash取余更适合
数据平衡性
当节点相互映射的整数比较近,必定会有某些节点对应key值远远大于其他节点,造成严重的数据倾斜,所以需要解决这个问题,一致性hash计算逻辑引入虚拟节点。
通过对虚拟节点的个数控制,还可以做到按照比例的分片数据,在jedis中实现的虚拟节点个数160*weight,weight默认是1
在目前阶段,不管是单个jedis对象,还是分片对象ShardedJedis都是一个对象(关联的都是一个单个的链接),应用效率不高,在实际应用中,总要从代码中new出来然后用完了要关闭(close),应该引用连接池。
一次性创建多个jedis对象,都是链接一个节点的,用的时候从链接池中获取临街,用完归还链接,提升对象的使用效率,不必频繁的创建销毁对象。
/*
JedisPool
*/
@Test
public void test05(){
JedisPool pool=new JedisPool("10.9.118.11",6380);
//获取连接资源
Jedis jedis1 = pool.getResource();
jedis1.set("name","王翠花");
pool.returnResource(jedis1);
Jedis jedis2 = pool.getResource();
System.out.println(jedis2.get("name"));
pool.returnResource(jedis2);
}
ShardedJedisPool
如果说jedisPool管理了一批Jedis对象,ShardedJedisPool管理了一批ShardedJedis
/*
分片连接池
*/
@Test
public void test06(){
//提供分片节点信息
List<JedisShardInfo> infos=new ArrayList<>();
infos.add(new JedisShardInfo("10.9.118.11",6379));
infos.add(new JedisShardInfo("10.9.118.11",6380));
infos.add(new JedisShardInfo("10.9.118.11",6381));
//连接池配置属性
JedisPoolConfig config=new JedisPoolConfig();
//定义连接池的连接上限,最大空闲,最小空闲,不同的使用环境这些数值配置不一样的
config.setMaxIdle(8);
config.setMinIdle(2);
config.setMaxTotal(200);
//创建连接池
ShardedJedisPool pool=new ShardedJedisPool(config,infos);
ShardedJedis resource = pool.getResource();
//可以实现api操作,分布式计算
pool.returnResource(resource);
}