java数据引擎(六):高级主题

来源:互联网 发布:仿有赞前端源码 编辑:程序博客网 时间:2024/05/21 19:28

1.      缓存

为了避免同样的数据短时间内被反复读取,可以使用一些开源的缓存框架,如memcahe、ehcache等,数据引擎也提供了缓存功能,它采用的是本地内存缓存机制。

在前面所有涉及到以Param作为参数的查询操作中,只需加上这句就可以:

setCacheTime(inttime);//time是希望缓存的时间,以秒记

                     例如:

                            Parampm=new Param();

                        pm. setCacheTime(10);//将查询结果缓存10秒

                        在10秒内,如果引擎发现有同样的查询,将直接返回缓存中的数据。

            需要注意的是:

                     当查询的结果集超大时,可能导致内存溢出。

                     当设置了分页查询时(setPage),没有缓存功能;

                     当使用xml中的sql修改了数据时,可能需要主动更新缓存(方法中有声明);

                    当调用的存储过程修改了数据时,需要主动更新缓存。

                     当从数据库中直接修改了数据时,缓存可能不是真实数据(这一点是任何缓存框架都无法处理的)。

                         主动更新缓存的方法:

                         DataCenter .clearCacheData(String tableName);

                     参数中传递的是表名称,实际是清除了对该表的数据缓存,在开发过程中,有时也需要清除缓存。

2.      延迟加载

A.一般属性的延迟加载

当执行了限制字段数的查询时,在访问具体记录时,可能也需要获取其它字段的值,这时可使用延迟加载功能,方法十分简单(hibernate需要对每个字段进行预先设置)。

在前面所有涉及到以Param作为参数的查询操作中,只需加上这句就可以:

param.setLazy(true)

                    

Param  pm=new Param();

pm.addField(“name,age”);//只查询name和age字段的值

pm.setLazy(true);//设置使用延迟加载

List  list= DataCenter.getMapList(“student”,pm);//返回map结果集

//实际执行的sql是select  name, age  from  student

String name=(String)list.get(0).get(“name”);//获取第一条记录的name值

int class_id=(Integer)list.get(0).get(“class_id”);//获取其它字段值,延迟加载起作用

byte[] photo=(byte[])list.get(0).get(“photo”);//获取其它字段值,延迟加载起作用

                     如果返回实体对象的结果集也是一样的。

B.复合属性的延迟加载

                         一般涉及到关联关系才会有复合属性的概念,上文提到了两种设置关联关系的方式,其中讲述过复合属性的延迟加载,请参考前文。

                     当使用xml的SQL进行操作时,是不支持延迟加载的。

3.      事务控制

A.不同粒度控制

数据引擎将一次完整的请求或一个完整的业务过程当成一个事务,以此对数据操作的提交或回滚进行了控制,因此不用担心事务问题。如果在业务过程中,想进行更细粒度的事务控制,可以主动调用提交方法:

       DataCenter.exeInsert(..);//插入

       DataCenter.exeUpdate(..);//更新

       DataCenter.ensureCommit();//提交,保证上面的操作入库

       DataCenter.exeUpdate(..);//如果随后的数据操作有异常,仅该句将回滚

 

主动回滚方法:

if(不满足规定的条件){

    DataCenter.rollback();//回滚当前事务之前的所有数据操作

}

如果只想部分回滚,可设置保存点:

DataCenter.exeUpdate(..);//位置1

Map  point =DataCenter.setSavePoint();//建立保存点

DataCenter.exeUpdate(..);//位置2

DataCenter.rollbackToSavePoint(point);//回滚到保存点

DataCenter.exeUpdate(..);//位置3

如果正常执行,位置1和位置3的操作将生效,位置2不会入库。

如果位置1或位置3的操作有异常,那么三个位置的操作都会回滚。

 

如果在单独的线程中进行数据的增删改,必需调用这句才能写入库:

DataCenter.ensureCommitThread();//线程中修改数据后保证提交

              也可以使用JDBC提供的四个事务隔离级别,在具体的数据操作前执行该句即可:

                  DataCenter .setTransactionIsolation(intlevel);

           其中level为隔离级别,取值为以下几种:

              DataCenter. TRANSACTION_READ_UNCOMMITTED//1可以脏读、不可重复读和虚读

              DataCenter. TRANSACTION_READ_COMMITTED//2不可以脏读

              DataCenter. TRANSACTION_REPEATABLE_READ//4不可以脏读和不可以不可重复读

              DataCenter. TRANSACTION_SERIALIZABLE//8 都不允许

                     如果设置为8,表示隔离级别最高,数据最安全,但并发性最低,究竟使用哪个等级,需要权衡应用的业务状况。在一般情况下,我们并没有设置该值,是利用了各种数据库自身的默认等级。

B.乐观锁控制

                     为防止并发更新产生数据不一致问题,引擎采用了与hibernate类似的乐观锁机制。要使乐观锁生效,很简单,对需要进行控制的库表增加整数型字段(例如ver),在udbcConfig.xml中配置如下面的version描述。

<commonConfig>

 <!--乐观锁配置,指定版本字段名称。只有此处设置了值,且库表中含有这个值所对应的字段时,锁才起作用。-->

  <propertyname="version"value="ver"/>

</commonConfig>

加了上面这个配置后,在进行更新时,引擎就会自动控制了。当有更新冲突时,会抛出异常,并回滚所有操作。使用乐观锁的条件是,表必需有主键。

在更新时如果条件限制较宽,一次会出现大量记录需要更新,如果乐观锁发挥作用的话,其性能是有影响的,例如调用exeUpdate(StringtableName, Object record)时,如果符合更新条件的记录很多,引擎需要读取这些记录的版本值进行比对控制,这要消耗时间。如果是将库中的记录读出后(读出的记录必需包含版本字段),修改某些值,调用exeUpdateBatch(StringtableName, List<Object> records)进行批量更新,则不会重读数据,性能就好多了。

如果使用xml sql进行更新,并且表中的版本字段没有设置默认值,需要明确指定版本字段值。

直接根据sql进行操作也要指定默认值(当表中没有默认值时)。

使用实体对象进行操作时也要指定默认值(当表中没有默认值时)。

默认值一般设为0.

调用exeUpdate方法时,如果参数对象不含主键,也不会进行锁的处理。

4.      高并发操作

随着应用的用户访问量越来越大,数据并发操作会成为瓶颈,在相同硬件配置的条件下,数据引擎提供的方法可以比其它框架有更高并发性和吞吐量。以下方法的原理是利用了队列机制,不同的用户请求先被放入队列尾,引擎同时从队列头取出请求,当取到的数据到一定量的时候(比如1万),执行一个批次的操作,由于取数的速度相当快,因此放入的速度越快,吞吐量就越大,即如果一秒中的并发请求为10000次,就没有100毫秒请求10000次(相当于1秒10万次)更能发挥引擎的优势。由于是多个请求,一次执行,可称为多路并举。

A.插入

boolean flag= DataCenter .exeInsertRequest(“tab”, object);

第一个参数是表名,object是待插入的记录,类型为map对象或实体对象。

调用后,可能在约500ms的延迟后,才能查询到新数据

注意:不能获取插入的结果是成功或失败。不能回滚,只要一个错误,会导致整个同批次的所有操作都失败。

例:假设用户a、b、c几乎同时调用的是同一个业务场景,但c遗漏了非空字段,用户a和b的插入也会失败。

例:假设用户a、b、c几乎同时调用的是不同的业务场景,但都是向同一张表插入记录。

                                 a和b创建的记录是要插入字段f1 f2 f3,而c要插入f1 f2 f3 f4,则全部失败。这就是说同一批次的记录质地应该是相同的。是否为同一批次,完全是随机的。

调用本方法是先入队列,如果成功,则返回true,但实际究竟插入成功否,不能确定。

程序中可以判断,如果返回为false,则应该调用常规的操作:DataCenter .exeInsert(“tab”, object );

B. 修改

 boolean flag= DataCenter . exeUpdateRequest(“tab”, object );

 注意点可参考上面的描述。

C. 精确插入

前面的并发插入不能确定是否成功,下面的方法能确定结果,获取新数据时也没有延迟,但性能要比前者稍差些。

int c=DataCenter . exeInsertRequestSure(“tab”, object );

如果操作成功,返回1或0,返回-1表示有数据库异常。

返回0表示没有插入数据,返回-1时,本次请求可以自动回滚。

该方法不要求不同的调用场景是同质的记录,就是说,在某一时间段内,如果a用户是向表tab1插入一条记录,b用户是向tab2插入记录,c用户向tab3中放记录..,那么引擎将一同处理,各个请求的操作结果互不影响。可以看出,并发越高,越能体现出引擎的优势。

D.精确修改

int c=DataCenter . exeUpdateRequestSure(“tab”, object );

object必需包含主键属性,即是按主键进行修改的,否则,如果同批次操作中有删除,可能被死锁。不支持乐观锁控制。

E. 精确删除

int c=DataCenter . exeDeleteRequestSure(“tab”, object );

object必需包含主键属性,否则在同批次操作中有修改,可能死锁。

                              多路并举的精确操作中,插入、修改和删除共用一个队列,因此对同一个表的修改和删除可能在一个批次里出现,如果无主键限制,容易死锁,即使含主键,如果在极短的时间内对同一条记录进行修改和删除操作,很可能出现在同批次中,也会发生死锁。采用常规删除操作,可以避开该问题。

F. 单次并举

上面提到的是多次请求,按批次并行处理,引擎也提供了单次请求的多个操作的并举。

假如在一个事务中,有查询、有修改、有删除等一系列数据操作,各操作间没有依赖关系,其中有的操作可能很耗时,后续动作将一直等待,但此时cpu基本处于空闲,在此情况下,使用单次并举操作,是极为合适的。这需要用到DataActs类的实例。

DataActs的方法与DataCenter中增删改查方法完全相同,即名称相同,参数相同。不同的是这些方法都是异步执行的,不会等待执行结果的返回,实际上它只是一个添加动作,等所有添加操作都完成后,就可以获取各个操作的结果了。

DataActs da=newDataActs();

//以下的调用示例中的pm1、pm2等为Param对象,构建方式同以前的描述

da.getObjectList("student",pm1,Student.class);//添加查询动作,异步执行,立即返回

da.getMap("student",pm2);//添加查询动作,异步执行,立即返回

da.exeUpdate(“tab3”,pm3);//添加修改动作,异步执行,立即返回

da. exeBySql(“deletefrom tab4 where ..”);//添加动作,异步执行,立即返回

//添加完成后,开始获取结果数据。所有动作执行后,不能回滚

Object[]datas=da.getData();//按添加次序产生的结果数组,每个元素可能是List类型、

//Map类型或实体对象类型或Integer类型等

                     getData()是等待所有的结果都返回,如果想尽快得到最先完成执行的结果,可调用:

                        Object[] data=da.getIfHas();

                          数组data只有两个元素,第一个是添加动作的索引(添加动作的索引从0开始),第二个是获取的结果,根据实际的结果转型为具体的的类型即可。

                        DataActs实例在用完后,如果想重复使用,需要调用reset()方法。

                        DataActs其它的方法还有:

               exeDeleteBatch()

           exeDelete()

           exeUpdateBatch()

           exeInsertBatch()

           exeInsert()

           getMapList()

           getObject()

          各方法的参数形式与上述已列的相同,可以看出,与DataCenter的方法一样。

5.      大批量数据操作

A.插入

前面介绍过批量插入记录的方法DataCenter.exeInsertBatch("tab",list);

对于mysql来说,当list量>10000时,调用以下方法,速度更快:

int c=DataCenter.exeInsertBatchQuick("tab", list);

当数据量大于20000时,还可以调用以下方法。该方法采用了线程的方式,充分利用了多核处理器的特点(如果是单核,意义不大)。本方法不限于mysql,但出现异常时,已经插入的记录会生效,即不会回滚全部操作。

int c=DataCenter. exeInsertBatchConcurrent ("tab",list);

上面两个方法的list包含的是同质的map对象或实体对象,所谓同质的,是指list的每个对象具有相同的属性或key;对象不能含大对象二进制字段blob。

B.修改

   int c= DataCenter.exeUpdateBatchQuick("tab", list);//mysql专用

   int c=DataCenter. exeUpdateBatchConcurrent("tab", list);//线程方式,不能回滚

                                       //要求是同质的对象,不含blob字段

C.删除

   int c=DataCenter. exeDeleteBatchConcurrent("tab", list);//线程方式,不能回滚

   list的元素要求是同质记录,一般包含主键属性及对应的值。

上面操作中,如果数据量不大,引擎会自动采用普通的方法进行操作。

0 0