Feeds:
Posts
Comments

Posts Tagged ‘transaction optimization’

This article is a small POC on how the readOnly attribute used in Spring Transaction strategy improves database access in case of transactions that involve only READing data from datasources.

First a small extract from the Spring reference documentation:

The TransactionDefinition interface specifies:
• Isolation: the degree of isolation this transaction has from the work of other transactions. For example, can this transaction see uncommitted writes from other transactions?
• Propagation: normally all code executed within a transaction scope will run in that transaction. However, there are several options specifying behavior if a transactional method is executed when a transaction context already exists: for example, simply continue running in the existing transaction (the common case); or suspending the existing transaction and creating a new transaction. Spring offers all of the transaction propagation options familiar from EJB CMT.
• Timeout: how long this transaction may run before timing out (and automatically being rolled back by the underlying transaction infrastructure).
• Read-only status: a read-only transaction does not modify any data. Read-only transactions can be a useful optimization in some cases (such as when using Hibernate).

As you can see the Read-only status seems to be silver bullet for transactions that involve only reading data. Following is a setup that I used to prove the point.

The setup included a Service method which in turn calls the DAO layer to access a table and ONLY retrieve records. The time required to access the data is calculated both WITH and WITHOUT the readOnly attribute. The time is calculated using Spring’s StopWatch utility class. However you can use your own timing methodologies to check out the outcome. The application is run for sometime to warm up the JVM followed by the actual estimation of the time. This is clear in the Main.java class

This test is run on the following configuration machine:

JDK: 1.5
MySQL: 5.0.26
Records in DB: 1,000,000 plus records
RAM: 1.5 GB
Processor: Pentium 1.8GHz

DAO Class : DAOStep.java


package com.tx.app.dao;

import java.sql.Types;
import java.util.Calendar;
import java.util.List;

import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class DAOStep extends  NamedParameterJdbcDaoSupport implements IDAOStep
{
	


	public void getData(int x) 
	{

		final String insertIntoTest = "insert into TEST(VAL1,VAL2) values(?,?) ";

		List val=getJdbcTemplate().queryForList("select 1 from test where val1= ? order by 1 desc" , new Object[]{x},new int[]{Types.INTEGER});
		
	}
	

}

This DAO class only queries a table name “TEST” with the following query

SELECT 1 from TEST where val1=? ORDER BY 1 DESC

Next is the Service Class : ServiceStep.java


package com.tx.app.service;

import com.tx.app.dao.IDAOStep;
import com.tx.app.exception.MyCheckedException;
import com.tx.app.exception.MyRuntimeException;

public class ServiceStep 
{

	private IDAOStep daoStep;
	
	
	
	public void getReadOnlyData(int x) 
	{
		daoStep.getData(x);

		
	}	
	
	public void getNoReadOnlyData(int x) 
	{
		daoStep.getData(x);
		
	}	

	public IDAOStep getDaoStep() {
		return daoStep;
	}

	public void setDaoStep(IDAOStep daoStep) {
		this.daoStep = daoStep;
	}
	
	
}

Next is the Main Class : Main.java


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.StopWatch;

import com.tx.app.dao.IDAOStep;
import com.tx.app.exception.MyCheckedException;
import com.tx.app.service.ServiceStep;


public class Main 

{

	
	
	public static void main(String[] args) throws MyCheckedException 
	{
	
		ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"context.xml"});
		ServiceStep service = (ServiceStep)context.getBean("serviceStepTX");
		
		
/*Warm Up for the application and the JVM*/
		for (int i = 0; < 10; i++)
		{
			service.getNoReadOnlyData(1);
			service.getReadOnlyData(1);
		}

		
		

		long readOnlyTime =0;
		long noReadOnlyTime =0;
		int runTime =500;
		
		for (int i = 0; i < runTime; i++) 
		{

		
		
		StopWatch watch = new StopWatch("Read-Only-Data");
		//watch.start("First Task");
		service.getReadOnlyData(1);
		//watch.stop();
		watch.start("Second Task");
		service.getReadOnlyData(2);
		watch.stop();
		watch.start("Third Task");		
		service.getReadOnlyData(3);
		watch.stop();
		watch.start("Four Task");		
		service.getReadOnlyData(4);
		watch.stop();
		watch.start("Five Task");		
		service.getReadOnlyData(5);
		watch.stop();		
		watch.start("Six Task");		
		service.getReadOnlyData(6);
		watch.stop();		
		//System.out.println(watch.prettyPrint());
		//System.out.println(watch.shortSummary());
		readOnlyTime = readOnlyTime+watch.getTotalTimeMillis();
		}
		

		for (int i = 0; i < runTime; i++) 
		{
			
		
		StopWatch watch1 = new StopWatch("No-Read-Only-Data");
		//watch.start("First Task");
		service.getNoReadOnlyData(1);
		//watch.stop();
		watch1.start("Second Task");
		service.getNoReadOnlyData(2);
		watch1.stop();
		watch1.start("Third Task");		
		service.getNoReadOnlyData(3);
		watch1.stop();
		watch1.start("Four Task");		
		service.getNoReadOnlyData(4);
		watch1.stop();
		watch1.start("Five Task");		
		service.getNoReadOnlyData(5);
		watch1.stop();		
		watch1.start("Six Task");		
		service.getNoReadOnlyData(6);
		watch1.stop();		
		//System.out.println(watch1.prettyPrint());
		//System.out.println(watch1.shortSummary());
		noReadOnlyTime = noReadOnlyTime+watch1.getTotalTimeMillis();
		
		
		}
		
		System.out.println("Read Only : "+(readOnlyTime/runTime));
		System.out.println("No Read Only : "+(noReadOnlyTime/runTime));
		
	}
	
	
}
&#91;/sourcecode&#93;


<strong>Finally the Spring XML configuration file</strong>




<?xml version="1.0" encoding="UTF-8"?>
<beans  xmlns="http://www.springframework.org/schema/beans"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns:context="http://www.springframework.org/schema/context"
		xsi:schemaLocation="http://www.springframework.org/schema/beans
							http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
	

   
    <bean id="faceDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
   		<property name="driverClass" value="com.mysql.jdbc.Driver"/>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/facedb"/>
		<property name="user" value="root"/>	
		<property name="password" value=""/>	
		<property name="initialPoolSize"><value>3</value></property>		
		<property name="minPoolSize"><value>5</value></property>	
		<property name="acquireIncrement"><value>5</value></property>		
        <property name="acquireRetryAttempts"><value>10</value></property>
        <property name="acquireRetryDelay"><value>1000</value></property>
        <property name="checkoutTimeout"><value>600000</value></property>
        <property name="maxPoolSize"><value>25</value></property>
        <property name="maxStatements"><value>200</value></property>
        <property name="maxStatementsPerConnection"><value>20</value></property>
        <property name="maxIdleTimeExcessConnections"><value>600</value></property>
    </bean>
    
	
	<bean id="daoStep" class="com.tx.app.dao.DAOStep">
		<property name="dataSource" ref="faceDataSource"/>
	</bean>
	
	<bean id="serviceStep" class="com.tx.app.service.ServiceStep">
		<property name="daoStep"  ref="daoStep"/>
	</bean>
	
	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    	<property name="dataSource" ref="faceDataSource"/>
    </bean>
    

	<bean id="serviceStepTX" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
		<property name="transactionManager" ref="txManager"/>
		<property name="target" ref="serviceStep"/>
		<property name="proxyTargetClass" value="true"/>		
		<property name="transactionAttributes">
			<props>
				<prop key="getReadOnly*">PROPAGATION_REQUIRED,readOnly</prop>	
				<prop key="getNoReadOnly*">PROPAGATION_REQUIRED</prop>				
			</props>
		</property>
	</bean>    
	
    
</beans>

Once Now create a table in the MySQL database with the following script

CREATE TABLE TEST
(VAL1 INT,
VAL2 INT) engine=innodb;

CREATE INDEX TEST_INDEX on TEST(VAL1);

OUTPUT
———-

Once you run the Main.java class you will see an output similar to this.

Read Only : 15
No Read Only : 30

The interpretation of this output is that a SELECT on the table

With readOnly attribute took an average of 15 ms
Without readOnly attribute took an average of 30 ms

Pretty good I suppose!!!

Read Full Post »