java倍速处理数据,加速数据读取的利器——缓存

定义:

缓存,在我们日常开发中是必不可少的一种解决性能问题的方法。简单的说,cache 就是为了提升系统性能而开辟的一块内存空间。

作用:

缓存的主要作用是暂时在内存中保存业务系统的数据处理结果,并且等待下次访问使用。在日常开发的很多场合,由于受限于硬盘IO的性能或者我们自身业务系统的数据处理和获取可能非常费时,当我们发现我们的系统这个数据请求量很大的时候,频繁的IO和频繁的逻辑处理会导致硬盘和CPU资源的瓶颈出现。缓存的作用就是将这些来自不易的数据保存在内存中,当有其他线程或者客户端需要查询相同的数据资源时,直接从缓存的内存块中返回数据,这样不但可以提高系统的响应时间,同时也可以节省对这些数据的处理流程的资源消耗,整体上来说,系统性能会有大大的提升。

常用使用场景:

在高并发的数据库访问时,为了抗住数据库并发连接压力,将数据缓存起来,当有请求过来,直接返回数据;

当应用中的数据,更新周期较长,而且每次都查数据库的情况下,可以采用周期更新数据,从而有效减少数据库无效的访问,保证效率;

衡量指标:

对于使用缓存来加速数据读取的情况,一个很关键的指标是缓存命中率,因为如果缓存命中率比较低的话,就意味着还有不少的读请求要回到数据库中。

2. 介绍一种缓存机制

本节以JAVA重构时使用的缓存机制为例,讲解我们日常工作中常用的一种比较高效的缓存机制。具体如下图所示:

6b3bf4ee525d

技术架构图.png

以上缓存机制使用了一级缓存(Memcache) + 二级本地缓存(Guava)的二级缓存机制。

其中,Memcache是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。简单的说就是将数据调用到内存中,然后从内存中读取,从而大大提高读取速度。

Guava是一个全内存的本地缓存实现,它提供了线程安全的实现机制。能够保证当多个请求获取同一个key值时,只允许一个线程访问服务器,其他线程阻塞等待(系统处理:直接返回,不用阻塞),这样就能很好的减轻服务端压力;另外,比起服务端向Memcache获取缓存,本地缓存减少了网络IO开销,提升数据返回的速度,减少了客户端响应的时间。

当然,使用Guava作为本地缓存,需要满足以下的适用场景:

愿意消耗一部分应用服务器的内存来提升速度;

预料某些值会被多次调用;

缓存数据不会超过内存总量;

那么,我们是否有疑问,反正,我这边至少有2点疑问:

如何保证一级缓存和二级缓存的数据一致性;

如何保证一级缓存更新后,不同的服务器下的二级缓存都会进行同步更新;

基于上面的问题,又因为我们面对的是一个黑盒的缓存框架,现有的日志无法直接验证这2点,那么直接上代码:

//Memcache缓存更新,然后删掉本地缓存

public void putCache(String key, Object value) {

long startTime = System.currentTimeMillis();

LOG.info("Start put cache, key = {}, value = {}", key, JSON.toJSONString(value));

ReentrantLock lock = this.getLock(key);

if(null != lock && lock.tryLock()) {

try {

CacheObject cacheObject = CacheObject.generateCacheObject(value);

this.memcachedClient.set(key, cacheObject.getUnixExpireTimeInSeconds(), cacheObject);

this.punishCacheRefreshEvent(JSON.toJSONString(Lists.newArrayList(new String[]{key})));

LOG.info("Put data to Memcached cost {}ms", Long.valueOf(System.currentTimeMillis() - startTime));

} finally {

lock.unlock();

}

}

}

//调zk删本地缓存的方法

private void punishCacheRefreshEvent(String cacheKeyListStr) {

long startTime = System.currentTimeMillis();

this.curatorService.writeDataToNode("/localCache/event/delete", cacheKeyListStr);

LOG.info("Punish cache refresh event to ZK cost {}ms, keys = {}", Long.valueOf(System.currentTimeMillis() - startTime), cacheKeyListStr);

}

通过上面的代码,可知更新缓存操作时,均有日志记录。进入####-wrapper.log可进行验证,上日志:

2017-06-15 10:54:03.120 [ItemListCacheTask-6-exe0] INFO MemcachedService - Punish cache refresh event to ZK cost 4ms, keys = ["master####_TAE_ITEM_LIST_BRAND_AREA_ID_2_174756"]

2017-06-15 10:54:03.120 [ItemListCacheTask-6-exe0] INFO MemcachedService - Put data to Memcached cost 5ms

2017-06-15 10:54:03.121 [pool-2-thread-1 ] INFO MemcachedService - Will delete caches : ["master####_TAE_ITEM_LIST_BRAND_AREA_ID_2_174756"]

2017-06-15 10:54:03.214 [ItemListCacheTask-6-exe0] INFO MemcachedService - Start put cache, key = master####_TAE_ITEM_LIST_BRAND_AREA_ID_2_174826, value = {"index_config":{"brandAreaButton":0,"brandAreaRedirectUrl":"","brandSpecialData":{"indexTopTitle":"测试我","style":"2"},"categorySwitch":1,"checkMaxVersion":"","checkMinVersion":"","couponSloganPicture":"/taobao/web_shopGuide591948637e93c_960_1280.jpg","curTab":1,"currentAndroidVersion":"","currentIosVersion":"","defaultChannelId":"","displayActivityName":1,"displayTab2Dot":0,"footerPicture":"/taobao/web_shopGuide5912d39b6305e_248_88.gif","globalPromotionData":{"image":"/taobao/web_shopGuide59194870cb96c_960_1280.jpg"},"historyDescript":"今天的新品都完了哟~\r\n看看之前错过了什么吧!","indexTips":"每天百款新品,10点更新,10点","isIndex":1,"itemPromotionStyleData":{"autoIncrement":"1","data":[{"id":"1","image":"/taobao/web_shopGuide591007923bbfe_138_138.png","name":"测试"}]},"leftIcon":1,"listStyle":1,"loadingImage":"/taobao/web_shopGuide591409020acac_224_226.jpg","noneIndex":1,"pictureTag":{"eveningMall":"/taobao/web_shopGuide591007cb5fe2b_68_68.png","superBurst":"/taobao/web_shopGuide591007da338bb_68_68.png","timedSpike":"/taobao/web_shopGuide591007d2c8544_68_68.png","today":"/taobao/web_shopGuide59194893e88b4_68_68.png"},"playTimes":12345667,"redirectCart":0,"refreshText":"每天10点,有新鲜货出没\r\n每天20点,有新鲜货出没\r\n每天30点,有新鲜货出没\r\n全场低折,你要的都在这\r\n达人推荐,买什么都知道\r\n柚币福利,惊喜不停\r\n柚姨帮你砍价啦~","shareSwitch":0,"sloganPicture":"/taobao/web_shopGuide5923f38252a22_750_350.jpg","tab1Title":"","tab2Title":"","useTbkSdk":1},"next_brand_area_data":{"1":{"tae_item":{"activityId":178382,"areaStyle":1,"brandAreaId":174560,"customTag":"","description":"","endAt":1513735200000,"isCoin":0,"isShowHot":1,"keplerSaleItemCount":1,"lowDiscount":0.0,"maxVersion":"","minVersion":"","name":"7号最后疯抢","oneItemPic":"","orderCount":0,"ordinal":23,"picture":"","platform":"","promotionText":"","saleItemCount":49,"sortType":0,"tagIds":""},"common_item":{"$ref":"$.next_brand_area_data.1.tae_item"}},"2":{"tae_item":{},"common_item":{"activityId":170517,"areaStyle":1,"brandAreaId":166847,"customTag":"","description":"","endAt":1524103200000,"isCoin":0,"isShowHot":1,"keplerSaleItemCount":2,"lowDiscount":0.0,"maxVersion":"","minVersion":"","name":"京东用品尿裤橱窗","oneItemPic":"","orderCount":0,"ordinal":0,"picture":"","platform":"","promotionText":"","saleItemCount":2,"sortType":0,"tagIds":""}}},"item_list":[],"brand_area_info":{"appId":2,"areaTimerType":1,"bannerPictureArray":[{"brandAreaId":174826,"createdAt":1496217409000,"height":"476","id":13816,"linkType":10017,"linkValue":"","type":1,"url":"/taobao/web_brand_area592e76550b369_361_476.jpg","width":"361"},{"brandAreaId":174826,"createdAt":1496217409000,"height":"440","id":13817,"linkType":0,"linkValue":"","type":1,"url":"/taobao/web_brand_area592e76640658a_1280_440.jpg","width":"1280"}],"bpLinkType":10017,"bpLinkValue":"","brandAreaType":1,"brandPicture":"/taobao/web_brand_area592e76550b369_361_476.jpg","description":"专场简介:123","endAt":1504836000000,"id":174826,"isActive":1,"isCoin":0,"isShowHot":1,"isTop":0,"isZijian":0,"listStyle":2,"name":"淑铮专场2","oridinal":0,"payErrorMessage":"","promotionText":"促销信息:促销会显示成什么样子呢?","saleItemCount":0,"sortType":1,"startAt":1496217380000,"timerType":2,"topItems":"null","topTag":"顶部标签1"}}, expireTimeInSeconds = 2592000

日志路径:

[jumpserver@master-app-01 ####-####]$ pwd

/data/log/####-####

3. 问题定位

3.1 定位问题的步骤

工作中,经常会碰到运营后台操作后或清除缓存后,接口数据未更新的情况。此时,我们需要掌握定位此类问题的技能,还是以JAVA端重构接口的缓存机制为例。

下图展示了一种删key类型的清缓存方式,一种_cache类型的更新缓存机制:

6b3bf4ee525d

后台-缓存.png

第一步:关注后台发送的topic消息

操作后台进行数据更新后,如上图所示,针对2种类型的更新缓存模式,会发送MQ消息,即JAVA端订阅的topic消息。日志路径:

[jumpserver@master-app-01 ####-####]$ cd /data/web/log/

具体到以下2种类型,具体分析:

删key类型的接口:日志为log下的refresh_cache文件夹下的info.log,如下:

[jumpserver@master-app-01 refresh_cache]$ pwd

/data/web/log/refresh_cache

[jumpserver@master-app-01 refresh_cache]$ ls

exception info.20170606.log info.20170612.log my_item_info refresh_by_methods tae_item_list_cache

info.20170601.log info.20170607.log info.20170613.log own_item_list_cache tae_active_item_list_cache

info.20170602.log info.20170608.log info.20170614.log phone_recharge tae_category_client_item_list_cache

info.20170605.log info.20170609.log info.20170615.log recommend_item_pool_cache tae_category_client_list_cache

_cache类型的接口:针对具体的接口,日志放在不同的文件夹下,如tae_brand_list相关的首页、专场等在refresh_brand_area文件夹下,专场分类列表在refresh_category_client_item_list文件夹下。以首页专场为例,如下:

[jumpserver@master-app-01 refresh_brand_area]$ pwd

/data/web/log/refresh_brand_area

[jumpserver@master-app-01 refresh_brand_area]$ ls

info.20170511.log info.20170516.log info.20170521.log info.20170526.log info.20170531.log info.20170605.log info.20170610.log info.20170615.log

info.20170512.log info.20170517.log info.20170522.log info.20170527.log info.20170601.log info.20170606.log info.20170611.log

info.20170513.log info.20170518.log info.20170523.log info.20170528.log info.20170602.log info.20170607.log info.20170612.log

info.20170514.log info.20170519.log info.20170524.log info.20170529.log info.20170603.log info.20170608.log info.20170613.log

info.20170515.log info.20170520.log info.20170525.log info.20170530.log info.20170604.log info.20170609.log info.20170614.log

打开日志文件,后台操作删key后,打印日志如下:

info | 16687 | 1497496242.3234 | 2017:06:15 11:10:42 | 0.002588987350 | 成功发送消息: {"data":[{"v":1,"app_id":1,"app_key":"001","method":"new_user_special_item","clear_cache":1,"delete_key":true,"append_id":"174844"},{"v":1,"app_id":7,"app_key":"007","method":"new_user_special_item","clear_cache":1,"delete_key":true,"append_id":"174844"}],"time":"2017-06-15 11:10:42","cachebuildUrl":"http:\/\/test.cachebuild.master.youzibuy.com\/router\/rest","uuid":"REFRESH_CACHE__5941fab24ef424.48576463","cache_prefix":"master"}

第二步:关注JAVA端监听到消息后,是否进行了处理

JAVA端的日志路径:

/data/log/####-####

我们关注日志文件####-mq-listener.log即可。在接收到后台发送的topic消息后,JAVA端会向Memcache系统发送相关的删key or 更新key的请求。

[jumpserver@master-app-01 ####-####]$ tail -f ####-mq-listener.log

2017-06-15 11:10:42.322 [cacheDeleteContainer-1] WARN CacheDeleteCommandListener - Current instance is not leader, will not handle cache refresh request from mq.

2017-06-15 11:10:42.337 [cacheDeleteContainer-1] WARN AbstractListener - receieve magic message key ID:master-app-01-47896-1496894034686-3:10103:-1:1:397 content : {"data":[{"v":1,"app_id":1,"app_key":"001","method":"new_user_special_item","clear_cache":1,"delete_key":true,"append_id":"174842"},{"v":1,"app_id":7,"app_key":"007","method":"new_user_special_item","clear_cache":1,"delete_key":true,"append_id":"174842"}],"time":"2017-06-15 11:10:42","cachebuildUrl":"http:\/\/####.master.####.com\/router\/rest","uuid":"REFRESH_CACHE__5941fab24c4025.73699813","cache_prefix":"master"}

2017-06-15 11:10:42.338 [cacheDeleteContainer-1] WARN CacheDeleteCommandListener - #### del cache from mq

2017-06-15 11:10:42.338 [cacheDeleteContainer-1] WARN CacheDeleteCommandListener - Current instance is not leader, will not handle cache refresh request from mq.

第三步:缓存更新是否成功

该部分监控,我们可以借助MQ平台:

http://...:**/admin/queues.jsp;jsessionid=gvurz20v706dev474xwsgd95

6b3bf4ee525d

image.png

我们在确认上面第二部JAVA端进行了消息处理后,可关注Memcache系统有没有更新缓存成功。具体的缓存类型,关注具体的缓存处理,其中:

删key ——> refresh_cache_new_java

_cache ——> refresh_cache_java

关注点:

Number of Consumers,即消费消息的数量是否>0,如果等于0,那么JAVA端的缓存构建系统出现故障。

Messages Enqueued和Messages Dequeued的数量分别+1,则表示缓存更新成功,若Messages Enqueued数量+1,而Messages Dequeued数量不变,则表示缓存更新失败。

6b3bf4ee525d

image.png

3.2 配置相关

从后台发送topic消息,到JAVA端接收,二者必须保证发送的和接收的一致性。这样才能够最终刷缓存成功。

PHP端后台配置

我们一般关注后台发送的topic消息前缀,如操作主干后台,通过日志可知前缀为master。

info | 26698 | 1497521442.5619 | 2017:06:15 18:10:42 | 0.008535861969 | 成功发送消息:

{"data":

[

{"v":1,"app_id":1,"app_key":"001","method":"new_user_special_item","clear_cache":1,"delete_key":true,"append_id":"174847"},{"v":1,"app_id":7,"app_key":"007","method":"new_user_special_item","clear_cache":1,"delete_key":true,"append_id":"174847"}

],

"time":"2017-06-1518:10:42",

"cachebuildUrl":"http:\/\/test.cachebuild.master.youzibuy.com\/router\/rest","uuid":"REFRESH_CACHE__59425d22892ac2.15914753",

"cache_prefix":"master"}

JAVA端配置

我们一般关注JAVA端接收消息的前缀,这个需要和后台发送消息的前缀保持一致。JAVA端的配置平台:http://dev.#####.####.com/modifyFile.html?configId=183

关注disconf-managed-config.properties下的缓存前缀,如下:

memcache.key.prifix=master

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值