zoukankan      html  css  js  c++  java
  • 【转】DataSource高级应用

    我们知道 DataSource能提供最高性能的对数据库的并发访问,
    但是,对DataSource的引用,也还有很多知识要弄清楚,获取Connection的方式是数据库性能最相
    关的技术,而对DataSource的调用对数据库性能起着很大的决定作用。
        一般对于DataSource的引用是通过以下流程来进行:

     Context ct = new InitialContext();
     DataSource ds = (DataSource) ct.lookup(sourceUrl);

        就这么简单的两行,但其调用条件不同却可以产生性能上巨大的差别。因为一个取得
    Connect的封装类(Bean)要对DataSource的引用,会有着多种方式。如果是作为一个普通的封
    装类,我们可以在构造方法中调用,而作为javaBean可以在init方法中调用,但无论要哪儿调用,
    当我们对这个类或Bean进行调用时,都要对DataSource进行查找。其实DataSource的查找过程也
    是一个相当消耗资源的过程,所以我们应该把DataSource声明为静态资源,因为即使你对封装类
    或Bean中每次调用都进行一次查找,事实上你得到的DataSource还是那个不变的唯一的资源。那
    么为什么不把它声明为static呢?这样只在需要的时候才去查找,而更多的时候就可以直接对已
    经查找到的DataSource进行直接引用:

    public class ConnectionFactory{
         static DataSource dsCache = null;
    
        aMethodForGetConnectio(){
            if(dsCache == null){
                synchronized(this){
                    if(dsCache==null){
                        Context ct = new InitialContext();
                        dsCache = (DataSource) ct.lookup(source);
                    }
                }
            }
            Connection conn = dsCache.getConnection();
        }
    }

    我们看到,一般情况下,只有当第一次调用ConnectionFactory时才会对DataSource进行查找,而
    其它时候只要不发生意外(目前我还想不出有什么情况会使原来不是null的DataSource突然null)
    就不再需要查找而直接引用了.而如果你不把DataSource声明为静态的,那么每次对调用
    ConnectionFactory都要产生一次jndi的查找:

    public class ConnectionFactory{
         DataSource dsCache = null;
    
        aMethodForGetConnectio(){
            if(dsCache == null){
                Context ct = new InitialContext();
                dsCache = (DataSource) ct.lookup(source);
            }
            Connection conn = dsCache.getConnection();
        }
    }

    因为dsCache不是静态资源,所以每次调用,if(dsCache==null)都成立.
    有人说那Connection如果也是静态的那不也节省资源了吗?注意Connection不是工场,如果多个用
    户同时对同一Connection访问,那么任何人都不能关闭Connection,而最后变成谁也没有关闭
    Connection,因为最后一次使用的用户根本不知道后面到底是否有人要使用.另一方面,一个
    Connection处理能力很低,不可能同时满足很多用户同时对数据库存取数据.而DataSource虽然是
    多个用户对同一资源的引用,但它是工厂,不同用户访问同一DataSource得到的是不同的Connection
    资源.

    为了对特定DataSource进行查找,我们要对jndi的引用进行配置,一般来说,如果一个封装类或Bean
    不可能将jndi引用字符串写死要代码中,可以放在配置文件中然后读取:

        Properties properties = new Properties();
        properties.load(new FileInputStream("配置文件"));
        dsCache = (DataSource) ct.lookup(properties.getProperty("key"));

    这样在我们要修改DataSource时就可以难过修改"配置文件"而不要重新编译ConnectionFactory,但
    是如果是对一个容器下的多个应用同时配置了多个DataSource,我们根本无法指定到底查打哪个数据
    源,所以最好能重载一个方法,无论是构造方法还是其它方法都应该重载一个根据参数查找的方法:

    public class ConnectionFactory{
         static DataSource dsCache = null;
    
        aMethodForGetConnectio(){
            if(dsCache == null){
                synchronized(this){
                    if(dsCache==null){
                        Properties properties = new Properties();
                        properties.load(new FileInputStream("配置文件"));
                        Context ct = new InitialContext();
                        dsCache = (DataSource) ct.lookup(properties.getProperty("key"));
                    }
                }
            }
            Connection conn = dsCache.getConnection();
        }
        aMethodForGetConnectio(String source){
            if(dsCache == null){
                synchronized(this){
                    if(dsCache==null){
                        Context ct = new InitialContext();
                        dsCache = (DataSource) ct.lookup(source);
                    }
                }
            }
            Connection conn = dsCache.getConnection();
        }
    }

    另外对于默认的查找字符串,我们仍然可以做进一步的优化,不过即使每次读取属性,性能也没有太大的影响:

    public class ConnectionFactory{
         static DataSource dsCache = null;
         static Properties properties = new Properties();
        aMethodForGetConnectio(){
            if(dsCache == null){
                synchronized(this){
                    if(dsCache==null){
                        if(!properties.containsKey("key"))
                            properties.load(new FileInputStream("配置文件"));
                        //只有在没有找到属性时才去再读配置文件
                        Context ct = new InitialContext();
                        dsCache = (DataSource) ct.lookup(properties.getProperty("key"));
                    }
                }
            }
            Connection conn = dsCache.getConnection();
        }
    }

    通过以上优化,可以使你的封装类以最低的资源消耗来获取最大的性能.我们可以这样来进行测试:
    把if(dsCache == null){}发生时的情况记录到日志中,看看什么时候发生了这种异常,我对我的数据库进行了
    跟踪,我们给青岛日报社做的一台邮件系统主机上有6万多用户,同时并发的访问量很大,但日记记录,只有每次
    重启动应用的时候才产生一条记录,也就是ConnectionFactory第一次调用时才产生日志,其它情况非常正常:

    public class ConnectionFactory{
         static DataSource dsCache = null;
         static Properties properties = new Properties();
        aMethodForGetConnectio(){
            if(dsCache == null){
                synchronized(this){
                    if(dsCache==null){
                        if(!properties.containsKey("key"))
                            properties.load(new FileInputStream("配置文件"));
                        //只有在没有找到属性时才去再读配置文件
                        Context ct = new InitialContext();
                        dsCache = (DataSource) ct.lookup(properties.getProperty("key"));
                    }
                }
                //当发生dsCache == null时,记录下发生对该方法的调用者:
                PrintWriter pw = new PrintWriter(new FileWriter("connection.log",true));
                pw.println(new java.util.Date() + ":当前方法名如aMethodForGetConnectio():"
                        + sun.reflect.Reflection.getCallerClass(2));
                pw.close();
            }
            Connection conn = dsCache.getConnection();
        }
    }

    主要记录当前日期,当前方法名称和是哪个类调用了该方法.sun.reflect.Reflection.getCallerClass(int i)
    方法中,i为0是Reflection本身,1是Bean或封装类,就是ConnectionFactory,2就是调用它的类,3以上不确定.这样
    你可以把你的封装类或Bean进行一段时间(至少几天以上)跟踪,如果应用一直没有重启动,那就应该只产生一条
    记录.这样你就有了一个顶级性能的封装类了

    转自:

    DataSource高级应用

  • 相关阅读:
    Leetcode题目practice
    文件操作
    39个奇葩代码注释,拿走不谢
    Spring Boot 之配置导入,强大到不行!
    Git 的这个神技,学会爽歪歪~
    同事天天写垃圾代码,就没办法?
    for (;;) 与 while (true),哪个更快?
    Spring Boot 怎么打一个可执行 Jar 包?
    程序员真的是太太太太太太太太难了!
    面试官:new一个对象有哪两个过程?
  • 原文地址:https://www.cnblogs.com/549294286/p/3559758.html
Copyright © 2011-2022 走看看