1.Redis介绍
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询等。
1.1为什么需要使用缓存
使用缓存可以有效的降低用户访问物理设备的频次,有效的减少并发的压力。保护后端真实的服务器。
1.2Redis在Linux中的常用命令
1.启动reids命令 redis-server redis.conf
2.检索redis服务 ps -ef |grep redis
3.进入redis客户端 redis-cli -p 6379(端口号)
4.关闭redis命令 redis-cli -p 6379 shutdown
kill -9 17179
2.Redis入门案例
2.1引入jar包
spring提供了redis的依赖jar包
<!--spring整合redis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
2.2编辑测试类
@Test
public void testString01() {
//1.创建jedis对象
Jedis jedis = new Jedis("192.168.126.129", 6379);
//2.操作redis
jedis.set("a", "redis入门案例");
String value = jedis.get("a");
System.out.println(value);
}
3.Sping整合Redis
思路:将jedis对象交给spring容器进行管理.之后哪里需要直接注入即可.
3.1编辑redis.properties文件
通过编辑redis.properties文件,动态指定IP地址以及端口号
redis.host=192.168.126.129
redis.port=6379
3.2编辑redis配置类
动态获取IP地址和端口号,将Jedis交给Spring进行管理
修改Bean配置,对象为Jedis
@Configuration //我是一个配置类 一般都会与@Bean联用
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private Integer port;
//将返回值的结果交给spring容器进行管理,如果以后想要使用该对象则可以直接注入.
@Bean
public Jedis jedis() {
return new Jedis(host, port);
}
}
3.3 封装ObjectMapperUtil
说明:定义API,提供将对象转化为JSON和将json串转化为对象的方法
public class ObjectMapperUtil {
//json与对象的转化 优化异常处理
private static final ObjectMapper MAPPER = new ObjectMapper();
//1.将对象转化为JSON
public static String toJSON(Object target) {
if(target == null) {
throw new NullPointerException("taget数据为null");
}
try {
return MAPPER.writeValueAsString(target);
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException(e); //如果转化过程中有问题则直接抛出异常
}
}
//2. 将json串转化为对象 用户传递什么样的类型,就返回什么样的对象!!!
// <T> 定义了一个泛型对象 代表任意类型
public static <T> T toObject(String json,Class<T> targetClass) {
if(StringUtils.isEmpty(json) || targetClass == null) {
throw new NullPointerException("参数不能为null");
}
try {
return MAPPER.readValue(json, targetClass);
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
3.4编辑Service业务代码
/**
* 通过缓存的方式查询数据库.
* 1).定义key
* 2).根据key查询redis.
*/
@Autowired(required = false)
private Jedis jedis;
@SuppressWarnings("unchecked") //告诉编译器忽略 unchecked 警告信息
@Override
public List<EasyUITree> findItemCatListByRedis(Long parentId) {
List<EasyUITree> itemCatList;
String key = "ItemCatList::"+parentId;//定义唯一的key
if (jedis.exists(key)){ //判断是否存在缓存
String s = jedis.get(key); //如果存在则直接取值
List<EasyUITree> easyUITrees = new ArrayList<>();//将取到的json串转换成List对象
itemCatList = ObjectMapperUtil.toObject(s, easyUITrees.getClass());
}else{
itemCatList = findItemCatList(parentId);//如果不存在缓存,则调用原来的方法
String json = ObjectMapperUtil.toJSON(itemCatList);//将取到的List对象转换成json串.
jedis.set(key, json);//存入缓存
}
return itemCatList;
}
4.Redis分片原理
4.1 为什么使用分片
1).说明: 虽然redis可以扩展内存空间的大小.但是如果需要存储海量的数据一味的扩大内存,其实效率不高.
2).分片介绍: 准备多台redis,共同为用户提供缓存服务.在保证效率的前提下,实现了内存的扩容.
用户在使用分片机制时,将多台redis当做1台使用.
4.2 Spring整合Redis(分片)入门案例
修改Bean配置,对象换为ShardedJedis
public class TestRedisShards {
@Test
public void test01() {
//1.准备list集合 之后添加节点信息
List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
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"));
}
}
说明:分片API:ShardedJedis对象需要传入LIst集合,集合中需要包含节点信息JedisShardInfo.
问题思考:key=shards 存储到了哪台redis中? 如何存储的?
4.3Redis分片原理
4.3.1一致性hash算法介绍
一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题。
4.3.1一致性hash算法原理
说明:
- 一圈表示所有的取值区间,8位16进制数,共有 2^32可能性.
- 每个node代表一个节点,通过对ip和post的hash计算,得出节点分布在取值区间的位置.
- 在存储数据时,对数据的key同样进行hash运算,得出其在区间内的位置,通过顺时针的方式,找到离自己最近的
node进行存储.
当节点数量发生变化时,数据的迁移过程如下图所示
4.3.1一致性hash算法特性
一致性哈希算法是在哈希算法基础上提出的,在动态变化的分布式环境中,哈希算法应该满足的几个条件:平衡性、单调性和分散性。
① 平衡性(均衡性)是指hash的结果应该平均分配到各个节点,这样从算法上解决了负载均衡问题。 利用虚拟节点实现数据平衡 (平衡数据不能做到绝对平均,只能是相对的)
② 单调性是指在新增或者删减节点时,不影响系统正常运行 . 可以实现动态的数据迁移.
③ 分散性是指数据应该分散地存放在分布式集群中的各个节点(节点自己可以有备份),不必每个节点都存储所有的数据 ,鸡蛋不要放到一个篮子里。
5.Redis集群整合
修改Bean配置,对象换JedisCluster
/* Redis集群,即多台Redis的配置 */
@Value("${redis.nodes}")
private String nodes; //node,node,node是字符串的节点
@Bean //实例化集群的JedisCluste对象之后交给Spring容器管理
public JedisCluster jedisCluster(){
Set<HostAndPort> set = new HashSet<>(); //创建集合存储集群里的每台Redis地址和端口
String[] nodeArray = nodes.split(","); //字符串按逗号分隔,得到字符串数组对象
for(String node : nodeArray){ //遍历数组host:port
String[] nodeTemp = node.split(":");
String host = nodeTemp[0]; //按数组下标取值
int port = Integer.parseInt(nodeTemp[1]); //字符类型转换为整数类型
HostAndPort hostAndPort = new HostAndPort(host, port); //创建每一台的地址和端口
set.add(hostAndPort);
}
return new JedisCluster(set); //返回集群对象
}