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

有了这款开源的本地缓存工具,Redis读写完全无压力!

时间:2023-03-16 22:35:55 科技观察

前言我们在开发中经常使用Redis作为缓存。将高频数据放在Redis中,可以提高业务性能,减轻MySQL等关系型数据库的压力。有些系统甚至使用Redis进行数据持久化。Redis具有松散的文档结构。非常适合业务系统开发,在精准查询和数据统计业务上有很大的优势。但是在高频数据流处理系统中,Redis的压力也??会很大,I/0开销是造成耗时的主要原因。这时候为了减轻Redis的读写压力,我们可以使用本地缓存。Guava为我们提供了优秀的本地缓存API,包括过期策略等,编码难度低,强烈推荐。设计实例Redis懒加载缓存数据在加入MySQL时不缓存,准确查找时缓存,做到query缓存,没有query不缓存。1)流程图2)代码示例//伪代码示例xx代表你的业务对象如用户商品等publicclassXxLazyCache{@AutowiredprivateRedisTemplateredisTemplate;@AutowiredprivateXxServicexxService;//你的业务服务/***通过查询缓存是否存在来驱动缓存加载。建议保证id对应的数据在数据库中绝对存在*/publicXxgetXx(intid){//1.检查缓存中是否有DataxxxxCache=getXxFromCache(id);if(xxCache!=null){returnxxCache;//guard语句让代码更易读}//2.查询数据库获取数据。我们假设在业务步骤,传入的id在数据库中都有对应的数据xxxx=xxService.getXxById(id);//3.设置缓存,这一步相当于Redis缓存懒加载,下次查询这个id时,会去缓存setXxFromCache(xx);返回xx;}}/***操作数据库成功后修改或删除xx数据并删除缓存*Deleterequest-删除数据库数据删除缓存*Modifyrequest-update数据库数据删除缓存会在下次查询get时从数据库中拉取新数据进入缓存*/publicvoiddeleteXxFromCache(longid){Stringkey="Xx:"+xx.getId();redisTemplate.delete(key);}privatevoidsetXxFromCache(Xxxx){斯特林gkey="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;//...略}3)优点保证最小的缓存量满足精准查询业务,避免冷数据占用宝贵的内存空间;对增删改查业务侵扰小,删后同步;可插拔,对于老系统升级,历史数据不需要在启动时初始化缓存4)缺点:需要数据量可控,不适用于无限增长的业务场景;不利于微服务场景下的全局缓存应用。5)最小化摘要空间;满足精准查询场景;建议总数据量可控;微服务场景不适用。Redis结合本地缓存微服务的场景,多个微服务使用一个大缓存。在流式数据服务下,高频读缓存给Redis带来了不小的压力。我们使用本地缓存结合Redis缓存来降低Redis的压力。同时,本地缓存没有连接开销,性能更好。1)流程图2)业务场景微服务在处理流数的过程中,处理多个设备上传的数据,每个设备都有一个编码,流数据出现频率高,使用分区发送消息队列,我们需要为设备码生成对应的自增数,用自增数对Kafka中的topic分区数取模,所以如果有10000台设备,自增数为0~9999,取模后发送分区每个分区可以平均分配。我们利用redis的自增数生成这个自增数,生成后放入redis的hash结构中进行缓存。每次来一个设备,我们都会去这个hashcache中去获取。拿到后,用自增数产生一个,然后放到redis的hash缓存中。这时候每个设备的自增数一旦产生,就不会再变了。我们想到了使用本地缓存进行优化,避免频繁调用redis获取,减轻redis压力。3)代码示例/***本缓存演示了如何使用redis自增数hash本地缓存生成设备自增数、缓存、本地缓存*本地缓存使用GuavaCache*/publicclassDeviceIncCache{/***LocalCache*/privateCachelocalCache=CacheBuilder.newBuilder().concurrencyLevel(16)//并发级别.initialCapacity(1000)//初始容量.maximumSize(10000)//最大缓存length.expireAfterAccess(1、TimeUnit.HOURS)//1小时不使用缓存过期。建造();@AutowiredprivateRedisTemplateredisTemplate;/***redis自增缓存key*/privatestaticfinalStringDEVICE_INC_COUNT="device_inc_count";/***redis设备码对应自增数的hash缓存key*/privatestaticfinalStringDEVICE_INC_VALUE="device_inc_value";/***获取设备自增数*/publicintgetInc(StringdeviceCode){//1.从本地缓存中获取Integerinc=localCache.get(deviceCode);如果(公司!=空){返回公司;}//2.本地缓存未命中,从redis哈希缓存中获取inc=(整数)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;}}4)优势Redis保证数据持久化,本地缓存保证超高读取性能,微服务共享大容量redis缓存的场景可以有效降低redis压力;guavaas本地缓存,提供丰富的API、过期策略、最大容量,保证业务内存可控,冷数据不会长时间占用内存空间;服务重启导致的本地缓存清空不会影响业务;微服务和分布式场景的使用,在分布式情况下,每个服务实例只会缓存它访问的那部分设备的自增数,本地内存空间最优;满足统计设备访问次数的业务场景,一石二鸟5)缺点增加编码复杂度,不直接;只适用于缓存内容只增加不改变的场景。6)总结本地缓存空间可控,过期策略优秀;适用于微服务和分布式场景;缓存内容不可更改;性能非常好。PostscriptRedis提供了丰富的数据类型和API,非常适合业务系统开发,统计计数(自增、自减)、标记位(bitmap)、松散数据(hash)、先进先出、排队读取(list);guava作为本地缓存,可以高效的读取缓存,同时提供了大量的API方便我们控制本地缓存的数据量和冷数据的淘汰;我们可以充分学习这些特点??,帮助我们在业务发展中更加轻松灵活,在空间和时间上找到平衡点。