黑马---Redis实战篇

(一)、缓存

(1)、什么是缓存

缓存就是数据交换的缓冲区,称作Cache,是存储数据的临时地方,一般读写性能高 

黑马---Redis实战篇_第1张图片

黑马---Redis实战篇_第2张图片

(2)、添加Redis缓存

黑马---Redis实战篇_第3张图片黑马---Redis实战篇_第4张图片

 service层代码

@Override
	public Result getShopById(Long id) {
		String key = RedisConstants.CACHE_SHOP_KEY + id;
		//  TODO  1. 在缓存中查找数据
		String shopJson = stringRedisTemplate.opsForValue().get(key);
		//  TODO  2. 缓存中存在,直接将数据返回
		if (!StrUtil.isBlank(shopJson)) {
			Shop shop = JSONUtil.toBean(shopJson, Shop.class);
			return Result.ok(shop);
		}
		//  TODO  3. 缓存中不存在,在数据库中进行查找
		Shop shop = shopMapper.selectById(id);
		//  TODO  4. 数据库中不存在,返回【商铺信息不存在】
		if (shop == null) return Result.fail("商铺信息不存在");
		//  TODO  5. 数据库中存在,将数据写入到缓存中并且将数据返回给前端
		String shopJsonFromDataBase = JSONUtil.toJsonStr(shop);
		stringRedisTemplate.opsForValue().set(key,shopJsonFromDataBase);
		return Result.ok(shop);
	}

(3)、缓存更新策略

黑马---Redis实战篇_第5张图片

主动更新策略

黑马---Redis实战篇_第6张图片

黑马---Redis实战篇_第7张图片

黑马---Redis实战篇_第8张图片

黑马---Redis实战篇_第9张图片

案列:  给查询商铺的缓存添加超时剔除和主动更新策略

(4)、缓存穿透

是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库

缓存穿透解决方案

1. 缓存空对象

                优点: 实现简单,维护方便

                缺点:      额外内存消耗(可以设置TTL)

                              可能造成短期的不一致  

2.布隆过滤

                是一个算法

                优点:  内存占用较少,没有多余key

                缺点:实现复杂,存在误判可能

黑马---Redis实战篇_第10张图片

 --------------------------------------------------------

黑马---Redis实战篇_第11张图片

在数据库中查找不存在的时候,将商铺信息写成 "" 写入redis中

并且在redis查找的时候,先判断是否为空,不为空,判断是否为空字符串

public Result getShopById(Long id) {
		String key = RedisConstants.CACHE_SHOP_KEY + id;
		//  TODO  1. 在缓存中查找数据
		String shopJson = stringRedisTemplate.opsForValue().get(key);
		//  TODO  2. 缓存中存在,直接将数据返回
		if (!StrUtil.isBlank(shopJson)) {
			Shop shop = JSONUtil.toBean(shopJson, Shop.class);
			return Result.ok(shop);
		}
		//  现在不在数据库中进行查找,而是继续判断查找出来的信息是否为空字符串
		if (shopJson != null){
			return Result.fail("店铺信息不存在");
		}
		/**
		 * 这个逻辑目前是这样的:
		 *          存入缓存的只有:    1.{"key":value}   2.""
		 * 由于已经在第一个判断语句中判断了是否为空字符串
		 */

		//  TODO  3. 缓存中不存在,在数据库中进行查找
		Shop shop = shopMapper.selectById(id);
		//现在,将数据库中查出来的数据,如果为空的话,那么将【""】存入缓存中,过期时间为2分钟
		stringRedisTemplate.opsForValue().set(key,"",RedisConstants.CACHE_NULL_TTL);
		//  TODO  4. 数据库中不存在,返回【商铺信息不存在】
		if (shop == null) {
			//现在,将数据库中查出来的数据,如果为空的话,那么将【""】存入缓存中,过期时间为2分钟
			stringRedisTemplate.opsForValue().set(key,"",RedisConstants.CACHE_NULL_TTL);
			return Result.fail("商铺信息不存在");
		}
		//  TODO  5. 数据库中存在,将数据写入到缓存中并且将数据返回给前端
		String shopJsonFromDataBase = JSONUtil.toJsonStr(shop);
		stringRedisTemplate.opsForValue().set(key,shopJsonFromDataBase, RedisConstants.CACHE_SHOP_TTL);
		return Result.ok(shop);
	}

黑马---Redis实战篇_第12张图片

(5)、缓存雪崩

是指在同一时间段大量的缓存key同时失效或者redis服务器宕机,导致大量请求到达数据库,带来巨大压力

黑马---Redis实战篇_第13张图片

 解决方案:

黑马---Redis实战篇_第14张图片

 (6)缓存击穿

缓存击穿问题也叫热点key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效,无数的请求访问会在瞬间给数据库带来巨大冲击

解决方案:

 黑马---Redis实战篇_第15张图片

黑马---Redis实战篇_第16张图片

案例:

 (1)基于互斥锁方式解决缓存击穿问题

黑马---Redis实战篇_第17张图片

 黑马---Redis实战篇_第18张图片

代码

public Result queryThroughMultex(Long id) {
	String key = RedisConstants.CACHE_SHOP_KEY + id;
	//  TODO  1. 在缓存中查找数据
	String shopJson = stringRedisTemplate.opsForValue().get(key);
	//  TODO  2. 缓存中存在,直接将数据返回
	if (!StrUtil.isBlank(shopJson)) {
		Shop shop = JSONUtil.toBean(shopJson, Shop.class);
		return null;
	}
	//  现在不在数据库中进行查找,而是继续判断查找出来的信息是否为空字符串
	if (shopJson != null){
		return null;
	}
	/**
	 * 以下进行缓存重建
	 */
	//  1) 获取锁
	//	2) 判断是否获取成功
	//  3) 成功   去数据库中进行查找
	//     失败   【休眠并且进行重试】 <----important
	Shop shop = null;
	String lockKey  = "lock:shop:" + id;
	try {
		if (!getLock(lockKey)) {   //  获取锁失败
			Thread.sleep(50);
			return queryThroughMultex(id);
		}
		//  ======================================================================================
		//  TODO  3. 缓存中不存在,在数据库中进行查找
		shop = shopMapper.selectById(id);
		//现在,将数据库中查出来的数据,如果为空的话,那么将【""】存入缓存中,过期时间为2分钟
		stringRedisTemplate.opsForValue().set(key,"",RedisConstants.CACHE_NULL_TTL);
		//  TODO  4. 数据库中不存在,返回【商铺信息不存在】
		if (shop == null) {
			//现在,将数据库中查出来的数据,如果为空的话,那么将【""】存入缓存中,过期时间为2分钟
			stringRedisTemplate.opsForValue().set(key,"",RedisConstants.CACHE_NULL_TTL);
			return Result.fail("商铺信息不存在");
		}
		//  TODO  5. 数据库中存在,将数据写入到缓存中并且将数据返回给前端
		String shopJsonFromDataBase = JSONUtil.toJsonStr(shop);
		stringRedisTemplate.opsForValue().set(key,shopJsonFromDataBase, RedisConstants.CACHE_SHOP_TTL);
	} catch (InterruptedException e) {
		throw new RuntimeException(e);
	}finally {
		//  释放锁
		deleteLock(lockKey);
	}
	return Result.ok(shop);
}

(2) 、基于逻辑过期方式解决缓存击穿问题

需求: 修改根据id查询商铺的业务,基于逻辑过期方式来解决缓存击穿问题

黑马---Redis实战篇_第19张图片

 

这里的线程使用线程池

(7)、缓存工具封装

你可能感兴趣的:(redis,缓存,数据库)