为什么用redis做缓存而不是mybatis自带的缓存_如何用Java设计一个本地缓存,涨姿势了...

最近在看Mybatis的源码,刚好看到缓存这一块,Mybatis提供了一级缓存和二级缓存;一级缓存相对来说比较简单,功能比较齐全的是二级缓存,基本上满足了一个缓存该有的功能。

当然如果拿来和专门的缓存框架如ehcache来对比可能稍有差距,本文我们将来整理一下实现一个本地缓存都应该需要考虑哪些东西。

5ce2846df6b0223f7b5c6780629941e9.png

考虑点

考虑点主要在数据用何种方式存储,能存储多少数据,多余的数据如何处理等几个点,下面我们来详细的介绍每个考虑点,以及该如何去实现;

1.数据结构

首要考虑的就是数据该如何存储,用什么数据结构存储,最简单的就直接用Map来存储数据;或者复杂的如redis一样提供了多种数据类型哈希,列表,集合,有序集合等,底层使用了双端链表,压缩列表,集合,跳跃表等数据结构;

2.对象上限

因为是本地缓存,内存有上限,所以一般都会指定缓存对象的数量比如1024,当达到某个上限后需要有某种策略去删除多余的数据;

3.清除策略

上面说到当达到对象上限之后需要有清除策略,常见的比如有LRU(最近最少使用)、FIFO(先进先出)、LFU(最近最不常用)、SOFT(软引用)、WEAK(弱引用)等策略;

4.过期时间

除了使用清除策略,一般本地缓存也会有一个过期时间设置,比如redis可以给每个key设置一个过期时间,这样当达到过期时间之后直接删除,采用清除策略+过期时间双重保证;

5.线程安全

像redis是直接使用单线程处理,所以就不存在线程安全问题;而我们现在提供的本地缓存往往是可以多个线程同时访问的,所以线程安全是不容忽视的问题;并且线程安全问题是不应该抛给使用者去保证;

6.简明的接口

提供一个傻瓜式的对外接口是很有必要的,对使用者来说使用此缓存不是一种负担而是一种享受;提供常用的get,put,remove,clear,getSize方法即可;

7.是否持久化

这个其实不是必须的,是否需要将缓存数据持久化看需求;本地缓存如ehcache是支持持久化的,而guava是没有持久化功能的;分布式缓存如redis是有持久化功能的,memcached是没有持久化功能的;

8.阻塞机制

在看Mybatis源码的时候,二级缓存提供了一个blocking标识,表示当在缓存中找不到元素时,它设置对缓存键的锁定;这样其他线程将等待此元素被填充,而不是命中数据库。

其实我们使用缓存的目的就是因为被缓存的数据生成比较费时,比如调用对外的接口,查询数据库,计算量很大的结果等等;这时候如果多个线程同时调用get方法获取的结果都为null,每个线程都去执行一遍费时的计算,其实也是对资源的浪费。

最好的办法是只有一个线程去执行,其他线程等待,计算一次就够了;但是此功能基本上都交给使用者来处理,很少有本地缓存有这种功能。

如何实现

以上大致介绍了实现一个本地缓存我们都有哪些需要考虑的地方,当然可能还有其他没有考虑到的点;下面继续看看关于每个点都应该如何去实现,重点介绍一下思路;

1.数据结构

本地缓存最常见的是直接使用Map来存储,比如guava使用ConcurrentHashMap,ehcache也是用了ConcurrentHashMap,Mybatis二级缓存使用HashMap来存储:

Map cache = new ConcurrentHashMap()

Mybatis使用HashMap本身是非线程安全的,所以可以看到起内部使用了一个SynchronizedCache用来包装,保证线程的安全性。面试必问-几种线程安全的Map解析,这篇推荐大家看下。

当然除了使用Map来存储,可能还使用其他数据结构来存储,比如redis使用了双端链表,压缩列表,整数集合,跳跃表和字典;当然这主要是因为redis对外提供的接口很丰富除了哈希还有列表,集合,有序集合等功能;

2.对象上限

本地缓存常见的一个属性,一般缓存都会有一个默认值比如1024,在用户没有指定的情况下默认指定;当缓存的数据达到指定最大值时,需要有相关策略从缓存中清除多余的数据这就涉及到下面要介绍的清除策略;

3.清除策略

配合对象上限之后使用,场景的清除策略如:LRU(最近最少使用)、FIFO(先进先出)、LFU(最近最不常用)、SOFT(软引用)、WEAK(弱引用);LRU:Least Recently Used的缩写最近最少使用,移除最长时间不被使用的对象;常见的使用LinkedHashMap来实现,也是很多本地缓存默认使用的策略;FIFO:先进先出,按对象进入缓存的顺序来移除它们;常见使用队列Queue来实现;LFU:Least Frequently Used的缩写大概也是最近最少使用的意思,和LRU有点像;区别点在LRU的淘汰规则是基于访问时间,而LFU是基于访问次数的;可以通过HashMap并且记录访问次数来实现;SOFT:软引用基于垃圾回收器状态和软引用规则移除对象;常见使用SoftReference来实现;WEAK:弱引用更积极地基于垃圾收集器状态和弱引用规则移除对象;常见使用WeakReference来实现;

4.过期时间

设置过期时间,让缓存数据在指定时间过后自动删除;常见的过期数据删除策略有两种方式:被动删除和主动删除;

被动删除:每次进行get/put操作的时候都会检查一下当前key是否已经过期,如果过期则删除,类似如下代码:

if (System.currentTimeMillis() - lastClear > clearInterval) { clear();}

主动删除:专门有一个job在后台定期去检查数据是否过期,如果过期则删除,这其实可以有效的处理冷数据;

5.线程安全

尽量用线程安全的类去存储数据,比如使用ConcurrentHashMap代替HashMap;或者提供相应的同步处理类,比如Mybatis提供了SynchronizedCache:

public synchronized void putObject(Object key, Object object) { ...省略...}@Overridepublic synchronized Object getObject(Object key) { ...省略...}

提供常用的get,put,remove,clear,getSize方法即可,比如Mybatis的Cache接口:

public interface Cache { String getId(); void putObject(Object key, Object value); Object getObject(Object key); Object removeObject(Object key); void clear(); int getSize(); ReadWriteLock getReadWriteLock();}

再来看看guava提供的Cache接口,相对来说也是比较简洁的:

public interface Cache { V getIfPresent(@CompatibleWith("K") Object key); V get(K key, Callable extends V> loader) throws ExecutionException; ImmutableMap getAllPresent(Iterable> keys); void put(K key, V value); void putAll(Map extends K, ? extends V> m); void invalidate(@CompatibleWith("K") Object key); void invalidateAll(Iterable> keys); void invalidateAll(); long size(); CacheStats stats(); ConcurrentMap asMap(); void cleanUp();}

7.是否持久化

持久化的好处是重启之后可以再次加载文件中的数据,这样就起到类似热加载的功效;比如ehcache提供了是否持久化磁盘缓存的功能,将缓存数据存放在一个.data文件中;

diskPersistent="false" //是否持久化磁盘缓存

redis更是将持久化功能发挥到极致,慢慢的有点像数据库了;提供了AOF和RDB两种持久化方式;当然很多情况下可以配合使用两种方式;

8.阻塞机制

除了在Mybatis中看到了BlockingCache来实现此功能,之前在看<>的时候其中有实现一个很完美的缓存,大致代码如下:

public class Memoizerl implements Computable { private final Map> cache = new ConcurrentHashMap>(); private final Computable c; public Memoizerl(Computable c) { this.c = c; } @Override public V compute(A arg) throws InterruptedException, ExecutionException { while (true) { Future f = cache.get(arg); if (f == null) { Callable eval = new Callable() { @Override public V call() throws Exception { return c.compute(arg); } }; FutureTask ft = new FutureTask(eval); f = cache.putIfAbsent(arg, ft); if (f == null) { f = ft; ft.run(); } try { return f.get(); } catch (CancellationException e) { cache.remove(arg, f); } } } }}

compute是一个计算很费时的方法,所以这里把计算的结果缓存起来,但是有个问题就是如果两个线程同时进入此方法中怎么保证只计算一次,这里最核心的地方在于使用了ConcurrentHashMap的putIfAbsent方法,同时只会写入一个FutureTask;

总结

本文大致介绍了要设计一个本地缓存都需要考虑哪些点:数据结构,对象上限,清除策略,过期时间,线程安全,阻塞机制,实用的接口,是否持久化;当然更多Java学习资料,可以关注“武汉千锋”微信公众号!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/573749.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

process 类 java_编写可执行jar——java的Process类的使用(二)

你知道怎么在控制台使用ping吗&#xff1f;那你知道怎么在java中使用ping吗&#xff1f;1.批处理文件批处理文件大家一定不陌生。接触最多的应该就是tomcat中的start.bat或者start.sh了。bat是在windows环境下运行的批处理文件&#xff0c;sh则是linux的shell脚本。2.adb指令安…

2782: [HNOI2006]最短母串

2782: [HNOI2006]最短母串 Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 3 Solved: 2[Submit][Status][Web Board]Description 给定n个字符串&#xff08;S1,S2,„,Sn&#xff09;&#xff0c;要求找到一个最短的字符串T&#xff0c;使得这n个字符串&#xff08;S1,S2,„,…

c java 内部类_java程序中能否在内部类当中再定义一个内部类?

展开全部我被你的想62616964757a686964616fe78988e69d8331333363386664法震撼了,哈哈.亏你想的出来...这么弄代码不好理解,Java看起来醒目,也是Java中的一个规范!可以吗?必须可以..看代码演示...声明下,我也第一次,多次嵌套,看你想法后去试验下是可行的我用的两种办法!不多说看…

数据库分区分表以及读写分离

谈谈怎么实现Oracle数据库分区表 Oracle数据库分区是作为Oracle数据库性能优化的一种重要的手段和方法&#xff0c;做手头的项目以前&#xff0c;只聆听过分区的大名&#xff0c;感觉特神秘&#xff0c;看见某某高手在讨论会上夸夸其谈时&#xff0c;真是骂自己学艺不精&#x…

JSP Workshop

http://www.cnblogs.com/ITtangtang/p/4126395.html 发现http://www.tutorialspoint.com/里的资料很全也很不错啊&#xff01; 资料&#xff1a;http://www.tutorialspoint.com/jsp/jsp_tutorial.pdf 另外&#xff0c;http://www.runoob.com/jsp/jsp-tutorial.html 中关于JSP…

h5页面不可 移动_H5营销|为什么H5适合于微信营销

随着互联网技术的不断发展&#xff0c;更新在移动互联网时代&#xff0c;网络营销也开始越来越新颖化&#xff0c;而微信H5就是其中的佼佼者。它的出现满足了用户视觉上的审美要求&#xff0c;并且可以使营销方式变得更加的美观整洁&#xff0c;那么这里就有一个问题。为什么微…

亚太地区数学建模优秀论文_数学建模美赛强势来袭!

01美赛&#xff0c;即美国大学生数学建模竞赛(MCM/ICM)又要来啦&#xff01;赛题内容涉及经济、管理、环境、资源、生态、医学、安全、未来科技等众多领域。竞赛要求三人(本科生)为一组&#xff0c;在四天时间内&#xff0c;就指定的问题完成从建立模型、求解、验证到论文撰写的…

《软件调试》读书笔记:第13章 硬错误和蓝屏

会话管理器进程SMSS.exe是系统启动后的第一个用户态进程&#xff0c;负责启动和监护windows子系统进程&#xff1a;CSRSS.exe和登陆管理进程&#xff1a;WinLogonSMSS.exe从注册表中查询子系统exe文件的位置&#xff0c;并且启动它 CSRSS是windows子系统进程&#xff0c;自NT4以…

信息安全技术网络安全等级保护定级指南_行业标准 |报业网络安全等级保护定级参考指南V2.0发布,明确保护对象、定级要求...

近期&#xff0c;中国新闻技术工作者联合会正式发布《报业网络安全等级保护定级参考指南V2.0》。该指南由中国新闻技术工作者联合会组织网络安全领域的专家、报业技术专家以及业务专家经过多次调研、学习、探讨后&#xff0c;在原《报业网络安全等级保护定级参考指南V1.0》的基…

数学作图工具_科研论文作图系列-从PPT到AI (一)

导语&#xff1a;之前的推送中&#xff0c;小编给大家介绍过几款科研作图软件&#xff0c;包括统计分析软件Origin和Prism&#xff0c;图像处理软件ImageJ等等。从本期开始&#xff0c;小编将和大家一起继续学习科研论文作图。重点介绍图像的处理和排版&#xff0c;用到的工具主…

优启通怎么重装系统win10_重装系统失败?小编教你安全给神舟战神GX9 Pro重装win10系统方法...

神舟战神GX9 Pro 上市于2016年1月&#xff0c;虽然现在已经停产&#xff0c;但是还是有不少用户依旧十分喜爱这款笔记本。这款笔记本的性能完全对得起它的售价&#xff0c;很多用户都反馈其比外星人电脑要好。对于爱玩游戏的用户而言&#xff0c;对它17.3英寸的显示屏简直欲罢不…

20135202闫佳歆--week 8 实验:理解进程调度时机跟踪分析进程调度与进程切换的过程--实验及总结...

week 8 实验&#xff1a;理解进程调度时机跟踪分析进程调度与进程切换的过程 1.环境搭建&#xff1a; rm menu -rf git clone https://github.com/megnning/menu.git cd menu ls make rootfs qemu -kernel ../linux-3.18.6/arch/x86/boot/bzImage -initrd ../rootfs.img -s -S …

java jni技术_JNI技术简介

JNI(Java Native Interface)提供一种Java字节码调用C/C的解决方案&#xff0c;JNI描述的是一种技术。NDK(Native Development Kit)Android NDK 是一组允许您将 C 或 C(“原生代码”)嵌入到 Android 应用中的工具&#xff0c;NDK描述的是工具集。 能够在 Android 应用中使用原生…

java web redis_java web redis使用(二)

上篇中已经安装好redis&#xff0c;然后下面就说怎么在java web中用客户端来使用regis使用jedis java客户端一&#xff1a;一个简单的示例代码:public static void main(String[] args) {Jedis jedis new Jedis( "147.151.240.234" , 6379 );jedis.set("foo&…

mysql 主从_搭建mysql主从并编写监控主从状态脚本

要求&#xff1a;两台centos7虚拟机分为主和从安装mysqlyum -y install mysql mysql-server关闭防火墙service iptables stopsetenforce 0上面的主从都做。修改主的配置文件vi /etc/my.cnf#添加server-id1log-binmysqlbinrelay-logrelays之后保存退出重启mysql服务service …

Java核心技术点之动态代理

本篇博文会从代理的概念出发&#xff0c;介绍Java中动态代理技术的使用&#xff0c;并进一步探索它的实现原理。由于个人水平有限&#xff0c;叙述中难免出现不清晰或是不准确的地方&#xff0c;希望大家可以指正&#xff0c;谢谢大家&#xff1a;&#xff09; 一、概述 1. 什么…

shell入门之expr的使用

在expr中加减乘除的使用&#xff0c;脚本例如以下&#xff1a; #!/bin/sh #a test about expr v1expr 5 6 echo "$v1" echo expr 3 5 echo expr 6 / 2 echo expr 9 \* 5 echo expr 9 - 6 执行效果 转载于:https://www.cnblogs.com/bhlsheji/p/5401458.html

java中子线程与主线程通信_Android笔记(三十二) Android中线程之间的通信(四)主线程给子线程发送消息...

之前的例子都是我们在子线程(WorkerThread)当中处理并发送消息&#xff0c;然后在主线程(UI线程)中获取消息并修改UI&#xff0c;那么可以不可以在由主线程发送消息&#xff0c;子线程接收呢&#xff1f;我们按照之前的思路写一下代码&#xff1a;packagecn.lixyz.handlertest;…

java开源笔记软件下载_开发常用软件笔记 - ZhaoQian's Blog - OSCHINA - 中文开源技术交流社区...

notepad使用快捷键CtrlH打开“替换对话框”&#xff0c;在每行的开头添加内容。 勾选左下角的“正则表达式”选项 在“查找目标”里输入上尖号“^”&#xff0c;上尖号代表每行的开头 在“替换为”里输入“private String”。然后点"全部替换"按钮&#xff0c;这样每…

极光推送指定用户推送_App用户都睡着了?是时候用推送和活动唤醒一波了!

想要运营好一款App&#xff0c;引流、留存、促活三大环节必不可少。引流解决了用户来的问题&#xff0c;留存解决了用户留下来的问题&#xff0c;而促活解决的是让一部分新注册用户以及许久没有动静的老用户&#xff0c;在平台中再次活跃起来。今天&#xff0c;我们就来聊聊关于…