分布式系列

分布式缓存架构

分布式缓存 知识点

ehcache入门基础示例

jvm内置缓存ehcache
ehcache 的五种集群模式
ehcache + Redis 实现一级缓存、二级缓存
springboot整合ehcache+redis实现双缓存
分布式Redis缓存持久化
分布式Redis缓存水平拆分
分布式Redis缓存主从复制
分布式Redis缓存集群模式
分布式Redis缓存穿透、雪崩、效应
基于Redis实现分布式锁

为什么使用缓存、缓存用在什么地方?

使用缓存技术为了减轻服务器压力
怎么解决缓存 与 DB 的一致性
在 更新 和 删除 之后清除缓存、创建定时任务 定时健康检查。

单个 jvm 缓存简单实现
// 单个jvm 缓存简单实现
@Component
public class MapCache<K,V> {

    // 缓存容器使用 ConcurrentHashMap不需要考虑线程安全问题
    public final Map<K,V> concruent = new ConcurrentHashMap<>();
    
    public V get(K k){
        return concruent.get(k);
    }
    public void put(K k,V v){
        concruent.put(k,v);
    }
    public void remove(K k){
        concruent.remove(k);
    }
}
springboot 开启 ehcache 缓存

开启缓存 @EnableCaching
使用类注解 @CacheConfig 配置缓存基本信息 缓存名字等
使用方法注解 @Cacheable 方法执行完之后使用缓存
编写 xml 配置文件

使用 EHCache + Redis 实现分布式缓存

分布式缓存实现流程图
在这里插入图片描述
注:问题 过期时间如何控制(如何同步呢?)
一级缓存过期时间一定要比二级缓存要短。
定时任务,mq通知。

远程调用 RMI 与 RPC

RMI 与 RPC 理念相同
RMI 输入jvm与 jvm 之间的远程调用不能够 跨平台
RPC 也属于远程调用技术,底层基于 socket 实现,能够跨平台调用
EHCache 集群方式就是 使用的 RMI 的方式实现缓存 同步。

Redis 学习知识点

Redis 默认有16个库 默认连接 第 0 个库
什么是Redis ,Redis是一个非关系行的内存数据库,以key value 的方式进行储存
Redis 以单线程方式存储,既然是单线程那就能保证线程安全。
使用持久化机制可以保证数据高可用。

Redis的应用场景

1.令牌的生成
2.短信验证码
3.热点数据(减轻服务器查询数据库)
4.分布式锁(使用ZK或者Redis实现分布式锁)
5.网站的计数器(因为Redis是单线程,在高并发情况下,能保证全局count的一致性。)
6.使用Redis实现消息中间件(不推荐)发布订阅功能

Redis的五种常用数据类型

string 类型
hash 类型
list 类型
set 类型
zset 类型

springboot2.0 整合 Redis

在 pom.xml 文件中加入 Redis jar包

  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>

在 application.yml 中编写 Redis 配置

spring:
  redis:
    database: 0      		#表示 连接 第几个库
    host: 127.0.0.1	 	# ip 地址
    port: 6739 				# 端口号 默认是 6379
    password: 123456  # 连接密码
    timeout: 10000 		# 超时时间
    jedis:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 8
        min-idle: 0

Redis 工具分装

@Component
public class RedisUtils {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public void setString(String key, String value,Long time){
        stringRedisTemplate.opsForValue().set(key,value);
        // 设置 key 的过期时间
        stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
    }
}

Redis 主从复制

1.为什么主从复制
将服务器分为主服务器 和 从服务器,主服务器可以允许做读写的操作,从服务器只允许做读的操作。
2.主从复制应用场景
集群(多台服务器)、读写分离、日志备份、高可用
3.为什么读写分离
读和写分库连接,读一个库,写一个库。互不影响,增加整体吞吐量。
主服务器可以做写,从服务器可以做读。
读写分离产生的 数据同步问题 ,Redis 已经自带 解决。
注:在 Redis 中只能有一个主,可以有多个 从。
主从服务器 安装及配置
在这里插入图片描述

Redis 高可用哨兵机制

1.哨兵机制:心跳检测、监控、故障转移(选举策略)
2.

Redis 持久化机制 AOF 与 RDB

RDB: 以二进制文件形式,以某个时间点进行存储。
非实时,突然断电,数据可能有一部分丢失。
默认开启 RDB 方式。
AOF:实时日志记录的形式只会记录写的操作,效率低,会影响到整体性能。
AOF比较安全,在突然断电的情况下。

Redis 事务处理机制

Redis集群不支持事务 单节点的Redis 支持事务
事务操作 示例代码

public void setString(String key, String value,Long time){
    //开启事务权限
    stringRedisTemplate.setEnableTransactionSupport(true);
    try {
        // 开启事务 相当于  begin
        stringRedisTemplate.multi();
        stringRedisTemplate.opsForValue().set(key,value);
        stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
        // 提交事务
        stringRedisTemplate.exec();
    }catch (Exception e){
        // 回滚事务
        stringRedisTemplate.discard();
    }
}
Redis集群方案

Redis 3.0之前不支持 群集 3.0之后支持集群 ,两个版本差别很大
1.客户端分片 (mycat) 没有故障转移功能
2.主从复制 缺点:数据非常冗余,浪费内存
3.使用代理工具
4. Redis-cluster 集群 Redis 3.0 之后开始实现(Redis 强烈推荐的方式)

Redis 雪崩效应、穿透效应
Redis雪崩效应

雪崩效应的产生,在Redis 突然宕机后 ,所有的访问量直接访问数据库,导致数据库CPU和内存负载过高,甚至宕机。
解决方案:
1.分布式锁(本地锁) (不推荐使用)
2.使用消息中间件(靠谱)消息中间件方式可以解决高并发
3.做 一级和二级缓存(Redis + ehcache)
4.均摊分配 Redis Key 的失效时间 (重点)

Redis 穿透效应

产生原因:客户端随机生成不同的 key,在Redis中没有的 key ,数据库中也没有,导致一直发送jdbc连接,导致雪崩的出现。
解决方案:
1.网关判断客户端传入的key的规则,如果不符合数据库查询规则,直接返回为空。
2.如果没有的 key 数据库也查不到就直接向 Redis 中存放一份为 null 缓存,

基于 Redis 实现分布式锁

什么是分布式锁?
为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁。
解决分布式锁的核心思路:在多台服务器集群中,只保证一个 jvm 进行操作。
分布式锁解决方案
1.采用数据库
2.基于 Redis 实现分布式
3.基于 Zookeeper 实现分布式锁
实现原理:
多个客户端(jvm),使用setnx命令的方式,同时在Redis中创建相同的key,因为临时节点路径是保证唯一,只要谁能够创建节点成功,谁就能获取锁。没有创建成功的,就会进行等待,当释放锁的时候,采用事件通知给客户端(jvm)重新获取锁。
setnx命令也可以存入key,如果存入成功返回 1,如果存入的key已存在则返回 0。
Redis 和 Zookeeper 实现分布式锁的原理是相同的,不同点是 释放锁 Zookeeper 有事件通知
如何释放锁:
在执行完操作之后,删除对应的key ,每个对应的key 都有自己的过期时间,防止删除失败出现死锁现象。
Redis 实现分布式锁 有两个超时时间的问题
1.在获取锁之前的超时时间----在尝试获取锁的时候,如果在规定的时间内没有获取到,就直接放弃
2.在获取锁之后的超时时间----当获取锁成功之后,对应的key 有对应的失效时间,在规定时间内失效
分布式锁 实例代码

public class LockRedis {
	// redis线程池
	private JedisPool jedisPool;
	// 同时在redis上创建相同的一个key 相同key 名称
	private String redislockKey = "redis_lock";

	public LockRedis(JedisPool jedisPool) {
		this.jedisPool = jedisPool;
	}
	// redis 以key (redislockKey) 和value(随机不能够重复数字 锁的id)方式进行存储

	// redis实现分布式锁 有两个超时 时间问题
	/**
	 * 两个超时时间含义:<br>
	 * 1.在获取锁之前的超时时间----在尝试获取锁的时候,如果在规定的时间内还没有获取锁,直接放弃。<br>
	 * 2.在获取锁之后的超时时间---当获取锁成功之后,对应的key 有对应有效期,对应的key 在规定时间内进行失效
	 */
	
	// 基于redis实现分布式锁代码思路 核心方法 获取锁 、释放锁
	public String getRedisLock(Long acquireTimeout, Long timeOut) {
		Jedis conn = null;

		try {
			// 1.建立redis连接
			conn = jedisPool.getResource();
			// 2.定义 redis 对应key 的value值( uuid) 作用 释放锁 随机生成value
			String identifierValue = UUID.randomUUID().toString();

			// 3.定义在获取锁之后的超时时间
			int expireLock = (int) (timeOut / 1000);// 以秒为单位
			// 4.定义在获取锁之前的超时时间
			// 5.使用循环机制 如果没有获取到锁,要在规定acquireTimeout时间 保证重复进行尝试获取锁(乐观锁)
			// 使用循环方式重试的获取锁
			Long endTime = System.currentTimeMillis() + acquireTimeout;
			while (System.currentTimeMillis() < endTime) {
				// 获取锁
				// 6.使用setnx命令插入对应的redislockKey ,如果返回为1 成功获取锁
				if (conn.setnx(redislockKey, identifierValue) == 1) {
					// 设置对应key的有效期
					conn.expire(redislockKey, expireLock);
					return identifierValue;
				}
				// 为什么获取锁之后,还要设置锁的超时时间 目的是为了防止死锁
				// zookeeper实现分布式锁通过什么方式 防止死锁 设置session 有效期
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				conn.close();
			}
		}
		return null;
	}

	// 如果直接使用 conn.del(redislockKey); 保证对应是自己的创建redislockKey 删除对应自己的。
	// 释放redis锁
	public void unRedisLock(String identifierValue) {
		Jedis conn = null;
		// 1.建立redis连接
		conn = jedisPool.getResource();
		try {
			// 如果该锁的id 等于identifierValue 是同一把锁情况才可以删除
			if (conn.get(redislockKey).equals(identifierValue)) {
				System.out.println("释放锁..." + Thread.currentThread().getName() + ",identifierValue:" + identifierValue);
				conn.del(redislockKey);
			}
		} catch (Exception e){
		
		} finally {
			if (conn != null) {
				conn.close();
			}
		}
		// 释放锁有两种 key自动有有效期
		// 整个程序执行完毕情况下,删除对应key
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值