Программа зависает после получения 100 строк, связывающихся с CLOB - PullRequest
3 голосов
/ 20 марта 2012

Я извлекаю один текстовый столбец (CLOB) из таблицы в «удаленной» базе данных H2 (фактически на локальном диске, но с помощью tcp для доступа к нему), и после получения первых 100 строк программа зависает при получении следующей строка набора результатов. Если, с другой стороны, я получаю доступ к той же базе данных, что и встроенная, нет проблем. Если я пытаюсь отобразить строки таблицы с помощью консольного приложения H2, обращающегося к базе данных с помощью метода Server (т.е. tcp), я получаю следующее сообщение об ошибке:

IO Exception: "java.io.IOException: org.h2.message.DbException: The object is already closed [90007-164]"; 
"lob: null table: 14 id: 1" [90031-164] 90031/90031

Вот программа. Если я раскомментирую вызов, который устанавливает системное свойство, программа работает. Я также попытался извлечь столбец, используя поток символов или просто вызов getString, контролируемый константой USE_STREAM. В результатах нет разницы:

import java.sql.*;
import java.util.*;
import java.io.*;

public class Jdbc4
{
    private static final boolean USE_STREAM = false;

    public static void main(String[] args) throws Exception
    {
        //System.setProperty("h2.serverResultSetFetchSize", "50");
        Connection conn = null;
        try {
            Class.forName("org.h2.Driver").newInstance();
            conn = DriverManager.getConnection("jdbc:h2:tcp://localhost/file:C:/h2/db/test/test;IFEXISTS=TRUE", "sa", "");
            Statement stmt = conn.createStatement();
            String sql = "select select_variables from ipm_queues";
            ResultSet rs = stmt.executeQuery(sql);
            int count = 0;
            while (rs.next()) {
                ++count;
                String s;
                if (USE_STREAM) {
                    Clob clob = rs.getClob(1);
                    Reader rdr = clob.getCharacterStream();
                    char[] cbuf = new char[1024];
                    StringBuffer sb = new StringBuffer();
                    int len;
                    while ((len = rdr.read(cbuf, 0, cbuf.length)) != -1)
                        sb.append(cbuf, 0, len);
                    rdr.close();
                    s = sb.toString();
                    clob.free();
                }
                else
                    s = rs.getString(1);
                System.out.println(count + ": " + s);
            }
        }
        finally {
            if (conn != null)
                conn.close();
        }
    }
}

Вот DDL для создания таблицы (вы можете видеть, что она изначально была таблицей MySql):

CREATE TABLE `ipm_queues` (
    `oid` bigint NOT NULL,
    `queue_id` varchar(256) NOT NULL,
    `store_id` bigint NOT NULL,
    `creation_time` datetime NOT NULL,
    `status` bigint NOT NULL,
    `deleted` bigint NOT NULL,
    `last_mod_time` datetime NOT NULL,
    `queue_name` varchar(128),
    `select_variables` text,
    `where_clause` text,
    `from_table` varchar(128),
    `order_by` varchar(256),
    `from_associate_table` varchar(256),
    `from_view` varchar(128)
);

ALTER TABLE ipm_queues
    ADD CONSTRAINT ipm_queues_pkey PRIMARY KEY (oid);

CREATE UNIQUE INDEX ipm_queues_key_idx ON ipm_queues(queue_id, store_id);

CREATE INDEX ipm_queues_str_idx ON ipm_queues(store_id);

Ответы [ 2 ]

3 голосов
/ 26 марта 2012

Мне кажется, я понимаю причину зависания. Я исследовал простейший случай использования значения h2.serverResultSetFetchSize, равного 600, что больше, чем 523 известных мне строк. Как я уже упоминал, я могу получить первые 3 строки (один столбец CLOB), а затем либо зависнуть при получении 4-й строки, либо получить исключение «Объект уже закрыт».

Оказывается, что фактическая строка, содержащая первые три столбца, кажется довольно короткой по длине, и метод getInputStream в классе org.h2.value.ValueLobDb уже имеет данные и просто возвращает ByteArrayInputStream, построенный на этих данных. Данные 4-й строки все еще находятся на стороне сервера, поэтому необходимо создать фактический RemoteInputStream для обработки выборки данных из большого объекта на стороне сервера.

Вот что кажется проблемой: Класс org.h2.server.TcpServerThread кэширует эти большие объекты в случае SmallLRUCache. Похоже, что этот кеш предназначен для поддержки только самых последних ссылок на большие объекты !!! Размер этого кэша по умолчанию задается системным свойством h2.serverCachedObjects, которое по умолчанию равно 64, а размер выборки по умолчанию равен 100. Поэтому даже если я не переопределил свойство h2.serverResultSetFetchSize по умолчанию, если все мои строки имели достаточно большой размер для столбцов, требующих кэшированных больших объектов, любой размер выборки> 64 приведет к тому, что большой объект, представляющий первую строку, будет удален из кэша, и я даже не смог бы получить первую строку.

Кажется, что кэш LRU - неправильная структура для хранения больших объектов, которые находятся в активном наборе результатов. Конечно, размер кэша по умолчанию, который меньше размера выборки по умолчанию, кажется не идеальным.

0 голосов
/ 20 марта 2012

вам, вероятно, следует дать больше подробностей, но вы проверили сетевое соединение? Возможно, ваш сервер базы данных блокирует соединения (или сетевые соединения), как только они пытаются извлечь слишком много данных. Это может быть «своего рода» защита.

...