如何设计缓存及其应用
缓存的设计一般要用到单例设计模式和资源设计模式,还需要注意多线程同步的问题,以下主要讨论如何设计缓存,什么是数据库连接池,以及Hibernate中的缓存机制。
如何设计缓存
缓存原理
在Java中经常用到缓存,在SSh框架中也会用到一级缓存和二级缓存,到底缓存是怎么实现的呢?
缓存就是利用本地参考原则:当CPU要读取一个数据时,首先从缓存中查找,找到就立即读取并送给CPU处理;没有找到,就用相对慢的速率从内存中读取并送给CPU处理,同时把这个数据所在的数据块调入缓存中,可以使得以后对整块数据的读取都从缓存中进行,不必再调用内存。缓存是一种典型的空间换时间的方案。
缓存就相当于是一个临时内存:它有一个有限的空间量,但访问它比访问原始数据速度要快。
在分布式系统设计中,如果在请求层节点上放置一个缓存,即可响应本地的存储数据。当对服务器发送一个请求时,如果本地存在所请求数据,那么该节点即会快速返回本地缓存数据。如果本地不存在,那么请求节点将会查询磁盘上的数据。请求层节点缓存即可以存在于内存中(这个非常快速)也可以位于该节点的本地磁盘上(比访问网络存储要快)。全局缓存是指所有节点都使用同一个缓存空间;分布式缓存即缓存在分布式系统各节点内存中的缓存数据。
开源缓存产品
一个非常流行的开源缓存产品:Memcached(即可以在本地缓存上工作也可以在分布式缓存上工作)。Memcached用于许多大型Web站点,其非常强大。Memcached基于一个存储键/值对的hashmap,优化数据存储和实现快速搜索(O(1))。
如何设计缓存
在Java中最常见的一种实现缓存的方式就是使用Map,基本的步骤是:
1)先到缓存里面查找,看看是否存在需要使用的数据
2)如果没有找到,那么就创建一个满足要求的数据,然后把这个数据设置回到缓存中,以备下次使用
3)如果找到了相应的数据,或者是创建了相应的数据,那就直接使用这个数据。
下面只是缓存的基本实现,还有很多功能都没有考虑,比如缓存的清除,缓存的同步等等。现在有很多专业的缓存框架。
缓存接口设计public Interface<E,V> ICashe(){ public void size(int max); //设置缓存池大小 public void add(E e); //往缓存中添加数据 public void remove(E e); //从缓存中移除数据 public void update(E e); //更新缓存中的数据 public E get(V v);//从缓存中获取符合条件的值 public void clear(); //清除缓存中数据 public boolean empty(); //判断缓存是否为空 public boolean full(); //判断缓存是否已满 }//end Interface ICashe
public class Cashe Implement ICashe{ private volatile static Singleton uniqueInstance=null; private Map<String,Object> map = new HashMap<String,Object>(); //缓存数据的容器 private Singleton(){ } public static Singleton getInstance() { if(uniqueInstance == null){ synchronized (Singleton.class){ //note we only synchronize the first time through! if(uniqueInstance == null) //once in the block,check again and if still null, create an instance uniqueInstance=new Singleton(); }//end synchronized }//end if return uniqueInstance; }//end getInstance() //other useful methods here @override public E get(V v){ //先从缓存里面取值 E e = map.get(v); //判断缓存里面是否有值 if(e == null){ //如果没有,那么就去获取相应的数据,比如读取数据库或者文件 //这里只是演示,所以直接写个假的值 e = v+",value"; //把获取的值设置回到缓存里面 map.put(v, e); } //end if //如果有值了,就直接返回使用 return e; }//end get() @override public void add(E e){ //TODO } @override public void remove(E e){ //TODO } @override public void update(E e){ //TODO } @override public void clear() { //TODO } @override public boolean empty(){ //TODO } @override public boolean full(){ //TODO } }//end class Singleton
数据库连接池
为什么使用数据库连接池
在传统的两层结构中,客户端程序在启动时打开数据库连接,在退出程序时关闭数据库连接。这样,在整个程序运行中,每个客户端始终占用一个数据库连接,即使在大量没有数据库操作的空闲时间,如用户输入数据时,从而造成数据库连接的使用效率低下。
对于Connection这样的资源,初始化的开销是很大的,因为建立连接必须进行Socket连接,验证以及授权等繁杂的操作,代价是昂贵的,因此及早初始化一定量打开的连接,并且缓存起来是一个相当不错的策略。
那么怎么做呢?对于共享资源,有一个很著名的设计模式:资源池 (Resource Pool)。该模式正是为了解决资源的频繁分配?释放所造成的问题。为解决上述问题,可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量?使用情况,为系统开发?测试及性能调整提供依据。
因此,在前面的两层结构中,加入一层-数据库连接池。在三层结构模式中,数据库连接通过中间层的连接池管理。只有当用户真正需要进行数据库操作时,中间层才从连接池申请一个连接,数据库操作完毕,连接立即释放到连接池中,以供其他用户使用。这样,不仅大大提高了数据库连接的使用效率,使得大量用户可以共享较少的数据库连接,而且省去了建立连接的时间。
如何设计一个数据库连接池
通常用连接池管理类(DBConnectionManager)来使用数据库连接池( DBConnectionPool)。因为系统中只能有一个连接池管理类的实例,所以连接池管理类的实现是单例模式。
连接池管理类主要用于对多个连接池对象的管理,具有以下功能:①装载并注册特定数据库的JDBC驱动程序;②根据属性文件给定的信息,创建连接池对象;③为方便管理多个连接池对象,为每一个连接池对象取一个名字,实现连接池名字与其实例之间的映射;④跟踪客户使用连接情况,以便需要是关闭连接释放资源。连接池管理类的引入主要是为了方便对多个连接池的使用和管理,如系统需要连接不同的数据库,或连接相同的数据库但由于安全性问题,需要不同的用户使用不同的名称和密码。
数据库连接池接口设计
//用数据库连接池接口设计
public interface DBConnectionPool<E> {
//最大最小连接数
public synchronized void size(int min,int max);
//新建一个数据库连接
public synchronized E newConnection(String name,String URL,String user,String password);
//得到一个连接,timeout是等待时间
public synchronized E getConnection(long timeout);
//断开所有连接,释放占用的系统资源
public synchronized void release();
//使用完毕之后,把连接返还给空闲池
public synchronized void freeConnection(Connection con);
} //end interface DBConnectionPool
连接池管理类
public class DBConnectionManager {
static private DBConnectionManager instance;
//连接池管理类的唯一实例
static private int clients;//客户数量
private ArrayList drivers=new ArrayList();
//容器,存放数据库驱动程序
private HashMap pools = new HashMap();
//以name/value的形式存取连接池对象的名字及连接池对象
static synchronized public DBConnectionManager getInstance()
/**如果唯一的实例instance已经创建,直接返回这个实例;否则,调用私有构造函数,
创建连接池管理类的唯一实例*/
private DBConnectionManager()
//私有构造函数,在其中调用初始化函数init()
public void freeConnection(String name,Connection con)
//释放一个连接,name是一个连接池对象的名字
public Connection getConnection(String name)
//从名字为name的连接池对象中得到一个连接
public Connection getConnection(String name,long time)
//从名字为name的连接池对象中取得一个连接,time是等待时间
public synchronized void release()//释放所有资源
private void createPools(Properties props)
//根据属性文件提供的信息,创建一个或多个连接池
private void init()//初始化连接池管理类的唯一实例,由私有构造函数调用
private void loadDrivers(Properties props)//装载数据库驱动程序
} //end DBConnectionManager
Hibernate缓存TODO
一级缓存
二级缓存
Hibernate优化