当前位置: 首页 > 科技观察

Redis和本地缓存的结合,味道更好!_0

时间:2023-03-23 10:51:14 科技观察

前言我们在开发中经常使用Redis作为缓存。将高频数据放在Redis中,可以提高业务性能,减轻MySQL等关系型数据库的压力。有些系统甚至使用Redis进行数据持久化。Redis具有松散的文档结构。非常适合业务系统开发,在精准查询和数据统计业务上有很大的优势。但是在高频数据流处理系统中,Redis的压力也??会很大,I/0开销是造成耗时的主要原因。这时候为了减轻Redis的读写压力,我们可以使用本地缓存。Guava为我们提供了优秀的本地缓存API,包括过期策略等,编码难度低,强烈推荐。设计实例Redis懒加载缓存数据在添加到MySQL时不缓存,而在精确搜索时缓存,这样查询缓存,没有查询不缓存流程图示例//伪代码示例xx代表你的业务对象如用户商品等publicclassXxLazyCache{@AutowiredprivateRedisTemplateredisTemplate;@AutowiredprivateXxServicexxService;//你的业务服务/***通过查询业务保证id前面的缓存来查询是否有驱动缓存加载建议数据库中肯定存在对应的数据*/publicXxgetXx(intid){//1.查询缓存中是否有数据XxxxCache=getXxFromCache(id);if(xxCache!=null){returnxxCache;//guard语句让代码更易读}//2.查询数据库获取数据。我们假设在业务步骤,传递的id在数据库中有对应的数据Xxxx=xxService.getXxById(id);//3.设置Cache,这一步相当于Redis的缓存懒加载,下次查询这个id的时候会去缓存setXxFromCache(xx);返回xx;}}/***数据库运行成功后修改或删除xx数据并删除缓存*删除请求-删除数据库数据删除缓存*修改请求-更新数据库数据删除缓存下次从数据库中拉取新数据到缓存查询时*/publicvoiddeleteXxFromCache(longid){Stringkey="Xx:"+xx.getId();redisTemplate.delete(key);}privatevoidsetXxFromCache(Xxxx){Stringkey="Xx:"+xx.getId();redisTemplate.opsForValue().set(key,xx);}privateXxgetXxFromCache(intid){//通过缓存前缀拼装出唯一的主键作为Xxx等信息的缓存键为Xxx:idStringkey="Xx:"+id;返回redisTemplate.opsForValue().get(key);}}//业务类publicclassXxServie{@AutowiredprivateXxLazyCachexxLazyCache;//查询数据库publicXxgetXxById(longid){//省略实现returnxx;}publicvoidupdateXx(Xxxx){//更新MySQL数据Omit//删除缓存xxLazyCache.deleteXxFromCache(xx.getId());}publicvoiddeleteXx(longid){//删除MySQL数据omit//删除缓存xxLazyCache.deleteXxFromCache(xx.getId());}}//实体类@DatapublicclassXx{//业务主键privateLongid;//...省略}优点是保证最小的缓存量可以满足精准查询业务,避免冷数据占用宝贵的内存空间。增删改查业务侵入性小,删除同步可插拔。对于旧系统升级,历史数据不需要在启动时进行初始化。缺点:需要数据量可控,不适用于无限增长的业务场景。在微服务场景下,不利于全局缓存的应用。应用摘要空间最小化,满足精准查询场景。总数据量是可控的。使用大缓存。流数据业务下,高频读缓存给Redis带来很大压力。我们使用本地缓存结合Redis缓存来降低Redis的压力。同时,本地缓存不有连接开销,性能更好。流程图业务场景微服务在处理数流的过程中,处理多个设备上传的数据。每个设备都有一个编码,流数据频率高。在消息队列发送过程中,要按分区发送,我们需要为设备码生成一个对应的自增数,并使用自增数对Kafka中的topic分区号取模,这样如果有10000个设备,自增数为0~9999,分区发送取模后,可以让每个分区平均分配自增数。我们使用redis自增数来生成,放入redis的hash结构中进行缓存。每次来一个设备,我们就去这个hashcache中取,如果没有取到,就用自增数生成一个,然后放到redis的hashcache中。此时每个设备的自增数一旦生成就不会改变。我们想到使用本地缓存进行优化。避免频繁调用redis获取,减少redis的压力。下面链接是一篇关于kafka分区消费的文章,可以去看看https://juejin.cn/post/6995746569580445709codesample/***这个缓存演示了如何使用redis自增hash本地缓存进行设备自-increment生成,缓存,本地缓存*本地缓存使用GuavaCache*/publicclassDeviceIncCache{/***本地缓存*/privateCachelocalCache=CacheBuilder.newBuilder().concurrencyLevel(16)//并发level.initialCapacity(1000)//初始容量.maximumSize(10000)//最大缓存length.expireAfterAccess(1,TimeUnit.HOURS)//如果缓存在1小时内未使用则过期.build();@AutowiredprivateRedisTemplateredisTemplate;/***redis自增缓存key*/privatestaticfinalStringDEVICE_INC_COUNT="device_inc_count";/***自增hash缓存key对应的redis设备码*/privatestaticfinalStringDEVICE_INC_VALUE="device_inc_value";/***获取设备自增数*/publicintgetInc(StringdeviceCode){//1.从本地缓存中获取Integerinc=localCache.get(deviceCode);如果(公司!=空){返回公司;}//2.本地缓存未命中,来自redis哈希缓存获取inc=(Integer)redisTemplate.opsForHash().get(DEVICE_INC_VALUE,deviceCode);//3.没有redishash缓存,说明是新设备,先为设备生成一个自增数if(inc==null){inc=redisTemplate.opsForValue().increment(DEVICE_INC_COUNT).整数值;//添加到redis哈希缓存redisTemplate.opsForHash().put(DEVICE_INC_VALUE,deviceCode,inc);}//4.添加到本地缓存localCache.put(deviceCode,inc);//4.返回自增数returninc;}}优势redis保证数据持久化,本地缓存保证超高读取性能,微服务共享redis大缓存的场景可以有效降低redis作为本地缓存,Guava提供丰富的API,过期策略,最大容量保证服务内存可控,冷数据不会长期占用内存空间。服务重启导致的本地缓存清除不会影响业务微服务和分布式场景。在分布式使用的情况下,每个服务实例只会缓存其访问的那部分设备的自增数,本地内存空间最优。示例业务中,自增号满足配送区域统一配送要求。满足统计连接设备数量的业务场景,一石二鸟。缺点增加了编码的复杂性。不直接适用于缓存内容只增加不改变的场景。总结本地缓存空间可控,过期策略最适合微服务和分布式场景。缓存内容Redis提供了丰富的数据类型和API,非常适合业务系统开发,统计计数(自增、自减)、标记位(bitmap)、松散数据(hash)、先进先出、队列-基础阅读(清单);guavacache作为本地缓存,可以高效的读取,同时提供了大量的API,方便我们控制本地缓存的数据量,剔除冷数据;我们可以充分学习这些功能,以帮助我们进行业务发展。灵活并在空间和时间之间找到平衡