yeshucheng
追逐自己,追逐方向,心随悟所动
posts - 24,comments - 24,trackbacks - 0

在JDBC应用中,如果你已经是稍有水平开发者,你就应该始终以PreparedStatement代替Statement.也就是说,在任何时候都不要使用Statement.
基于以下的原因:
一.代码的可读性和可维护性.
虽然用PreparedStatement来代替Statement会使代码多出几行,但这样的代码无论从可读性还是可维护性上来说.都比直接用Statement的代码高很多档次:

stmt.executeUpdate("insert into tb_name (col1,col2,col2,col4) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");

perstmt = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)");
perstmt.setString(1,var1);
perstmt.setString(2,var2);
perstmt.setString(3,var3);
perstmt.setString(4,var4);
perstmt.executeUpdate();

不用我多说,对于第一种方法.别说其他人去读你的代码,就是你自己过一段时间再去读,都会觉得伤心.

二.PreparedStatement尽最大可能提高性能.
每一种数据库都会尽最大努力对预编译语句提供最大的性能优化.因为预编译语句有可能被重复调用.所以语句在被DB的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中(相当于一个涵数)就会得到执行.这并不是说只有一个 Connection中多次执行的预编译语句被缓存,而是对于整个DB中,只要预编译的语句语法和缓存中匹配.那么在任何时候就可以不需要再次编译而可以直接执行.而statement的语句中,即使是相同一操作,而由于每次操作的数据不同所以使整个语句相匹配的机会极小,几乎不太可能匹配.比如:
insert into tb_name (col1,col2) values ('11','22');
insert into tb_name (col1,col2) values ('11','23');
即使是相同操作但因为数据内容不一样,所以整个个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存.这样每执行一次都要对传入的语句编译一次.

当然并不是所以预编译语句都一定会被缓存,数据库本身会用一种策略,比如使用频度等因素来决定什么时候不再缓存已有的预编译结果.以保存有更多的空间存储新的预编译语句.

三.最重要的一点是极大地提高了安全性.

即使到目前为止,仍有一些人连基本的恶义SQL语法都不知道.
String sql = "select * from tb_name where name= '"+varname+"' and passwd='"+varpasswd+"'";
如果我们把[' or '1' = '1]作为varpasswd传入进来.用户名随意,看看会成为什么?

select * from tb_name = '随意' and passwd = '' or '1' = '1';
因为'1'='1'肯定成立,所以可以任何通过验证.更有甚者:
把[';drop table tb_name;]作为varpasswd传入进来,则:
select * from tb_name = '随意' and passwd = '';drop table tb_name;有些数据库是不会让你成功的,但也有很多数据库就可以使这些语句得到执行.

而如果你使用预编译语句.你传入的任何内容就不会和原来的语句发生任何匹配的关系.(前提是数据库本身支持预编译,但上前可能没有什么服务端数据库不支持编译了,只有少数的桌面数据库,就是直接文件访问的那些)只要全使用预编译语句,你就用不着对传入的数据做任何过虑.而如果使用普通的statement, 有可能要对drop,;等做费尽心机的判断和过虑.

上面的几个原因,还不足让你在任何时候都使用PreparedStatement吗?

 

 

有的新人可能此时对于用法还不太理解下面给个小例子

Code Fragment 1:

String updateString = "UPDATE COFFEES SET SALES = 75 " + "WHERE COF_NAME LIKE ′Colombian′"; 
stmt.executeUpdate(updateString);

Code Fragment 2:

PreparedStatement updateSales = con.prepareStatement("UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? "); 
updateSales.setInt(1, 75); 
updateSales.setString(2, "Colombian"); 
updateSales.executeUpdate();

set中的1对应第一个? 2对应第二个? 同时注意你set 的类型 是int还是string  哈哈很简单吧


原文出处:http://blog.csdn.net/spcusa/archive/2009/05/09/4164076.aspx

posted @ 2010-12-14 13:58 叶澍成| 编辑 收藏

//ValueObject类

public class AdEntity {
    
private String id;
    
private String name;
    
private String broker;
    
private String date;
    
private String body;
    
//get/set
    
    
public String toString(){
        
return "【编号为:"+id+",广告名称为:"+name+",代理商为:"+broker+",发布日期为:"+date+",内容为:"+body+"";
    }
}

//调用任务类

public class AdTask implements Callable<AdEntity> {
    @Override
    
public AdEntity call() throws Exception {
        
// 模拟远程调用花费的一些时间
        Thread.sleep(5*1000);
        AdEntity vo
=new AdEntity();
        vo.setId(String.valueOf(
new Random().nextInt(1000)));
        vo.setName(
"Ad@内衣广告");
        vo.setBroker(
"CHANNEL");
        Date date
=new Date();
        SimpleDateFormat sdf
=new SimpleDateFormat("yyyy-MM-dd");
        String dateStr
=sdf.format(date);
        vo.setDate(dateStr);
        vo.setBody(
"远端内容");
        
return vo;
    }
}

//主函数

public class TestQueryMemg {
    
    
/**
     * 
@param args
     * 
@throws ExecutionException 
     * 
@throws InterruptedException 
     
*/
    
public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService exec
=Executors.newCachedThreadPool();
        
//创建Future来完成网络调用任务
        Callable<AdEntity> returnAd=new AdTask();
        Future
<AdEntity> future=exec.submit(returnAd);
        
        
//开始执行本地化查询信息
        AdEntity localAd=new AdEntity();
        localAd.setId(String.valueOf(
new Random().nextInt(1000)));
        localAd.setName(
"Ad@食品广告");
        localAd.setBroker(
"蒙牛");
        SimpleDateFormat sdf
=new SimpleDateFormat("yyyy-MM-dd");
        String dateStr
=sdf.format(new Date());
        localAd.setDate(dateStr);
        localAd.setBody(
"内容本地");

        System.out.println(
"当前本地化查询内容为:"+localAd.toString());
        System.out.println(
"稍等片刻以获取远端信息");
        
        
while(!future.isDone()){
            
try {
                Thread.sleep(
1*1000);
            } 
catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        AdEntity entityRemote
=(AdEntity)future.get();

        System.out.println(
"合并查询内容为:\n"+localAd.toString()+"\n"+entityRemote.toString());
    }
}


 

 

 

posted @ 2010-12-12 14:44 叶澍成| 编辑 收藏

个人账户类:

public class PrivateAccount implements Callable {
    Integer total;
    
public Object call() throws Exception {
        Thread.sleep(
5*1000);
        total
=new Integer(new Random().nextInt(10000));
        System.out.println(
"您个人账户上还有"+total+" 存款可以支配");
        
return total;
    }
}

主函数测试:

public class SumTest {
    
/**
     * 
@param args
     * 
@throws ExecutionException 
     * 
@throws InterruptedException 
     
*/
    
public static void main(String[] args) throws InterruptedException, ExecutionException {
        Callable privateAccount
=new PrivateAccount();
        FutureTask task
=new FutureTask(privateAccount);
                
//创建新线程获取个人账户信息
        Thread thread=new Thread(task);
        thread.start();

        
int total=new Random().nextInt(1000);
        System.out.println(
"主线程在这工作");
        System.out.println(
"您目前操作金额为: "+total+" .");
        System.out.println(
"请等待计算个人账户的金额");
        
while(!task.isDone()){//判断是否已经获取返回值
            try {
                Thread.sleep(
3*1000);
            } 
catch (InterruptedException e) {
                
// TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        Integer privateSingle
=(Integer)task.get();
        
int post=privateSingle.intValue();
        
        System.out.println(
"您当前账户共有金额为:"+(total+post)+" ¥");
    }

}


 

 

posted @ 2010-12-10 20:53 叶澍成| 编辑 收藏
Memcached,人所皆知的remote distribute cache(不知道的可以javaeye一下下,或者google一下下,或者baidu一下下,但是鉴于baidu的排名商业味道太浓(从最近得某某事件可以看出),所以还是建议javaeye一下下),使用起来也非常的简单,它被用在了很多网站上面,几乎很少有大型的网站不会使用memcached。

曾经我也看过很多剖析memcached内部机制的文章,有一点收获,但是看过之后又忘记了,而且没有什么深刻的概念,但是最近我遇到一个问题,这个问题迫使我重新来认识memcache,下面我阐述一下我遇到的问题

问题:我有几千万的数据,这些数据会经常被用到,目前来看,它必须要放到memcached中,以保证访问速度,但是我的memcached中数据经常会有丢失,而业务需求是memcached中的数据是不能丢失的。我的数据丢失的时候,memcached server的内存才使用到60%,也就是还有40%内存被严重的浪费掉了。但不是所有的应用都是这样,其他应用内存浪费的就比较少。为什么内存才使用到60%的时候LRU就执行了呢(之所以确定是LRU执行是因为我发现我的数据丢失的总是前面放进去的,而且这个过程中,这些数据都没有被访问,比如第一次访问的时候,只能访问第1000w条,而第300w条或者之前的数据都已经丢失了,从日志里看,第300w条肯定是放进去了)。

带着这些疑问,我开始重新审视memcached这个产品,首先从它的内存模型开始:我们知道c++里分配内存有两种方式,预先分配和动态分配,显然,预先分配内存会使程序比较快,但是它的缺点是不能有效利用内存,而动态分配可以有效利用内存,但是会使程序运行效率下降,memcached的内存分配就是基于以上原理,显然为了获得更快的速度,有时候我们不得不以空间换时间。

也就是说memcached会预先分配内存,对了,memcached分配内存方式称之为allocator,首先,这里有3个概念:
1 slab
2 page
3 chunk
解释一下,一般来说一个memcahced进程会预先将自己划分为若干个slab,每个slab下又有若干个page,每个page下又有多个chunk,如果我们把这3个咚咚看作是object得话,这是两个一对多得关系。再一般来说,slab得数量是有限得,几个,十几个,或者几十个,这个跟进程配置得内存有关。而每个slab下得page默认情况是1m,也就是说如果一个slab占用100m得内存得话,那么默认情况下这个slab所拥有得page得个数就是100,而chunk就是我们得数据存放得最终地方。

举一个例子,我启动一个memcached进程,占用内存100m,再打开telnet,telnet localhost 11211,连接上memcache之后,输入stats  slabs,回车,出现如下数据:
Java代码 复制代码
  1. STAT 1:chunk_size 80  
  2. STAT 1:chunks_per_page 13107  
  3. STAT 1:total_pages 1  
  4. STAT 1:total_chunks 13107  
  5. STAT 1:used_chunks 13107  
  6. STAT 1:free_chunks 0  
  7. STAT 1:free_chunks_end 13107  
  8. STAT 2:chunk_size 100  
  9. STAT 2:chunks_per_page 10485  
  10. STAT 2:total_pages 1  
  11. STAT 2:total_chunks 10485  
  12. STAT 2:used_chunks 10485  
  13. STAT 2:free_chunks 0  
  14. STAT 2:free_chunks_end 10485  
  15. STAT 3:chunk_size 128  
  16. STAT 3:chunks_per_page 8192  
  17. STAT 3:total_pages 1  
  18. STAT 3:total_chunks 8192  
  19. STAT 3:used_chunks 8192  
  20. STAT 3:free_chunks 0  
  21. STAT 3:free_chunks_end 8192  


以上就是前3个slab得详细信息
chunk_size表示数据存放块得大小,chunks_per_page表示一个内存页page中拥有得chunk得数量,total_pages表示每个slab下page得个数。total_chunks表示这个slab下chunk得总数(=total_pages * chunks_per_page),used_chunks表示该slab下已经使用得chunk得数量,free_chunks表示该slab下还可以使用得chunks数量。

从上面得示例slab 1一共有1m得内存空间,而且现在已经被用完了,slab2也有1m得内存空间,也被用完了,slab3得情况依然如此。 而且从这3个slab中chunk得size可以看出来,第一个chunk为80b,第二个是100b,第3个是128b,基本上后一个是前一个得1.25倍,但是这个增长情况我们是可以控制得,我们可以通过在启动时得进程参数 –f来修改这个值,比如说 –f 1.1表示这个增长因子为1.1,那么第一个slab中得chunk为80b得话,第二个slab中得chunk应该是80*1.1左右。

解释了这么多也该可以看出来我遇到得问题得原因了,如果还看不出来,那我再补充关键的一句:memcached中新的value过来存放的地址是该value的大小决定的,value总是会被选择存放到chunk与其最接近的一个slab中,比如上面的例子,如果我的value是80b,那么我这所有的value总是会被存放到1号slab中,而1号slab中的free_chunks已经是0了,怎么办呢,如果你在启动memcached的时候没有追加-M(禁止LRU,这种情况下内存不够时会out of memory),那么memcached会把这个slab中最近最少被使用的chunk中的数据清掉,然后放上最新的数据。这就解释了为什么我的内存还有40%的时候LRU就执行了,因为我的其他slab中的chunk_size都远大于我的value,所以我的value根本不会放到那几个slab中,而只会放到和我的value最接近的chunk所在的slab中(而这些slab早就满了,郁闷了)。这就导致了我的数据被不停的覆盖,后者覆盖前者。

问题找到了,解决方案还是没有找到,因为我的数据必须要求命中率时100%,我只能通过调整slab的增长因子和page的大小来尽量来使命中率接近100%,但是并不能100%保证命中率是100%(这话怎么读起来这么别扭呢,自我检讨一下自己的语文水平),如果您说,这种方案不行啊,因为我的memcached server不能停啊,不要紧还有另外一个方法,就是memcached-tool,执行move命令,如:move 3 1,代表把3号slab中的一个内存页移动到1号slab中,有人问了,这有什么用呢,比如说我的20号slab的利用率非常低,但是page却又很多,比如200,那么就是200m,而2好slab经常发生LRU,明显page不够,我就可以move 20 2,把20号slab的一个内存页移动到2号slab上,这样就能更加有效的利用内存了(有人说了,一次只移动一个page,多麻烦啊?ahuaxuan说,还是写个脚本,循环一下吧)。

有人说不行啊,我的memcache中的数据不能丢失啊,ok,试试新浪的memcachedb吧,虽然我没有用过,但是建议大家可以试试,它也使利用memcache协议和berkeleyDB做的(写到这里,我不得不佩服danga了,我觉得它最大的贡献不是memcache server本身,而是memcache协议),据说它被用在新浪的不少应用上,包括新浪的博客。

补充,stats slab命令可以查看memcached中slab的情况,而stats命令可以查看你的memcached的一些健康情况,比如说命中率之类的,示例如下:
Java代码 复制代码
  1. STAT pid 2232  
  2. STAT uptime 1348  
  3. STAT time 1218120955  
  4. STAT version 1.2.1  
  5. STAT pointer_size 32  
  6. STAT curr_items 0  
  7. STAT total_items 0  
  8. STAT bytes 0  
  9. STAT curr_connections 1  
  10. STAT total_connections 3  
  11. STAT connection_structures 2  
  12. STAT cmd_get 0  
  13. STAT cmd_set 0  
  14. STAT get_hits 0  
  15. STAT get_misses 0  
  16. STAT bytes_read 26  
  17. STAT bytes_written 16655  
  18. STAT limit_maxbytes 104857600  

从上面的数据可以看到这个memcached进程的命中率很好,get_misses低达0个,怎么回事啊,因为这个进程使我刚启动的,我只用telnet连了一下,所以curr_connections为1,而total_items为0,因为我没有放数据进去,get_hits为0,因为我没有调用get方法,最后的结果就是misses当然为0,哇哦,换句话说命中率就是100%,又yy了。

该到总结的时候了,从这篇文章里我们可以得到以下几个结论:
结论一,memcached得LRU不是全局的,而是针对slab的,可以说是区域性的。
结论二,要提高memcached的命中率,预估我们的value大小并且适当的调整内存页大小和增长因子是必须的。
结论三,带着问题找答案理解的要比随便看看的效果好得多。
posted @ 2010-08-17 17:45 叶澍成| 编辑 收藏