在过去一年的项目中,学习到以下点:
对于性能优化,主要从数据库,编码及系统架构上去做。
数据库层,我们主要考虑语句索引的建立,是否走索引查询。位图索引最好不要加,特别是对于更新频繁的表,会导致更新某一行的位图列,从而导致锁住一大片行(具有相同位图列)。
而对于我们业务系统,某个表一天大约10万左右的业务数据增长,所以对其进行水平分区,每三个月作为一个分区区间范围。三个月的数据将近一千万数据,而用户业务查询一般只会查询一个月内的数据,系统上也会有限制,查询一个月内的数据。我们对于这部分,以创建时间作为分区key,然后会建立一个local索引,对于某个分区内的索引。我们采用的方式是oracle的需要手动创建好分区的形式,如果插入一条数据,不在某一分区就会报错。oracle提供了其它,如设置如果某一行映射不到一个分区,就指定一个默认分区。或者通过告诉oracle一个分区规则,来动态分区,如三个月作为分区,第一次是1-3月,oracle会在快要4月的时间帮你建立4-6月数据。
对于相对不变的基础数据,我们采用memcache来做。我们映射层用的是mybatis,mybatis自带一级缓存,和可配置二级缓存。一级缓存只是对于单个session内而言,即我们任何通过mybatis查询,更新等操作开启的一个mybatis的session。在这个session内,mybatis会以语句namespace,mybatis解析的语句(即mybatis组装的语句,你传入的参数会被转换成实际的值 )作为一个key,放入一个localMap,将查询到的结果放入这个map。在这个session中,第二次再查询同样的数据,直接从localMap中返回。可以通过在语句上用flush开关为true,来关掉这个session。mybatis不能完全关掉一级缓存。只能设置一级缓存作用域,默认session,可以设置为statement(3.1版本及以上才支持)。
mybatis的一级缓存事实上用处不大,一个session中同一个数据查询两次的概率不大,而且有时会给人造成疑惑。因此我们这边禁用了一级缓存。
对于编码方面,在性能不是特别看重的地方,不要引入多线程,这会容易出错。有些需要性能改善的地方,可以引入多线程,但一般多线程数为cpu个数+1。引入多线程后并发问题就需要特别考虑了。我们这边,会把一些处理以业务号分queue,这样可以使业务处理并发进行,又能使相同业务号的数据不致于产生并发竞争。对于mq,消息是序列化的,但采用默认ack。显示地ack会比较耗性能。
编码方面还要注意在写sql语句的时候多用带参数 ? 号形式的语句,oracle数据库会有共享池去缓存解析过的语句,因此对于这种 带参数 ?,可以避免硬解析。这里的硬解析是指oracle,会对语句重新解析,生成执行计划等,而由于解析结果需要放入共享池,这时会要求当前oracle session需要申请闩(oracle内部用于保护共享内存的轻量级锁),如果很多提交到oracle的语句(可能是n个session),都需要申请闩,而闩的数量肯定是固定的,申请不到的就会出现阻塞等待状态(会先自旋,n个时间单位后才会阻塞挂起)。因此在编码中,语句尽量用带参数 ?,即prepareStatement形式的语句。还可以防止sql注入。
对于有些场景,需要与外界交互的数据量比较大,传统的webservice的xml量会比较大。可以查用压缩的更加紧密的格式,如json。
架构方面,使用apche的mod_proxy(自带的)负载均衡,两台机子,3:1的权重进行负载。mod_proxy缺点的失效备援这块做的不好。可以使用mod_jk(需要引入这个模块)。
现有的系统架构是ejb作为最上层,下面是service层,再下面是dao层。ejb这层可以拿掉,目前只是用ejb的注解发布webservice,以及将事务由ejb容器托管(事实上通过ejb的拦截器,进入系统中自动开启一个mybatis的session,在退步ejb时,执行最后的拦截器,无异常提交事务,有异常则回滚事务)
数据量,某个表每天25万单子。。