BLOB performance test FYI - Mailing list pgsql-jdbc

From Joe Shevland
Subject BLOB performance test FYI
Date
Msg-id HEECIHEEJDBMCCGMGIOBCEBOCHAA.jshevland@j-elite.com
Whole thread Raw
Responses callable statements  (Stuart Robinson <stuart@zapata.org>)
Re: BLOB performance test FYI  (Dave Cramer <Dave@micro-automation.net>)
List pgsql-jdbc
Hi,

I'm adding a page on using binary data to a primer doc I'm writing, and have been performing some tests with PostgreSQL
7.2and the latest JDBC code from CVS (14/4). 

The following output shows some results of the test on a FreeBSD 4.5-S machine, P3 1GHz CPU, 1G RAM, PostgreSQL 7.2,
JDK1.3.1p6(OpenJIT); here the test was storing and retrieving increasingly large amounts of random binary data. The
byteatype seems to cause real memory problems around the 4MB mark, presumably from trying to build an insert
statement/StringBufferbig enough for the escaped data. 

I was trying to see what data size would be best for which type. Around even 500k there seems to be a big overhead for
storingbytea, again I guess converting the mostly unprintable binary data into the PGbytea string, though I'm not sure
ifit s a fair comparison test. I don't suppose there's any easy way of streaming the bytea values like the LO manager?
Icould be missing something fundamental about the bytea type, but given its limits, should the JDBC driver act by
defaulton bytea? 

Cheers,
Joe

---
www:~user# java -Xmx128m -classpath .:postgresql.jar BLOBTest postgres username password
[bytea] storing    0k of data -> 24ms
[bytea] retrieving 0k of data -> 7ms
[LO/oid] storing    0k of data -> 19ms
[LO/oid] retrieving 0k of data -> 4ms
[bytea] storing    1k of data -> 8ms
[bytea] retrieving 1k of data -> 1ms
[LO/oid] storing    1k of data -> 3ms
[LO/oid] retrieving 1k of data -> 3ms
[bytea] storing    500k of data -> 4943ms
[bytea] retrieving 500k of data -> 136ms
[LO/oid] storing    500k of data -> 142ms
[LO/oid] retrieving 500k of data -> 28ms
[bytea] storing    1000k of data -> 12423ms
[bytea] retrieving 1000k of data -> 270ms
[LO/oid] storing    1000k of data -> 285ms
[LO/oid] retrieving 1000k of data -> 55ms
Exception in thread "main" java.lang.OutOfMemoryError
        at java.lang.StringBuffer.expandCapacity(StringBuffer.java, Compiled Code)
        at java.lang.StringBuffer.append(StringBuffer.java, Compiled Code)
        at org.postgresql.jdbc2.PreparedStatement.setString(Unknown Source)
        at org.postgresql.jdbc2.PreparedStatement.setBytes(Unknown Source)
        at org.postgresql.jdbc2.PreparedStatement.setBinaryStream(Unknown Source)
        at BLOBTest.storeBlobAsBytea(BLOBTest.java, Compiled Code)
        at BLOBTest.testData(BLOBTest.java, Compiled Code)
        at BLOBTest.main(BLOBTest.java, Compiled Code)

The LO/oid output is using the LargeObject manager, and performance-wise seems to scale well until about 65M in my case
(theresults below can be avoided if I bump -Xmx up to 256M). This output is from commenting out the bytea tests: 

---
www:~user# java -Xmx128m -classpath .:postgresql.jar BLOBTest postgres username password
[LO/oid] storing    0k of data -> 38ms
[LO/oid] retrieving 0k of data -> 5ms
[LO/oid] storing    1k of data -> 3ms
[LO/oid] retrieving 1k of data -> 3ms
[LO/oid] storing    500k of data -> 139ms
[LO/oid] retrieving 500k of data -> 30ms
[LO/oid] storing    1000k of data -> 268ms
[LO/oid] retrieving 1000k of data -> 55ms
[LO/oid] storing    4000k of data -> 1162ms
[LO/oid] retrieving 4000k of data -> 227ms
[LO/oid] storing    16000k of data -> 4817ms
[LO/oid] retrieving 16000k of data -> 868ms
[LO/oid] storing    32000k of data -> 9711ms
[LO/oid] retrieving 32000k of data -> 1704ms
[LO/oid] storing    63999k of data -> 19748ms
Unknown Response Type
Unknown Response Type
        at org.postgresql.core.QueryExecutor.execute(Unknown Source)
        at org.postgresql.Connection.ExecSQL(Unknown Source)
        at org.postgresql.Connection.ExecSQL(Unknown Source)
        at org.postgresql.Connection.setAutoCommit(Unknown Source)
        at BLOBTest.retrieveBlobAsLO(BLOBTest.java, Compiled Code)
        at BLOBTest.testData(BLOBTest.java, Compiled Code)
        at BLOBTest.main(BLOBTest.java, Compiled Code)

--- BLOBTest.java

import java.io.*;
import java.sql.*;
import org.postgresql.largeobject.*;

public class BLOBTest {

    public BLOBTest()
        throws Exception {

        Class.forName("org.postgresql.Driver");

        createDummyDataFile("bin0",256);
        createDummyDataFile("bin1",1024);
        createDummyDataFile("bin512",512000);
        createDummyDataFile("bin1024",1024000);
        createDummyDataFile("bin4096",4096000);
        createDummyDataFile("bin16384",16384000);
        createDummyDataFile("bin32768",32768000);
        createDummyDataFile("bin65535",65535000);
    }

    public static void main( String args[] ) {
        if ( args.length != 3 ) {
            System.err.println("usage: java BLOBTest <database_spec> <username> <password>");
            System.exit(1);
        }

        try {

            BLOBTest test = new BLOBTest();
            long time = 0;

            Connection conn = DriverManager.getConnection(
                "jdbc:postgresql:"+args[0],args[1],args[2]);

            test.testData(conn,"bytea","bin0",256);
            test.testData(conn,"LO/oid","bin0",256);

            test.testData(conn,"bytea","bin1",1024);
            test.testData(conn,"LO/oid","bin1",1024);

            test.testData(conn,"bytea","bin512",512000);
            test.testData(conn,"LO/oid","bin512",512000);

            test.testData(conn,"bytea","bin1024",1024000);
            test.testData(conn,"LO/oid","bin1024",1024000);

            test.testData(conn,"bytea","bin4096",4096000);
            test.testData(conn,"LO/oid","bin4096",4096000);

            test.testData(conn,"bytea","bin16384",16384000);
            test.testData(conn,"LO/oid","bin16384",16384000);

            test.testData(conn,"bytea","bin32768",32768000);
            test.testData(conn,"LO/oid","bin32768",32768000);

            test.testData(conn,"bytea","bin65535",65535000);
            test.testData(conn,"LO/oid","bin65535",65535000);

        } catch ( Exception e ) {
            System.err.println(e);
            e.printStackTrace();
        }
    }

    private void createDummyDataFile( String fileName, int size )
        throws Exception {

        byte data[] = new byte[size];
        FileOutputStream fos = new FileOutputStream(fileName);
        fos.write(data);
        fos.close();
    }

    private void testData( Connection conn, String method, String fileName, int size )
        throws Exception {

        long time;

        // Garbage collect to clean up any garbage objects
        System.gc();

        if ( method.equals("bytea") ) {
            time = storeBlobAsBytea(conn, fileName);
            System.err.println("["+method+"] storing    "+(size/1024)+"k of data -> "+time+"ms");
            time = retrieveBlobAsBytea(conn, fileName);
            System.err.println("["+method+"] retrieving "+(size/1024)+"k of data -> "+time+"ms");
        } else {
            time = storeBlobAsLO(conn, fileName);
            System.err.println("["+method+"] storing    "+(size/1024)+"k of data -> "+time+"ms");
            time = retrieveBlobAsLO(conn, fileName);
            System.err.println("["+method+"] retrieving "+(size/1024)+"k of data -> "+time+"ms");
        }
    }

    private long storeBlobAsBytea( Connection conn, String fileName )
        throws Exception {

        long t1, t2;

        t1 = System.currentTimeMillis();
        File file = new File(fileName);
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
        PreparedStatement ps = conn.prepareStatement("INSERT INTO blob_bytea VALUES (?, ?)");
        ps.setString(1, file.getName());
        ps.setBinaryStream(2, bis, (int)file.length());
        ps.executeUpdate();
        ps.close();
        bis.close();
        t2 = System.currentTimeMillis();
        return (t2 - t1);
    }

    private long retrieveBlobAsBytea( Connection conn, String fileName )
        throws Exception {

        long t1, t2;

        t1 = System.currentTimeMillis();
        PreparedStatement ps = conn.prepareStatement(
            "SELECT blob_data FROM blob_bytea WHERE blob_name=?");
        ps.setString(1, fileName);
        ResultSet rs = ps.executeQuery();
        if ( rs.next() ) {
            byte[] imgBytes = rs.getBytes(1);
            // use the stream in some way here
        }
        rs.close();
        ps.close();
        t2 = System.currentTimeMillis();
        return (t2 - t1);
    }

    private long storeBlobAsLO( Connection conn, String fileName )
        throws Exception {

        long t1, t2;

        boolean oldState = conn.getAutoCommit();
        try {

            t1 = System.currentTimeMillis();

            // All LargeObject API calls must be within a transaction
            conn.setAutoCommit(false);

            // Get the Large Object Manager to perform operations with
            LargeObjectManager lobj = ((org.postgresql.Connection)conn).getLargeObjectAPI();

            // create a new large object
            int oid = lobj.create(LargeObjectManager.READ | LargeObjectManager.WRITE);

            // open the large object for write
            LargeObject obj = lobj.open(oid, LargeObjectManager.WRITE);

            // Now open the file
            File file = new File(fileName);
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));

            // copy the data from the file to the large object
            byte buf[] = new byte[2048];
            int s, tl = 0;
            while ((s = bis.read(buf, 0, 2048)) > 0) {
                obj.write(buf, 0, s);
                tl += s;
            }
            // Close the large object
            obj.close();
            // Now insert the row
            PreparedStatement ps = conn.prepareStatement("INSERT INTO blob_lo VALUES (?, ?)");
            ps.setString(1, file.getName());
            ps.setInt(2, oid);
            ps.executeUpdate();
            ps.close();
            bis.close();

            t2 = System.currentTimeMillis();
            return (t2 - t1);

        } finally {
            conn.setAutoCommit(oldState);
        }
    }

    private long retrieveBlobAsLO( Connection conn, String fileName )
        throws Exception {

        long t1, t2;

        boolean oldState = conn.getAutoCommit();

        try {

            t1 = System.currentTimeMillis();

            // All LargeObject API calls must be within a transaction
            conn.setAutoCommit(false);

            // Get the Large Object Manager to perform operations with
            LargeObjectManager lobj = ((org.postgresql.Connection)conn).getLargeObjectAPI();
            PreparedStatement ps = conn.prepareStatement("SELECT blob_data FROM blob_lo WHERE blob_name=?");
            ps.setString(1, fileName);
            ResultSet rs = ps.executeQuery();

            while(rs.next()) {
                // open the large object for reading
                int oid = rs.getInt(1);
                LargeObject obj = lobj.open(oid, LargeObjectManager.READ);
                // read the data
                byte buf[] = new byte[obj.size()];
                obj.read(buf, 0, obj.size());
                // do something with the data read here
                // Close the object
                obj.close();
            }
            rs.close();
            ps.close();

            t2 = System.currentTimeMillis();
            return (t2 - t1);
        } finally {
            conn.setAutoCommit(oldState);
        }
    }
}

---
Finally, to build or run:

  javac -classpath .:postgresql.jar BLOBTest.java

  java -Xmx128m -classpath .:postgresql.jar BLOBTest //somehost/db user password


pgsql-jdbc by date:

Previous
From: AgentM
Date:
Subject: Meaningful Exception handling
Next
From: Barry Lind
Date:
Subject: Re: Meaningful Exception handling