• 多对多连接可以转为使用两个指向中间表的一对多的连接比较好

  • 在做关系尽可能使用单向关联,不要使用双向关联

  • 在大项目中,(数据量如果超过百万条的项目),使用Hibernate可以酌情考虑一下几个原则:

  • 1.不要使用对象关联,尽可能用冗余字段来替代外键(使用冗余字段所带来的问题就是在修改时必须修改所有的冗余字段)

  • 2.当某些操作对系统的性能带来瓶颈时,这些操作也许直接使用JDBC方式会更好。如:查询数据不再使用HQL,而使用SQL查询,如果涉及缓存,自己根据情况加入相应的缓存,而不是使用Hibernate的缓存。

  2.1如果查询的是单张数据库表的信息

Session =HibernateUtil.openSession();
List<Student> stus=session.createSQLQuery("select * from t_stu where name like ?")
      .addEntity(Student.class)
      .setParameter(0,"%莫%")
      .list();

  2.2如果查询的是多张数据库表的关联信息

Session session=null;
try{
      session=HibernateUtil.openSession();
      List<Object[]> stus=session.createSQLQuery("select (stu.*),(cla.*),(spe.*) from "+
           "t_stu stu left join t_classroom cla on(stu.cid=cid.id) "+
           "left join t_special spe on(spe.id=cla.spe_id) where stu.name like ?")
           .addEntity("stu",Student.class)
           .addEntity("cla",Classroom.class)
           .addEntity("spe",Special.class)
           .setParameter(0,"%莫%")
           .setFirstResult(0).setMaxResults(10)
           .list();
           Student stu=null;
           Classroom cla=null;
           Special spe=null;
           for(Object[] obj:suts){
                 stu=(Student)obj[0];
                 stu=(Classroom)obj[1];
                 stu=(Special)obj[2];
 System.out.println(stu.getName()+","+cla.getName()+","+spe.getName());
           }
}

  2.3使用DTO(只用于传输属性的对象,并不用于与数据库的交互)代替类对象:

Session session=null;
try{
      session =HibernateUtil.openSession();
      List<StudentDto> stus=session.createSQLQuery("select stu.id as sid,stu.name as name,"+
           "stu.sex as sex,cla.name as cname,spe.name as spename from "+
           "t_stu stu left join t_classroom cla on(stu.cid=cls.id) "+
           "left join t_special spe on(spe.id=cls.spe_id) where stu.name like ?")
 .setResultTransformer(Transformers.aliasToBean(StudentDto.class))//将结果集进行一次封装,封装成StudentDto对象,方便service层代码的取用
           .setParameter(0,"%莫%")
           .setFirstResult(0).setMaxResult(10)
           .list();
      for(StudentDto sd:stus){
           System.out.println(sd);
      }
}
  • 就像在 JDBC 编程中一样,应该总是用占位符 “?” 来替换非常量值,不要在查询中用字符串值来构造非常量值。你也可以考虑在查询中使用命名参数。

  • 不要自己来管理 JDBC 连接:

Hibernate 允许应用程序自己来管理 JDBC 连接,但是不推荐。如果你不能使用 Hibernate 内建的 connections providers,那么考虑实现自己来实现org.hibernate.connection.ConnectionProvider。

  • 考虑使用用户自定义类型(custom type):

如果持久化的类没有提供映射操作需要的存取方法,那么该类才可以通过实现org.hibernate.UserType接口,来实现类属性类型与 Hibernate type之间的相互转换。

  • 当异常发生的时候,必须要回滚Transaction ,关闭 Session。

  • 对于关联来说,若其目标是无法在第二级缓存中完全缓存所有实例的类,应该使用代理(proxies)与/或具有延迟加载属性的集合(lazy collections)。若目标是可以被缓存的,尤其是缓存的命中率非常高的情况下,应该使用lazy=”false”

  • 图片使用img元素还是backgroud.url属性

如果图片在页面以后会替换的,就使用img元素,否则应该使用backgroud.url();引入图片。

  • 根据文档内容的结构而选择Html元素

设置HTML时一个常见的错误就是过度使用div和span。少量、必要的合理使用可以明显地增强文档的结构性。但若是文档中使用了太多的div和span,那么你就应该考虑一下是不是还有更加合适的HTML元素供选择了。例如:若是h3更能表示内容的含义,那么就不应该使用div,span也不应该代替label的作用。

  • CSS文件尽量使用UTF-8编码

考虑到UTF-8在处理多语言时的强大之处,若是相关服务器端软件和开发工具能够支持的话,我们应该尽可能地选择使用UTF-8编码。

  • 元素的内容居中

  • 让容器div居中

 1.非IE的方式:非IE可以通过设置margin属性使元素水平居

div#container{
    margin:auto;
    width:168px;
}

 2.IE方式:IE需要先知道屏幕的分辨率的宽度是多少,例如:1100px的时候,使用如下方式:

#container{
    position:absolute;
    left:50%;
    margin-left:-550px;
}
  • 使用text-align实现文字居中

 1.对<p>、<body>、<div>等块元素,可以使用text-align属性使文字居中

p{
    text-align:center;
}

 2.表格的td居中

<td style="text-align: center;">是否下发
    <selectid="isXF">
        <optionvalue="y">同意</option>
        <optionvalue="n">不同意</option>
    </select>
</td>
  • 解决不同浏览器的边距问题
*{
     padding:0px;
     margin:0px;
}
  • 解决IE6等和firefox/chrome的margin的比例不一样的问题
dl.index_r_article {
    ...
    margin-left:20px; //所有浏览器都可以看到
    *margin-left:10px; //只有ie6/7浏览器能看见,就会覆盖前面的
}
  • 解决IE6不支持dl、dd的问题

ie6不支持dl中添加div,可以考虑把dl标签改为ul标签 ,dd标签改为li标签

  • 不要在属性值与单位之间留有空格

假如你使用 “margin-left: 20 px” 而不是 “margin-left: 20px” ,它仅能工作于 IE6,但是在 Mozilla/Firefox 或 Netscape 中却无法正常工作

  • 不允许为a、span标签设置width属性

在html,有一些标签仅仅只是用来设置文本,诸如:a和span对于这两个标签而言,在W3C的标准中默认是不能进行width等样式进行修饰的,所以直接为这些标签设置width是没有作用的,需要在样式加display:block(添加区域)之后才有作用。但是对于IE而言就是有作用。

  • 未完待续

memcached的工作原理

Memcached的高性能源于两阶段哈希(two-stage hash)结构。Memcached就像一个巨大的、存储了很多<key,value>对的哈希表。通过key,可以存储或查询任意的数据。客户端可以把数据存储在多台memcached上。当查询数据时,客户端首先参考节点列表计算出key的哈希值(阶段一哈希),进而选中一个节点;客户端将请求发送给选中的节点,然后memcached节点通过一个内部的哈希算法(阶段二哈希),查找真正的数据(item)并返回给客户端。从实现的角度看,memcached是一个非阻塞的、基于事件的服务器程序。

memcached的cache机制

Memcached主要的cache机制是LRU(最近最少用)算法+超时失效。当您存数据到memcached中,可以指定该数据在缓存中可以呆多久。如果memcached的内存不够用了,过期的slabs会优先被替换,接着就轮到最老的未被使用的slabs。

适用memcached的业务场景

  • 如果网站包含了访问量很大的动态网页,因而数据库的负载将会很高。由于大部分数据库请求都是读操作,那么memcached可以显著地减小数据库负载。

  • 如果数据库服务器的负载比较低但CPU使用率很高,这时可以缓存计算好的结果( computed objects )和渲染后的网页模板(enderred templates)。

  • 利用memcached可以缓存session数据、临时数据以减少对他们的数据库写操作。

  • 缓存一些很小但是被频繁访问的文件。

  • 缓存Web ‘services’(非IBM宣扬的Web Services,译者注)或RSS feeds的结果。

不适用memcached的业务场景

  • 缓存对象的大小大于1MB

Memcached本身就不是为了处理庞大的多媒体(large media)和巨大的二进制块(streaming huge blobs)而设计的。

  • key的长度大于250字符

  • 虚拟主机不让运行memcached服务

如果应用本身托管在低端的虚拟私有服务器上,像vmware, xen这类虚拟化技术并不适合运行memcached。Memcached需要接管和控制大块的内存,如果memcached管理的内存被OS或 hypervisor交换出去,memcached的性能将大打折扣。

  • 应用运行在不安全的环境中

Memcached为提供任何安全策略,仅仅通过telnet就可以访问到memcached。如果应用运行在共享的系统上,需要着重考虑安全问题。

  • 业务本身需要的是持久化数据或者说需要的应该是database

memcached的最佳实践

  1. 不能够遍历memcached中所有的item,任何遍历所有item的命令执行所消耗的时间,将随着memcached中数据量的增加而增加。当其他命令因为等待(遍历所 有item的命令执行完毕)而不能得到执行,因而阻塞将发生。

  2. 由于客户端自己做了一次哈希,那么我们很容易增加大量memcached到集群中。memcached之间没有相互通信,因此不会增加memcached的负载;没有多播协议,不会网络通信量爆炸(implode)。

  3. 利用memcached,我们可以搭建出各种高效的缓存。比如,可以执行多个独立的查询,构建出一个用户对象(user object),然后将用户对象缓存到memcached中。而数据库的query cache是SQL语句级别的,不可能做到这一点。另外当写操作很频繁时,数据库的query cache会经常让所有缓存数据都失效,影响性能。

  4. local cache(本地缓存)适合缓存数据量少且访问频繁的数据(如产品分类,连接信息,服务器状态变量,应用配置变量等),当删除或更新一个缓存数据时,local cache需要通知所有的服务器刷新cache(很慢,不具扩展性)或者仅仅依赖缓存超时失效机制来完成。但在memcached集群中,删除或更新一个key会让所有的观察者觉察到。

  5. memcached 1.2及更高版本拥有了多线程模式。多线程模式允许memcached能够充分利用多个CPU,并在CPU之间共享所有的缓存数据。memcached使用一种简单的锁机制来保证数据更新操作的互斥。相比在同一个物理机器上运行多个memcached实例,这种方式能够更有效地处理multi gets。如果系统的负载并不重,那么不需要启用多线程工作模式。如果您在运行一个拥有大规模硬件的、庞大的网站,将体验到看到多线程的好处。

  6. Memcache客户端仅根据哈希算法来决定将某个key存储在哪个节点上,而不考虑节点的内存大小。因此,可以在不同的节点上使用大小不等的内存作为缓存空间。但是一般可以这样做:拥有较多内存的节点上可以运行多个memcached实例,每个实例使用的内存跟其他节点上的实例相同。

  7. memcached 1.2.5以及更高版本,提供了gets和cas命令,保证写操作同一时间只有一个线程执行,如果使用gets命令查询某个key的item,memcached会返回该item当前值的唯一标识。如果客户端程序覆写了这个item并想把它写回到memcached中,可以通过cas命令把那个唯一标识一起发送给memcached。如果该item存放在memcached中的唯一标识与您提供的一致,写操作将会成功。如果另一个进程在这期间也修改了这个item,那么该item存放在memcached中的唯一标识将会改变,写操作就会失败。

  8. Memcached的目标是可伸缩性。当连接和请求增加的时候,memcached的性能将比大多数数据库查询好。可以先在高负载的环境(并发的连接和请求)中测试您的代码,然后再决定memcached是否适合您

  9. 如果您要存储复杂的数据并且想被多种客户端库读取,需要满足以下:各个客户端以简单的string格式来存储;各个客户端都对数据进行压缩,要么都不压缩;各个客户端需要使用相同的哈希算法

  10. 适合缓存的内容:

  • 缓存简单的查询结果:查询缓存存储了给定查询语句对应的整个结果集,最合适缓存那些经常被用到,但不会改变的 SQL 语句对查询到的结果集,比如载入特定的过滤内容。但如果查询语句对应的结果集改变,该结果集不会展现出来。

  • 缓存简单的基于行的查询结果:基于行的缓存会检查缓存数据key的列表,那些在缓存中的行可以直接被取出,不在缓存中的行将会从数据库中取出并以唯一的键为标识缓存起来,最后加入到最终的数据集中返回。随着时间的推移,大多数数据都会被缓存,这也意味着相比与数据库,查询语句会更多地从 memcached 中得到数据行。如果数据是相当静态的,我们可以设置一个较长的缓存时间。

  • 缓存的不只是 SQL 数据,可以缓存经过渲染的Html数据块,那时你就可以简单地取出被预处理后的 HTML 直接填充在页面中,以节省CPU计算时间

  1. 根据系统的情况考虑是否使用多层的缓存结构,如:可以通过memcached缓存和本地缓存(如ehcache、oscache等)建立起多级缓存。本地缓存更加接近处理器, 这样可以帮助减少生成页面的时间,并且在 memcached 失效的情况下可以增加可靠性。

  2. 当数据更新时需要更新缓存

  3. 预热你的缓存:可以写一些脚本来缓存通用的页面;也可以写一个命令行工具来填充缓存;你可以在高峰时刻在缓存里填充一些内容。

  4. ehcache直接在jvm虚拟机中缓存,速度快,效率高;但是缓存共享麻烦,集群分布式应用不方便。ehcache的缓存共享方案是通过RMI或者Jgroup多播方式进行广播缓存通知更新,缓存共享复杂,维护不方便;简单的共享可以,但是涉及到缓存恢复,大数据缓存,则不合适。

memcached通过socket访问到缓存服务,效率比ecache低,比数据库要快很多,处理集群和分布式缓存方便,有成熟的方案

参考资料

  1. memcached的最佳实践方案