当前位置: 首页 > news >正文

湖南网站建设磐石网络口碑好seo教育

湖南网站建设磐石网络口碑好,seo教育,品牌网站设计工作室,wordpress 有什么用前言 es我们已经在前文中有所了解#xff0c;和es有相似功能的是Redis#xff0c;他们都不是纯粹的数据库。两者使用场景也是存在一定的差异的#xff0c;本文目的并不重点说明他们之间的差异#xff0c;但会简要说明#xff0c;重点还是在对Redis的了解和学习上。学完本…前言 es我们已经在前文中有所了解和es有相似功能的是Redis他们都不是纯粹的数据库。两者使用场景也是存在一定的差异的本文目的并不重点说明他们之间的差异但会简要说明重点还是在对Redis的了解和学习上。学完本篇你将了解Redis的特点和作用掌握Redis的基础用法这将有助于你在后续的项目中更好的使用Redis。建议大家都动手和博主一起实操莫要养成眼高手低的毛病下面让我们提起精神一起开始这场Redis盛宴吧。 Redis 什么是Redis Redis全名Remote Dictionary Server即远程字典服务是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库并提供多种语言的API。 其官网Redis 虽然Redis也是数据库但又有别于我们所知的mysql等关系型数据库Redis是一款基于内存的NoSQL数据存储服务也就是非关系型的数据库这点要搞搞清楚。 为什么使用Redis 相信大家在学习SQL的时候都有过这样的经历往数据库插入50w条数据然后去条件检索某些数据效率如何大家心里多少还是有点x数的试想这种暴力型操作要是出现在我们常用的一些网站和应用上且每次请求到这么做那该是何等的疯狂 说到这里其实还没有说出Redis的主要应用场景每每想到这个总是会跳出另一个东西ES。算了咱们看下面的对比吧有对比才有伤害啊 Redis和ES的区别 Redis使用场景 因为Redis是基于内存运行的说起内存你应该知道其运行效率远高于和硬盘的交互。这也就导致了Redis运行效率非常高。 Redis同样支持将数据存储在硬盘上支持主从和分布式使用但在事务上有着严重不足所以在关系比较复杂的地方就不适合使用Redis但却可以配合关系型数据库做缓存也就是通过复杂SQL查找到数据缓存在Redis。 此类缓存数据我准备用一个绝对一些的词必须是稳定型通用型高频次数据比如类别商品信息资讯信息等。如果每次请求都会变每个人又都不一样的数据不太建议存储在Redis不是不行只是不建议因为会占用大量的内存造成数据的冗余还不利于做数据同步。 ES使用场景 ES是非关系型数据库我们通常说他是一个引擎实时搜索引擎他是把数据按照一定的规律存储起来达到比关系型数据库查询效率更高的目的。 ES扩展容易前文中曾使用了ik插件ES同样支持主从由于其存储的数据结构特点所以其查询效率非常高在微服务这种大数据形态下的表现尤为优秀可以快速实现数据的整合对于日志和数据分析非常友好对于实时状态的高也并发有着极强的适应能力且延迟也很低。 想了解ES的童鞋可以点击下面链接前往查看Java开发 - Elasticsearch初体验 Redis安装 由于博主是Mac电脑这里就以Mac为例Windows没试过Windows的童鞋可自行百度安装。 打开终端输入brew install redis测试安装成功输入redis-server看到Redis 的启动日志则说明安装成功通过Ctrl-C可停止此redis使用 launchd 启动Redisbrew services start redis 暂停Redisbrew services stop redis                                                                                                               查看Redis信息brew services info redis 其实博主觉得这么做挺麻烦的有个东西叫Docker Desktop 直接安装这个Mac电脑省去了安装虚拟机的麻烦直接在此软件内安装各种服务不要太爽 数据库nacossenta等都可以在这里安装并且一键启动推荐大家去装一个就不用每一个东西都要自己装太麻烦了。 还有个Redis的客户端也推荐大家装一下 此软件在Mac上App Store是收费的推荐安装方式如下 brew install --cask another-redis-desktop-manager 安装后就可以找到启动图标了Windows可在网上自行搜索其工作页面如下 可看到目前我们是启动状态客户端连接数是1。 最好弄好了这些东西后再来跟着学下去这种可视化工具可以在我们调用接口时看到存储在Redis中的数据非常方便。 Redis缓存 缓存淘汰 Redis帮助我们解决了一些三高的问题但在访问量非常大的时候Redis要在同一时间保存大量的数据但Redis内存并不是无限的一旦内存占满可能会发生什么降速阻塞宕机都有可能所以在连续不断的存入新数据的同时还要将不使用的老数据及时的从内存中删除这就需要一个淘汰策略。 好在Redis提供了这种机制我们看看有哪些淘汰策略 noeviction返回错误**(默认)** allkeys-random所有数据中随机删除数据 volatile-random所有过期时间的数据库中随机删除数据 volatile-ttl删除剩余有效时间最少的数据 allkeys-lru所有数据中删除上次使用时间最久的数据 volatile-lru所有过期时间的数据中删除上次使用时间最久的数据 allkeys-lfu所有数据中删除使用频率最少的 volatile-lfu所有过期时间的数据中删除使用频率最少的 通过合理的选择以上参数配置Redis可以有效解决这个问题但也需要时刻监测Redis内存情况现在的云服务做得都很好可以提前预警通知。 缓存穿透 我们在使用Redis时将数据库中查询出来的数据保存在Redis中但Redis有自己的淘汰策略所以这些数据并不会无限期保存。 正常来说访问的请求会先去Redis中拿数据拿不到才会去数据库中查找再将查到的数据存储到Redis中一旦这样的请求数量非常多的时候数据库的压力就会变大我们可以认为其表现的现象即为Redis失效没有工作当然算是失效了而这种情况我们称之为缓存穿透。 开发中当然要避免缓存穿透简单点可以将查询回来为空的数据在Redis中存为null防止Redis被反复穿透但这也有缺点比如反复更换查询关键字反复穿透依然存在当然这只是特例虽然实际中发生的概率不会太高但还是要防范利用此情况攻击服务器的可能。 最好的做法是通过增加布隆过滤器来解决此问题在业务进入时提前判断用户查询的信息是否存在于数据库中如果没有直接返回不再走完整的路径。 缓存击穿 缓存穿透和缓存击穿很类似我们正常的流程是先访问RedisRedis没有就去数据库查询这种情况数据库是可以查到数据的此种现象就叫击穿而少量的击穿并不是问题。 缓存雪崩 上面的击穿在同一时间大量发生就变成了雪崩数据库短时间内出现很多新的查询请求就会发生性能问题。 这是由于Redis缓存淘汰策略把过期的数据大批量清空导致的它本身不算异常只是我们要避免同一时间大量的过期情况出现所以在设置过期时间时在基础时间上增加10分钟或30分钟以内的随机时间来解决这个问题时间你可以自己定。 Redis持久化 存储特点 Redis是在内存中运行的这和我们所有的软件都是一样的内存可以保存但Redis保存的数据却并不是在内存上试想我们的电脑手机关机后再打开还能恢复打开时的样子吗这自然是不能的。 所以为了解决断电重启等问题Redis支持了持久化将需要保存的数据保存在服务器硬盘上。 针对以上硬盘保存数据的特点Redis在重新启动后恢复数据的方式有两种我们来看看是哪两种。 RDB RDB全称Redis Database Backup中文名叫数据库快照它可以将Redis数据库数据转化为二进制数据保存在硬盘上生成一个dump.rdb的文件想使用此恢复模式需要提前在Redis安装程序的配置文件中进行配置才能生效。 基于此模式由于是整体Redis数据的二进制格式所以数据恢复是整体恢复的非常方便。但也因此存在了一个大文件的通病读写效率不高。快照的备份不能实时进行所以断电重启恢复只能恢复最后一次生成的rdb文件数据。可能会造成短时间的数据丢失。 AOF AOF全称Append Only File它的策略不是缓存数据而是将所有命令日志备份下来在数据丢失后可以根据运行过的日志恢复为断电前的状态注意一点这种保存日志的策略也不是实时的数据量比较大时会分批分次进行缓存。 实际中我们一般设置1s发送一次日志断电最多丢失1s数据。为了降低日志对内存的占用AOF支持AOF rewrite也就是说如果你是删除数据那完全没有留日志的必要但默认时有日志的所以可以将这些删除操作的日志删除。 存储原理 存储原理博主简单给大家说说想要深入了解的推荐这篇博客Redis存储原理深入剖析 - 墨天轮 也可以自行查找。 Redis将内存划分为16384个槽类似哈希槽将要存储的数据的key通过CRC16算法处理得到一个0~16383之间的值然后将这条数据存储到对应的槽中下次查找的时候也是通过CRC16算法处理过的数字去对应槽中查找当前key是否存在因为有可能直接一次就找到对应key所以这种存储查找方式效率非常高。这也是一种散列算法和数据库主键查找的原理很类似。推荐读一下博主这篇博客Java开发 - 数据库索引的数据结构 Redis集群 Redis我们一般说起来都会说Redis服务器所以Redis本质上也是一台服务器Redis即服务器服务器即Redis。服务器宕机Redis肯定也好不到哪里去。如果只有一台Redis服务器那将会面临一定的风险比如系统崩溃。 主从 为了解决单Redis服务器可能存在的问题我们一般会使用一台备用机这就叫做主从。主从状态下备用机Redis会实时同步主机Redis的数据如果主机掉线备用机就可以起到预备队的效果。但这也存在一定的问题主机正常工作时从机就在那里歇着主机累的喘不过来气肯定不愿意从机的钱不是也白花了吗如下图 读写分离 为了解决从机不干活的问题我们一般会将读写分离主机可读可写从机也可以读取这样不仅让从机干活还减轻了主机的压力提高了项目运行的流畅度。如下图 哨兵 此时还存在一个问题主机宕机后需要人手动切换到从机要是及时发现还好要是不及时将会造成严重的后果。这时候我们需要一个可以自动切换到从机的机制哨兵模式。如下图 哨兵每隔固定时间向主从节点发送请求如果节点正常相应则说明节点正常工作否则将视为节点异常启将自动切换备用机。 有时候因为网络问题或者其他因素会导致哨兵接受请求返回异常而切换备用机这不仅没起到保护的作用还降低了Redis的工作效率这时有两种方式来解决一是多次请求后都返回异常再切换备用机二是采用多个哨兵的形式当多台哨兵都认为某台机器存在异常再切换到备用机。但是切记一点哨兵不能和Redis在同一台服务器上否则服务器异常哨兵也将离线。 哨兵的配置推荐看看这篇博客Redis中的哨兵模式 - 简书  Redis基本使用 下面我们就来看看Redis有哪些API具体该怎么使用。在原来微服务项目中上一篇Quartz是在stock子项目下运行的Redis我们也在stock子项目下添加吧你也可以选择其他模块或者独立建一个项目也是可以的。 添加依赖 !-- Spring Boot Data Redis缓存 -- dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId /dependency 请注意博主依赖中没有添加version信息是因为主工程中对版本进行了管理如果你单独创建的项目是需要加上版本的其他依赖在需要时按需添加。 添加配置类 操作Redis需要使用RedisTemplate对象我们在config包下创建RedisConfiguration类 package com.codingfire.cloud.stock.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializer;import java.io.Serializable;Configuration public class RedisConfiguration {Beanpublic RedisTemplateString, Serializable redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplateString, Serializable redisTemplate new RedisTemplate();redisTemplate.setConnectionFactory(redisConnectionFactory);redisTemplate.setKeySerializer(RedisSerializer.string());redisTemplate.setValueSerializer(RedisSerializer.json());return redisTemplate;} }固定模式也没啥好说的只是需要使用这样一个实例来操作Redis。 编写测试方法  由于我们之前删除了用于测试的文件夹还是需要新建的选择src新建file 选择test/java新建测试类 package com.codingfire.cloud.stock;import org.springframework.boot.test.context.SpringBootTest;SpringBootTest public class CloudStockApplicationTests {}包名路径和格式注意下不要错了。下面我们来添加Redis的测试方法。 存储普通字符串 package com.codingfire.cloud.stock;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.RedisTemplate; import org.junit.jupiter.api.Test;import java.io.Serializable; import java.util.concurrent.TimeUnit;SpringBootTest class CloudStockApplicationTests {AutowiredRedisTemplateString, Serializable redisTemplate;Testvoid testSetValue() {redisTemplate.opsForValue().set(name, codingfire);}}运行测试方法成功后我们去Redis客户端看看有没有什么变化 这个就是我们测试方法中存入的数据表示我们已经将此字符串存入Redis中。有问题的童鞋看看自己的Redis有没有启动连接。 有存就有取我们去把刚才存进去的字符串取出来 Testvoid testGetValue() {// 当key存在时可获取到有效值// 当key不存在时获取到的结果将是nullSerializable name redisTemplate.opsForValue().get(name);System.out.println(get value -- name);} 运行测试方法查看控制台输出 get value -- codingfire成功从Redis取到了我们存入的数据。 不知道你注意到没我们前面的配置类里面对Redis的存取类型做了限制 redis存储类型很有限不过好在string和json几乎能满足所有的需要下面我们来存取对象类型试试 Testvoid testSetAdminValue() {AdminLoginDTO adminLoginDTO new AdminLoginDTO();adminLoginDTO.setUsername(codingfire);adminLoginDTO.setPassword(123456);redisTemplate.opsForValue().set(user, adminLoginDTO);}Testvoid testGetAdminValue() {// 当key存在时可获取到有效值// 当key不存在时获取到的结果将是nullSerializable user redisTemplate.opsForValue().get(user);System.out.println(get value -- user);if (user ! null) {AdminLoginDTO adminLoginDTO (AdminLoginDTO) user;System.out.println(get value -- adminLoginDTO);}} 分别运行以上存取对象类型方法看看Redis客户端和控制台会输出什么。 存储对象后Redis客户端 获取对象后控制台输出 两次输出一样为什么还要强转呢不强转你就不知道获取的对象类型也无法直接使用序列化后对象进行数据的操作。 有添加就有删除那么删除Redis中数据该怎么做呢 Testvoid testDeleteKey() {// 删除key时将返回“是否成功删除”// 当key存在时将返回true// 当key不存在时将返回falseBoolean result redisTemplate.delete(name);System.out.println(result -- result);} 运行此测试方法查看结果 由于Redis中存在名为name的参数所以result为true对象类型的删除也是一样的操作。 接下来我们设置Redis的过期时间时间到了就自动删除我们通过查看源码得知set有三个参数第二个为过期时间第三个为时间单位 下面我来写代码 Testvoid testSetValueTimeout() {redisTemplate.opsForValue().set(name, codingfire,20, TimeUnit.SECONDS);} 运行测试方法在Redis中能看到存入的数据20s后数据自动删除 在客户端中能看到TTL就是倒计时的意思打开自动刷新功能你能看见数字是在变化的。 在存储数据时还有一个特殊情况比如分页数据是一个数组这该怎么存呢Redis中ops是操作器opsForValue可以操作普通字符串或对象类型既然是对象为什么不能操作数组数组也是对象啊博主也不死心我们来试试看 好像成功了为了对比这两种方式存储数组的能力我们来试试ospForList存数组后是否一样 Testvoid testRightPushList() {// 存入List时需要redisTemplate.opsForList()得到针对List的操作器// 通过rightPush()可以向Redis中的List追加数据// 每次调用rightPush()时使用的key必须是同一个才能把多个数据放到同一个List中ListAdminLoginDTO list new ArrayList();for (int i 1; i 5; i) {AdminLoginDTO adminLoginDTO new AdminLoginDTO();adminLoginDTO.setUsername(name i);list.add(adminLoginDTO);}String key UserList;for (AdminLoginDTO adminLoginDTO : list) {redisTemplate.opsForList().rightPush(key, adminLoginDTO);}} 运行测试代码后查看Redis客户端数据 呀数据展示的形式不一样啊我们来获取并输出一下这两组数据看看输出是否一样 Testvoid testGetListValue() {// 调用opsForList()后再调用range(String key, long start, long end)方法取出List中的若干个数据将得到List// long start起始下标结果中将包含// long end结束下标结果中将包含如果需要取至最后一个元素可使用-1作为此参数值ListSerializable rangeList redisTemplate.opsForList().range(UserList, 0, -1);Serializable listSer redisTemplate.opsForValue().get(list);ListSerializable list (ListSerializable) listSer;System.out.println(rangeList);System.out.println(list);for (Serializable serializable : rangeList) {System.out.println(serializable);}for (Serializable serializable : list) {System.out.println(serializable);}} 运行测试方法查看控制台输出 从数据库其实并没有什么太大的差别 但是请仔细看博主获取两个list的代码秘密就藏在里面也就是说怎么存怎么取不通过数组专用方法的需要强转。这就是最终结论。大家不用自己试了博主都一一试过了opsForxxxxx用的不对就报错了。 最后再补充两个方法一个是获取数组长度一个是获取Redis中所有key的方法注意获取数组长度的方法必须是通过opsForList方法存进去的否则此方法无效且报错看代码 Testvoid testListSize() {// 获取List的长度即List中的元素数量String key UserList;Long size redisTemplate.opsForList().size(key);System.out.println(size -- size);} 运行结果 获取所有Redis的key Testvoid testKeys() {// 调用keys()方法可以找出匹配模式的所有key// 在模式中可以使用星号作为通配符SetString keys redisTemplate.keys(*);for (String key : keys) {System.out.println(key);}} 此方法对ops无影响只获取key查看运行结果 对比Redis客户端中所有key值 完全一致测试成功。 最后关于Key的使用通常建议使用冒号区分多层次类似URL的设计方式例如 用户列表的Keyusers:list或users某个用户id001对应用户信息的Keyusers:userId:001 但也不是绝对可根据自己需要选择合适的组合方式目的是使key不重复且好理解。 到这里Redis基础方法使用就讲解完了但这毕竟只是基础方法在实战中该怎么用还是个问题。下面我们将在真实项目中去使用Redis。 Redis实战 开始前的思考 使用Redis可以提高查询效率降低数据库的压力。基本上是用在高频查询的数据或是几乎不太会改变的数据上。所以有些比较精密的经常需要变动的数据就不能使用Redis比如购物类应用的创建订单库存都属于此类数据。 所以在开始前确定哪些数据使用RedisRedis中的数据从哪来是很重要的。关于Redis的调用是写在业务逻辑层还是做一个单独的组件独立出来这也是一个问题。 如果直接将访问Redis的代码写在Service中首次开发时会很省事但却不利于后期的维护。 如果将访问Redis的代码写的新的组件中首次开发时会更麻烦但有利于后期的维护。 所以你会怎么选呢我们首先要知道访问Redis的API都很简单上面的基础使用大家基本应该是掌握了也没什么难的自定义组件虽然有利于后期维护但代码量可能会很少这个需要我们去衡量总体的工作量和后期的维护情况。 每一个项目都不一样甚至有些小的项目根本不使用Redis都有可能这并不是危言耸听这个就教给大家自己选择了今天博主的目的是教会大家在项目中使用Redis。 创建Redis模块 博主准备以passport为基础在其上使用Redis虽然实际中不会在这么简单的模块用不过该有的功能博主是一步都不会省略的照葫芦画瓢其他的模块参照此模块就可以移植。passport是做单点登录的模块Java开发 - 单点登录初体验Spring Security JWT 没有此模块的童鞋可以先学此篇也可以先看看博主代码新建一个模块照着往别的模块上面搬。 创建调用Redis接口 在passport包下创建repository包repository下创建IPassportRedisRepository接口 package com.codingfire.cloud.passport.repository;import com.codingfire.cloud.commons.pojo.passport.vo.AdminLoginVO;public interface IPassportRedisRepository {String KEY_ADMIN_ITEM_PREFIX admins:item:;// 将用户信息存入到Redis中void save(AdminLoginVO adminLoginVO);// 根据用户id获取用户信息AdminLoginVO getAdminDetailsById(Long id); }创建Redis实现类 在这一步之前请大家添加Redis的依赖并将上面代码中Redis的配置类RedisConfiguration复制到passport的config包下在repository包下新建impl包包下建PassportRedisRepositoryImpl实现类 package com.codingfire.cloud.passport.repository.impl;import com.codingfire.cloud.commons.pojo.passport.vo.AdminLoginVO; import com.codingfire.cloud.passport.repository.IPassportRedisRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Repository; import java.io.Serializable;Repository public class PassportRedisRepositoryImpl implements IPassportRedisRepository {Autowiredprivate RedisTemplateString, Serializable redisTemplate;Overridepublic void save(AdminLoginVO adminLoginVO) {String key KEY_ADMIN_ITEM_PREFIX adminLoginVO();redisTemplate.opsForValue().set(key, adminLoginVO);}Overridepublic AdminLoginVO getAdminDetailsById(Long id) {String key KEY_ADMIN_ITEM_PREFIX id;Serializable result redisTemplate.opsForValue().get(key);if (result null) {return null;} else {AdminLoginVO adminLoginVO (AdminLoginVO) result;return adminLoginVO;}} }测试以上代码 下面在test文件夹下depassport包下新建一个测试类PassportRedisRepositoryTests: package com.codingfire.cloud.passport;import com.codingfire.cloud.commons.pojo.passport.dto.AdminLoginDTO; import com.codingfire.cloud.commons.pojo.passport.vo.AdminLoginVO; import com.codingfire.cloud.passport.repository.IPassportRedisRepository; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;SpringBootTest class PassportRedisRepositoryTests {AutowiredIPassportRedisRepository repository;Testvoid testGetAdminDetailsByIdSuccessfully() {testSave();Long id 2L;AdminLoginDTO admin repository.getAdminDetailsById(id);System.out.println(admin);}Testvoid testGetAdminDetailsByIdReturnNull() {Long id -1L;AdminLoginVO adminLoginVO repository.getAdminDetailsById(id);Assertions.assertNull(adminLoginVO);}private void testSave() {AdminLoginVO adminLoginVO new AdminLoginVO();adminLoginVO.setId(2L);adminLoginVO.setUsername(codeliu);adminLoginVO.setPassword(123456);repository.save(adminLoginVO);} }运行第一个方法先存储后查找 控制器输出我们存入的数据看看Redis客户端有没有数据 可以看到室友层级的key这种key的命名方式我们在上面已经讲过有利于数据分层便于观察。 让Redis在业务中体现调用逻辑 业务逻辑思考 下面我们结合接口的调用来做个修改在接口的调用过程中我们去使用Redis。我们打开IAdminService类在里面添加一个新的方法 AdminLoginVO getAdminDetailsById(Long id);接着打开AdminServiceImpl类在里面实现接口方法 Overridepublic AdminLoginVO getAdminDetailsById(Long id) {// 以下是原有代码只从数据库中获取数据 // AdminLoginVO adminLoginVO adminMapper.getLoginInfoByUserId(id); // if (adminLoginVO null) { // throw new CloudServiceException(ResponseCode.ERR_INTERNAL_SERVER_ERROR, // 获取用户信息失败尝试访问的数据不存在); // } // return adminLoginVO;// 以下是新的业务将从Redis中获取数据 // 从repsotiroy中调用方法根据id获取缓存的数据// 判断缓存中是否存在与此id对应的key// 有表示明确的存入过某数据此数据可能是有效数据也可能是null// -- 判断此key对应的数据是否为null// -- 是表示明确的存入了null值则此id对应的数据确实不存在则抛出异常// -- 否表示明确的存入了有效数据则返回此数据即可// 无表示从未向缓存中写入此id对应的数据在数据库中此id可能存在数据也可能不存在// 从mapper中调用方法根据id获取数据库的数据// 判断从数据库中获取的结果是否为null// 是数据库也没有此数据先向缓存中写入错误数据null再抛出异常// 将从数据库中查询到的结果存入到缓存中// 返回查询结果return null;} 大家看看实现的逻辑这个很重要。 看完后为了保证项目能运行我们还有两步需要做。 添加调用SQL的方法 AdminMapper添加如下方法 AdminLoginVO getLoginInfoByUserId(Long id);添加SQL AdminMapper.xml添加如下SQL select idgetLoginInfoByUserId resultMapLoginInfoResultMapselectinclude refidLoginInfoQueryFields /from adminleft join admin_roleon admin.id admin_role.admin_idleft join role_permissionon admin_role.role_id role_permission.role_idleft join permissionon role_permission.permission_id permission.idwhere id#{id} /select  避免缓存穿透 在IPassportRedisRepository接口中添加如下方法 /*** 判断是否存在id对应的缓存数据** param id 类别id* return 存在则返回true否则返回false*/boolean exists(Long id);/*** 向缓存中写入某id对应的空数据null此方法主要用于解决缓存穿透问题** param id 类别id*/void saveEmptyValue(Long id); 在PassportRedisRepositoryImpl类中添加实现方法如下 Overridepublic boolean exists(Long id) {String key KEY_ADMIN_ITEM_PREFIX id;return redisTemplate.hasKey(key);}Overridepublic void saveEmptyValue(Long id) {String key KEY_ADMIN_ITEM_PREFIX id;redisTemplate.opsForValue().set(key, null);} 其实我们在上面说过这种设置null的方法能一定程度上防止缓存反复穿透但却并不是最好的解决办法常规做法应该是通过布隆过滤器来做。 业务实现 Overridepublic AdminLoginVO getAdminDetailsById(Long id) { // 以下是原有代码只从数据库中获取数据 // AdminLoginVO adminLoginVO adminMapper.getLoginInfoByUserId(id); // if (adminLoginVO null) { // throw new CloudServiceException(ResponseCode.ERR_INTERNAL_SERVER_ERROR, // 获取用户信息失败尝试访问的数据不存在); // } // return adminLoginVO;// 以下是新的业务将从Redis中获取数据 log.debug(根据id{}获取用户详情……, id);// 从repository中调用方法根据id获取缓存的数据// 判断缓存中是否存在与此id对应的keyboolean exists redisRepository.exists(id);if (exists) {// 有表示明确的存入过某数据此数据可能是有效数据也可能是null// -- 判断此key对应的数据是否为nullAdminLoginVO cacheResult redisRepository.getAdminDetailsById(id);if (cacheResult null) {// -- 是表示明确的存入了null值则此id对应的数据确实不存在则抛出异常log.warn(在缓存中存在此id对应的Key却是null值则抛出异常, id);throw new CloudServiceException(ResponseCode.ERR_INTERNAL_SERVER_ERROR,获取用户详情失败尝试访问的数据不存在);} else {// -- 否表示明确的存入了有效数据则返回此数据即可return cacheResult;}}// 缓存中没有此id匹配的数据// 从mapper中调用方法根据id获取数据库的数据log.debug(没有命中缓存则从数据库查询数据……);AdminLoginVO dbResult adminMapper.getAdminInfoByUserId(id);// 判断从数据库中获取的结果是否为nullif (dbResult null) {// 是数据库也没有此数据先向缓存中写入错误数据再抛出异常log.warn(数据库中也无此数据id{}先向缓存中写入错误数据, id);redisRepository.saveEmptyValue(id);log.warn(抛出异常);throw new CloudServiceException(ResponseCode.ERR_INTERNAL_SERVER_ERROR,获取用户信息失败尝试访问的数据不存在);}// 将从数据库中查询到的结果存入到缓存中log.debug(已经从数据库查询到匹配的数据将数据存入缓存……);redisRepository.save(dbResult);// 返回查询结果log.debug(返回查询到数据{}, dbResult);return dbResult;} 到这里基于业务调用的Redis业务调用流程代码就结束了你可以在controller中添加新的方法来调用此接口完成测试博主不再写了。 但此时还有一个问题我们不能让每次数据查询的时候再去存Redis否则第一次查询的时候Redis是空的。基于此我们需要在系统启动时就把数据存入Redis中此法叫做缓存预热。 缓存预热 创建预热类 缓存预热需要确定哪些数据在系统启动时就存入数据我们在Spring Boot内自定义一个组件他需要实现实现ApplicationRunner我们和启动类平级建一个这样的类名字叫CachePreLoad: package com.codingfire.cloud.passport;import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner;public class CachePreLoad implements ApplicationRunner {Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println(CachePreLoad.run());} }此类用于启动项目时提前预热资源run方法是重写的启动类方法启动时此方法会被调用可在这里写存储Redis相关的内容。 但却不可将业务实现直接写在这个类中为了项目代码整体的一致性我们还将预热的业务以接口和实现的形式写在Redis单独的Repository组件内。 在写之前为了防止童鞋们迷惑博主需要解释一个问题我个人也感觉使用用户模块做Redis不太恰当应该是使用店铺列表商品列表品牌列表这样不太会变的数据做Redis考虑到再加入新的表增加大家学习难度所以才在用户表上面做文章好在这个用户表也很简单大家在真实场景中可以根据这里的代码转嫁到其他的业务下即可万不可钻牛角尖。此处数据只做案例讲解使用并非真实使用场景但代码绝对是业务级别的。下面我们来在Redis组件中增加预热的代码。 添加Redis操作接口 在IPassportRedisRepository接口中增加以下接口 String KEY_ADMIN_LIST admins:list;/*** 将用户的列表存入到Redis中** param admins 用户列表*/void save(ListAdminLoginVO admins);/*** 删除Redis中各独立存储的用户数据*/void deleteAllItem();/*** 删除Redis中的用户列表* return 如果成功删除则返回true否则返回false*/Boolean deleteList(); 实现Redis操作接口 接口增加完了实现类报错需要实现新增加的方法 Overridepublic void save(ListAdminLoginVO admins) {for (AdminLoginVO admin : admins) {redisTemplate.opsForList().rightPush(KEY_ADMIN_LIST, admin);}}Overridepublic void deleteAllItem() {SetString keys redisTemplate.keys(KEY_ADMIN_ITEM_PREFIX *);redisTemplate.delete(keys);}Overridepublic Boolean deleteList() {return redisTemplate.delete(KEY_ADMIN_LIST);} 添加调用SQL方法 在IAdminMapper接口中增加方法 Select(select * from admin)ListAdminLoginVO list(); 由于比较简单就把SQL直接写在注解里来。 添加预热调用接口 在IAdminService接口中增加预热方法 void preloadCache();实现预热方法 在AdminServiceImpl实现类中实现上面的接口方法 Overridepublic void preloadCache() {log.debug(删除缓存中的用户列表……);redisRepository.deleteList();log.debug(删除缓存中的各独立的用户数据……);redisRepository.deleteAllItem();log.debug(从数据库查询用户列表……);ListAdminLoginVO list adminMapper.list();for (AdminLoginVO admin : list) {log.debug(查询结果{}, admin);log.debug(将当前用户存入到Redis{}, admin);redisRepository.save(admin);}log.debug(将用户列表写入到Redis……);redisRepository.save(list);log.debug(将用户列表写入到Redis完成);} 启动时预热类调用预热方法  最后一步在预热缓存类中调用此预热方法 package com.codingfire.cloud.passport;import com.codingfire.cloud.passport.service.IAdminService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component;Component Slf4j public class CachePreLoad implements ApplicationRunner {Autowiredprivate IAdminService adminService;Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println(CachePreLoad.run());log.debug(准备执行缓存预热……);adminService.preloadCache();log.debug(缓存预热完成);} }测试缓存预热 其实这一步测试是最简单的我们什么都不需要做直接启动项目就可以项目启动后我们在控制台会看到预热方法运行的输出 下面是启动前Redis客户端内的参数列表 下面是项目启动后Redis客户端内的参数列表 到这一步若果你的测试结果和博主一样那么恭喜你你已经完成了Redis的基本学习赶快到自己的项目中使用吧。 结语 Redis学习到这里就结束了美中不足是少了布隆过滤器对Redis做的一个防止缓存穿透的操作建议大家可以自己写写后期博主会做个补充建议大家不要等自己动动手也不算难。总体上Redis整体上还算是比较简单的用过几次其实就熟练了很多东西都是调用API再配合我们的业务逻辑来写。行吧结语也不知道该写点啥就是跟大家诉诉苦太累了坐的腰酸背疼熬夜熬的眼疼觉得写的不错三连支持一下。
http://www.dnsts.com.cn/news/152748.html

相关文章:

  • 手机网站分享js代码专门做网页设计网站
  • 苏州淘宝网站建设网络软文怎么写
  • 2个小时学会网站建设wordpress 固定链接 nginx
  • 栖霞酒店网站设计价格上海做网站的公司哪个好
  • 江苏cms建站系统做网站的费用是多少钱
  • 宿迁网站建设宿迁区块链外包开发
  • 甘肃兰州做网站如何建设网站安全
  • 有哪些可以免费推广的网站wordpress内容分享微信
  • 东至县住房和城乡建设网站门户网站建设的公司
  • 四平市住房和畅想建设局网站微信公众平台开发者
  • 免费给别人开发网站找个网站懂的网站
  • 超星网站开发实战答案中国建设银行广东分行网站
  • 开设网站需要什么网站开发过程中感想
  • 用手机可以做网站嘛wordpress腾讯企业邮箱
  • 帮一个公司做网站多少钱山东省住房和城乡建设厅厅长
  • 诸城哪有做公司网站的网站5建设需要学什么条件
  • 开发网站合作协议2023年电商市场数据报告
  • 企业网站备案代理公司广州网站提升排名
  • 河池网站制作ajax实现wordpress导航栏
  • 网站打开速度影响因素网站目录做外链
  • 网站域名使用怎么做待摊分录旅游电子商务网站规划书
  • 如何把网站的文字编辑自建网站三种模式
  • 厦门网站开发费用机器人软件开发平台
  • 做网站销售的技巧国家高新技术企业认定机构
  • 现在一般做网站用什么技术广告创意设计图片赏析
  • 如何用front怕个做网站山东手工活外发加工网
  • 伊春网站建设公司在百度怎么推广
  • 网站备案哪个部门美图秀秀在线制作
  • 织梦网站2个模型wordpress更新的文章编辑器不好用
  • 网站建站公司排名网上商城推广13种方法