网站建设培训多少钱,新国标小区网络建设,凡科做的网站如何绑定域名,上海企业网站建站模板文章目录 项目介绍1 短信登录2 商户查询缓存2.1 什么是缓存2.1.1 缓存的应用场景2.1.2 为什么要使用缓存2.1.3 Web应用中缓存的作用2.1.4 Web应用中缓存的成本 附#xff1a;缓存封装工具类 #x1f64a; 前言#xff1a;本文章为瑞_系列专栏之《Redis》的实战篇的商户查询缓… 文章目录 项目介绍1 短信登录2 商户查询缓存2.1 什么是缓存2.1.1 缓存的应用场景2.1.2 为什么要使用缓存2.1.3 Web应用中缓存的作用2.1.4 Web应用中缓存的成本 附缓存封装工具类 前言本文章为瑞_系列专栏之《Redis》的实战篇的商户查询缓存章节的什么是缓存小节。由于博主是从B站黑马程序员的《Redis》学习其相关知识所以本系列专栏主要是针对该课程进行笔记总结和拓展文中的部分原理及图解等也是来源于黑马提供的资料特此注明。本文仅供大家交流、学习及研究使用禁止用于商业用途违者必究 主机操作系统Windows10VMware版本 VMware Workstation 16.2.4Linux版本CentOS 7 64位远程连接工具MobaXterm_Personal_23.2Redis版本redis-6.2.6.tar.gzRedis客户端resp-2022.2.0.0MySQL版本8.0.295.7均可Navicat Premium15.0.28JDK1.8 相关链接《瑞_VMware虚拟机安装Linux纯净版(含卸载图文超详细)》 相关链接《瑞_Redis_短信登录》 项目介绍 本文基于B站黑马程序员的《黑马点评》项目进行学习笔记总结和拓展项目的相关资源和课程视频可以到B站获取。 博主提供的该项目的相关资源的某度网盘链接https://pan.baidu.com/s/1N-yr86yTRi3LbQdAL7prEQ?pwdq0ry 本项目具有以下功能点本文为《商户查询缓存》篇的什么是缓存小节 短信登录 这一块我们会使用redis共享session来实现 商户查询缓存 通过本章节我们会理解缓存击穿缓存穿透缓存雪崩等问题让小伙伴的对于这些概念的理解不仅仅是停留在概念上更是能在代码中看到对应的内容 优惠卷秒杀 通过本章节我们可以学会Redis的计数器功能 结合Lua完成高性能的redis操作同时学会Redis分布式锁的原理包括Redis的三种消息队列 附近的商户 我们利用Redis的GEOHash来完成对于地理坐标的操作 UV统计 主要是使用Redis来完成统计功能 用户签到 使用Redis的BitMap数据统计功能 好友关注 基于Set集合的关注、取消关注共同关注等等功能这一块知识咱们之前就讲过这次我们在项目中来使用一下 达人探店 基于List来完成点赞列表的操作同时基于SortedSet来完成点赞的排行榜功能 由于该项目主要是为了学习Redis所以不会设计为微服务架构简化代码复杂度所以采用前后端分离的单体架构 说明 手机或者app端发起请求请求我们的nginx服务器nginx基于七层模型走的事HTTP协议可以实现基于Lua直接绕开tomcat访问redis也可以作为静态资源服务器轻松扛下上万并发 负载均衡到下游tomcat服务器打散流量我们都知道一台4核8G的tomcat在优化和处理简单业务的加持下大不了就处理1000左右的并发 经过nginx的负载均衡分流后利用集群支撑起整个项目同时nginx在部署了前端项目后更是可以做到动静分离进一步降低tomcat服务的压力这些功能都得靠nginx起作用所以nginx是整个项目中重要的一环。 在 tomcat 支撑起并发流量后我们如果让 tomcat 直接去访问 Mysql 根据经验 Mysql 企业级服务器只要上点并发一般是16或32 核心cpu32 或64G内存像企业级mysql加上固态硬盘能够支撑的并发大概就是4000起~7000左右上万并发 瞬间就会让Mysql服务器的cpu硬盘全部打满容易崩溃所以我们在高并发场景下会选择使用mysql集群同时为了进一步降低Mysql的压力同时增加访问的性能我们也会加入Redis同时使用Redis集群使得Redis对外提供更好的服务。 1 短信登录 瑞见《瑞_Redis_短信登录》 2 商户查询缓存 本章节基于hm-dianping【1.3Redis代替session的业务流程】的代码需要请自取
链接https://pan.baidu.com/s/1DomlH_sXyAkrciXk8-bWww?pwdz6lu
提取码z6lu2.1 什么是缓存 缓存就是数据交换的缓冲区称作Cache [ kæʃ ] 是存贮数据的临时地方一般读写性能较高。 2.1.1 缓存的应用场景 缓存的应用场景浏览器缓存、应用层缓存如Redis、数据库缓存如索引、CPU多级缓存、磁盘缓存 浏览器缓存主要是存在于浏览器端的缓存 应用层缓存可以分为tomcat本地缓存比如之前提到的map或者是使用redis作为缓存 数据库缓存在数据库中有一片空间是 buffer pool增改查数据都会先加载到mysql的缓存中 CPU缓存当代计算机最大的问题是 cpu性能提升了但内存读写速度没有跟上所以为了适应当下的情况增加了cpu的L1L2L3级的缓存 CPU多级缓存的诞生在计算机中主要的构造为CPU、内存、磁盘。由于CPU的运算能力随着科技的发展其计算能力已经远远的超过内存和磁盘的读写数据的能力但是CPU所做的任何运算都需要从内存或者磁盘中读到数据再放到自己的寄存器里才可以进行运算。正是由于这种数据读写的能力远远低于CPU的运算能力导致计算机性能受到瓶颈。所以人们在CPU的内部添加了缓存即CPU会把经常需要读写的数据放入CPU缓存中当进行高速运算的时候就不需要每次都去内存或磁盘中读取数据再运算而是直接从缓存中获取数据直接运算这样就可以充分释放CPU的运算能力。 缓存的常见使用示例
例1:Static final ConcurrentHashMapK,V map new ConcurrentHashMap(); 本地用于高并发例2:static final CacheK,V USER_CACHE CacheBuilder.newBuilder().build(); 用于redis等缓存例3:Static final MapK,V map new HashMap(); 本地缓存由于其被Static修饰所以随着类的加载而被加载到内存之中作为本地缓存由于其又被 final 修饰所以其引用 (例3:map) 和对象 (例3:new HashMap()) 之间的关系是固定的不能改变因此不用担心赋值()导致缓存失效。 2.1.2 为什么要使用缓存 一句话因为速度快好用 缓存数据存储于代码中而代码运行在内存中内存的读写性能远高于磁盘缓存可以大大降低用户访问并发量带来的服务器读写压力 实际开发过程中企业的数据量少则几十万多则几千万这么大的数据量如果没有缓存来作为避震器系统是几乎是撑不住的所以企业会大量运用到缓存技术。
2.1.3 Web应用中缓存的作用 缓存的作用 1️⃣ 降低后端负载请求先进入缓存中查找数据若缓存中不存在再将请求向数据库发送大大降低后端数据库压力。 2️⃣ 提高读写效率降低响应时间。
2.1.4 Web应用中缓存的成本 缓存的成本 1️⃣ 数据一致性成本数据本身只保存在数据库现在将数据缓存了一份放到了内存中如Redis如果数据库中的数据发生了变化而缓存中的数据仍然是旧数据由于请求先进入缓存中查找数据就会造成数据的不一致性。 2️⃣ 代码维护成本由于要保证数据一致性自然会增加业务编码且会出现缓存穿透、雪崩、击穿等问题会大幅度提高代码复杂度。 3️⃣ 运维成本为了避免缓存雪崩或缓存的高可用需要搭建成缓存集群模式提高了运维成本。 瑞后续内容请关注瑞_系列专栏之《Redis》 附缓存封装工具类
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;/*** 缓存工具类* br* Component / 实现 bean 的注入*/
Component
SuppressWarnings(all)
public final class RedisUtil {Resourceprivate RedisTemplateString, Object redisTemplate;/*** 时间有效期 左区间*/private final static int TIME_EXPIRE_LEFT 300;/*** 时间有效期 右区间*/private final static int TIME_EXPIRE_RIGHT 3000;// common/*** 指定缓存失效时间* br* 防止缓存雪崩。* 过期时间 基础时间 随机时间让缓存慢慢过期避免瞬间全部过期对 DB 产生过大压力** param key 键* param time 时间(秒)*/public boolean expire(String key, long time) {try {if (time 0) {redisTemplate.expire(key, time MathUtil.getIntRandom(TIME_EXPIRE_LEFT, TIME_EXPIRE_RIGHT), TimeUnit.SECONDS);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据key 获取过期时间** param key 键 不能为null* return 时间(秒) 返回0代表为永久有效*/public long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 判断key是否存在** param key 键* return true 存在 false不存在*/public boolean hasKey(String key) {try {boolean result redisTemplate.hasKey(key);return result;} catch (Exception e) {return false;}}/*** 删除缓存** param key 可变长参数可以不传但是不对无参进行操作*/public void del(String... key) {if (key ! null key.length 0) {if (key.length 1) {redisTemplate.delete(key[0]);} else {redisTemplate.delete((CollectionString) CollectionUtils.arrayToList(key));}}}// String/*** 普通缓存获取** param key 键* return 值*/public Object get(String key) {return key null ? null : redisTemplate.opsForValue().get(key);}/*** 普通缓存放入** param key 键* param value 值* return true成功 false失败*/public boolean set(String key, Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 普通缓存放入并设置时间** param key 键* param value 值* param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期* return true成功 false 失败*/public boolean set(String key, Object value, long time) {try {if (time 0) {redisTemplate.opsForValue().set(key, value, time MathUtil.getIntRandom(60, 300), TimeUnit.SECONDS);} else {set(key, value);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 递增** param key 键* param delta 要增加几(大于0)*/public long incr(String key, long delta) {if (delta 0) {throw new RuntimeException(递增因子必须大于0);}return redisTemplate.opsForValue().increment(key, delta);}/*** 递减** param key 键* param delta 要减少几(小于0)*/public long decr(String key, long delta) {if (delta 0) {throw new RuntimeException(递减因子必须大于0);}return redisTemplate.opsForValue().increment(key, -delta);}// Map/*** HashGet** param key 键 不能为null* param item 项 不能为null*/public Object hget(String key, String item) {return redisTemplate.opsForHash().get(key, item);}/*** 获取hashKey对应的所有键值** param key 键* return 对应的多个键值*/public MapObject, Object hmget(String key) {return redisTemplate.opsForHash().entries(key);}/*** HashSet** param key 键* param map 对应多个键值*/public boolean hmset(String key, MapString, Object map) {try {redisTemplate.opsForHash().putAll(key, map);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** HashSet 并设置时间** param key 键* param map 对应多个键值* param time 时间(秒)* return true成功 false失败*/public boolean hmset(String key, MapString, Object map, long time) {try {redisTemplate.opsForHash().putAll(key, map);if (time 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** param key 键* param item 项* param value 值* return true 成功 false失败*/public boolean hset(String key, String item, Object value) {try {redisTemplate.opsForHash().put(key, item, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** param key 键* param item 项* param value 值* param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间* return true 成功 false失败*/public boolean hset(String key, String item, Object value, long time) {try {redisTemplate.opsForHash().put(key, item, value);if (time 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除hash表中的值** param key 键 不能为null* param item 项 可以使多个 不能为null*/public void hdel(String key, Object... item) {redisTemplate.opsForHash().delete(key, item);}/*** 判断hash表中是否有该项的值** param key 键 不能为null* param item 项 不能为null* return true 存在 false不存在*/public boolean hHasKey(String key, String item) {return redisTemplate.opsForHash().hasKey(key, item);}/*** hash递增 如果不存在,就会创建一个 并把新增后的值返回** param key 键* param item 项* param by 要增加几(大于0)*/public double hincr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, by);}/*** hash递减** param key 键* param item 项* param by 要减少记(小于0)*/public double hdecr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, -by);}// set/*** 根据key获取Set中的所有值** param key 键*/public SetObject sGet(String key) {try {return redisTemplate.opsForSet().members(key);} catch (Exception e) {e.printStackTrace();return null;}}/*** 根据value从一个set中查询,是否存在** param key 键* param value 值* return true 存在 false不存在*/public boolean sHasKey(String key, Object value) {try {return redisTemplate.opsForSet().isMember(key, value);} catch (Exception e) {e.printStackTrace();return false;}}/*** 将数据放入set缓存** param key 键* param values 值 可以是多个* return 成功个数*/public long sSet(String key, Object... values) {try {return redisTemplate.opsForSet().add(key, values);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 将set数据放入缓存** param key 键* param time 时间(秒)* param values 值 可以是多个* return 成功个数*/public long sSetAndTime(String key, long time, Object... values) {try {Long count redisTemplate.opsForSet().add(key, values);if (time 0)expire(key, time);return count;} catch (Exception e) {e.printStackTrace();return 0;}}/*** 获取set缓存的长度** param key 键*/public long sGetSetSize(String key) {try {return redisTemplate.opsForSet().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 移除值为value的** param key 键* param values 值 可以是多个* return 移除的个数*/public long setRemove(String key, Object... values) {try {Long count redisTemplate.opsForSet().remove(key, values);return count;} catch (Exception e) {e.printStackTrace();return 0;}}// list/*** 获取list缓存的内容** param key 键* param start 开始* param end 结束 0 到 -1代表所有值*/public ListObject lGet(String key, long start, long end) {try {return redisTemplate.opsForList().range(key, start, end);} catch (Exception e) {e.printStackTrace();return null;}}/*** 获取list缓存的长度** param key 键*/public long lGetListSize(String key) {try {return redisTemplate.opsForList().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 通过索引 获取list中的值** param key 键* param index 索引 index0时 0 表头1 第二个元素依次类推index0时-1表尾-2倒数第二个元素依次类推*/public Object lGetIndex(String key, long index) {try {return redisTemplate.opsForList().index(key, index);} catch (Exception e) {e.printStackTrace();return null;}}/*** 将list放入缓存** param key 键* param value 值*/public boolean lSet(String key, Object value) {try {redisTemplate.opsForList().rightPush(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** param key 键* param value 值* param time 时间(秒)*/public boolean lSet(String key, Object value, long time) {try {redisTemplate.opsForList().rightPush(key, value);if (time 0)expire(key, time);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** param key 键* param value 值* return*/public boolean lSet(String key, ListObject value) {try {redisTemplate.opsForList().rightPushAll(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** param key 键* param value 值* param time 时间(秒)* return*/public boolean lSet(String key, ListObject value, long time) {try {redisTemplate.opsForList().rightPushAll(key, value);if (time 0)expire(key, time);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据索引修改list中的某条数据** param key 键* param index 索引* param value 值* return*/public boolean lUpdateIndex(String key, long index, Object value) {try {redisTemplate.opsForList().set(key, index, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 移除N个值为value** param key 键* param count 移除多少个* param value 值* return 移除的个数*/public long lRemove(String key, long count, Object value) {try {Long remove redisTemplate.opsForList().remove(key, count, value);return remove;} catch (Exception e) {e.printStackTrace();return 0;}}} 本文是博主的粗浅理解可能存在一些错误或不完善之处如有遗漏或错误欢迎各位补充谢谢 如果觉得这篇文章对您有所帮助的话请动动小手点波关注你的点赞收藏⭐️转发评论都是对博主最好的支持~