Mybatis - произошла ошибка при закрытии соединения - PullRequest
1 голос
/ 11 апреля 2019

Я использую mybatis для подключения к оракулу.

Мой конфиг mybatis:

<settings>
    <setting name="lazyLoadingEnabled" value="true" />
    <setting name="aggressiveLazyLoading" value="false" />
    <setting name="logImpl" value="${logImpl}" />
    <setting name="defaultStatementTimeout" value="10" />
</settings>
<environments default="default">
    <environment id="default">
        <transactionManager type="JDBC" />
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.driver}" />
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
            <property name="poolPingConnectionsNotUsedFor" value="290000"/>
            <property name="poolPingQuery" value="SELECT COUNT(*) FROM RESORT"/>
            <property name="poolPingEnabled" value="true"/>
        </dataSource>
    </environment>
</environments>

Мой код открытого сеанса похож на

SqlSession sqlSession = factory.openSession();
Object result = null;
try
{
    QueryInfoMapper mapper = sqlSession.getMapper(QueryInfoMapper.class);
    result = mapper.queryInfoFromOpera(mybatisMapping);
} finally
{
    sqlSession.close();
}

Из-за того, что область действия приложения ограничена, и sqlSession не может использоваться в области приложения, поэтому мне приходится управлять sqlSession самостоятельно.

Журнал

2019-04-11 15: 30: 35,773 ИНФОРМАЦИЯ [стандартный вывод] (задание по умолчанию-60) Открытие соединения JDBC

2019-04-11 15: 30: 41 860 ИНФОРМАЦИЯ [stdout] (задание по умолчанию-57) Плохое соединение. Не удалось откатиться

2019-04-11 15: 30: 41,861 INFO [стандартный вывод] (задание по умолчанию-57) Заявленное просроченное соединение 962608913.

2019-04-11 15: 30: 41,861 ИНФОРМАЦИЯ [stdout] (задание по умолчанию-57) Из пула возвращено плохое соединение (962608913), получено другое соединение.

2019-04-11 15: 30: 41 895 INFO [stdout] (задание по умолчанию-57) Создано соединение 1812494479.

2019-04-11 15: 30: 41 895 ИНФОРМАЦИЯ [стандартный вывод] (задание по умолчанию-57) Установка для автоматической фиксации значения false при подключении к JDBC [oracle.jdbc.driver.T4CConnection@6c08788f]

2019-04-11 15: 30: 41 895 ИНФОРМАЦИЯ [stdout] (задание по умолчанию-57) ==> Подготовка: ВЫБЕРИТЕ TRAVEL_AGENT_NAME FROM (ВЫБЕРИТЕ TRAVEL_AGENT_NAME ОТ OPERA.NAME_RESERVATION ГДЕ RESV_NAME_ID =?) ГДЕ ROWNUM = 1 * 1028 = 1

2019-04-11 15: 30: 41 896 ИНФОРМАЦИЯ [стандартный вывод] (задание по умолчанию-57) ==> Параметры: 288541 (строка)

2019-04-11 15: 30: 41 900 INFO [stdout] (задание по умолчанию-57) <== Столбцы: TRAVEL_AGENT_NAME </p>

2019-04-11 15: 30: 41 900 INFO [stdout] (задание по умолчанию-57) <== Строка: ноль </p>

2019-04-11 15: 30: 41 900 INFO [стандартный вывод] (задание по умолчанию-57) <== Итого: 1 </p>

2019-04-11 15: 30: 41,900 ИНФОРМАЦИЯ [stdout] (задание по умолчанию-57) Сброс автоматической фиксации на true при подключении JDBC [oracle.jdbc.driver.T4CConnection@6c08788f]

2019-04-11 15: 30: 41 , 900 INFO [стандартный вывод] (задание по умолчанию-57) Закрытие соединения JDBC [oracle.jdbc.driver.T4CConnection@6c08788f ]

2019-04-11 15: 31: 00 , 788 INFO [стандартный вывод] (задание по умолчанию-60) Плохое соединение. Не удалось откатиться

2019-04-11 15: 31: 00,788 ИНФОРМАЦИЯ [stdout] (задание по умолчанию-60) Заявленное просроченное соединение 1228464923.

2019-04-11 15: 31: 00,788 ИНФОРМАЦИЯ [stdout] (задание по умолчанию-60) Из пула возвращено плохое соединение (1228464923), получено другое соединение.

2019-04-11 15: 31: 00,820 INFO [стандартный вывод] (задание по умолчанию-60) Создано соединение 265625885.

2019-04-11 15: 31: 00,820 ИНФОРМАЦИЯ [stdout] (задание по умолчанию-60) Установка для автоматической фиксации значения false при подключении JDBC [oracle.jdbc.driver.T4CConnection@fd5211d]

2019-04-11 15: 31: 00,820 INFO [стандартный вывод] (задание по умолчанию-57) Возвращено соединение 1812494479 в пул.

При просмотре журнала, согласно отметке времени, это происходит при закрытии соединения (здесь это транзакция)

Но чтобы его закрыть, нужны 9 или 19 секунд. Второй журнал «Плохое соединение. Невозможно выполнить откат». Я не могу найти, где на самом деле причина. И какой метод занимает столько времени. Эта проблема возникает не каждый раз, а случайно.

Я думал установить <property name="poolMaximumActiveConnections" value="40" />, чтобы увеличить количество соединений. Я не уверен, поможет ли это.

В чем причина сбоя соединения / транзакции? Как избежать неудачного закрытия соединения / транзакции?

===========================

Обновление: я столкнулся с этой проблемой снова, и журнал идет что-то другое:

2019-04-13 15: 42: 31 812 INFO [стандартный вывод] (задание по умолчанию-86) Открытие соединения JDBC

2019-04-13 15: 42: 35,493 INFO [stdout] (задание по умолчанию-62) Сбой при выполнении запроса ping «SELECT COUNT (*) FROM RESORT»: ошибка ввода-вывода: истекло время ожидания чтения сокета

2019-04-13 15: 42: 35,493 ИНФОРМАЦИЯ [stdout] (задание по умолчанию-62) Соединение 1963609369: ПЛОХО: ошибка ввода-вывода: истекло время ожидания чтения сокета

2019-04-13 15: 42: 35,493 INFO [stdout] (задание по умолчанию-62) Из пула было возвращено плохое соединение (1963609369), получено другое соединение.

2019-04-13 15: 42: 35,493 INFO [stdout] (задание по умолчанию-62) Проверено соединение 195963529 из пула.

2019-04-13 15: 42: 35,493 INFO [стандартный вывод] (задание по умолчанию-62) Тестирование соединения 195963529 ...

2019-04-13 15: 42: 54,448 INFO [stdout] (задание по умолчанию-62) Ошибка выполнения запроса ping «SELECT COUNT (*) FROM RESORT»: ошибка ввода-вывода: истекло время ожидания чтения сокета

2019-04-13 15: 42: 54,448 INFO [стандартный вывод] (задание по умолчанию-62) Соединение 195963529 ПЛОХОЕ: ошибка ввода-вывода: истекло время ожидания чтения сокета

2019-04-13 15: 42: 54,448 ИНФОРМАЦИЯ [stdout] (задание по умолчанию-62) Из пула возвращено плохое соединение (195963529), получено другое соединение.

2019-04-13 15: 42: 54 479 INFO [стандартный вывод] (задание по умолчанию-62) Создано соединение 741137137.

Кстати, я поменяю ping sql на SELECT 1 FROM DUAL. Что могло вызвать тайм-аут чтения этого сокета?

1 Ответ

1 голос
/ 15 апреля 2019

Я вижу несколько проблем здесь:

  1. потенциально тяжелый пинг-запрос (как указано beny23 )
  2. длительное закрытое соединение
  3. некорректное поведение пула соединений mybatis

Вам определенно нужно использовать SELECT 1 FROM DUAL в качестве запроса ping. В противном случае вы выполняете не очень дешевую операцию при каждом открытии соединения.

Длинное закрытие и IO Error: Socket read timed out указывают на то, что существует либо проблема с сетевым подключением, либо проблема доступности сервера oracle, либо и то и другое.

Имеет смысл проверить здоровье оракула в тот момент, когда возникает эта проблема. Отвечает ли он на другие запросы в то время? Что такое загрузка процессора / io / memory / swap и т. Д. Если сервер находится под очень высокой нагрузкой, возможно, он не отвечает вовремя.

Проверка проблем с сетевым подключением - очень широкая тема. Самый надежный (и также сложный) способ, который я знаю, - это захватить сетевой трафик (с помощью таких инструментов, как tcpdump или WireShark) на обоих концах и сравнить их.

Тогда существует проблема с пулом соединений mybatis.

Прежде всего, немного информации о том, как работает пул соединений mybatis.

Одна важная и неочевидная вещь заключается в том, что реализация пула соединений mybatis принудительно возвращает соединения в пул, если они используются слишком долго. Вот цитата из документации :

poolMaximumCheckoutTime - время, в течение которого Соединение может быть «извлечено» из пула, прежде чем оно будет принудительно возвращено. По умолчанию: 20000 мс (то есть 20 секунд)

Это означает, что если приложение пытается открыть новое соединение и все соединения заняты, mybatis закроет самое старое соединение, если оно использовалось более 20 секунд (по умолчанию).

Само по себе это может быть очень неожиданным поведением, если у вас есть несколько долго выполняющихся запросов. Другая и, вероятно, более серьезная проблема заключается в том, как это реализовано в mybatis. Чтобы захватить соединение, запрос на откат транзакция выполняется из потока, который запросил новое соединение (В приведенном выше примере поток default task-57 удерживает соединение, а поток default task-60 пытается получить соединение из бассейна).

Это проблема, потому что драйвер oracle jdbc требует правильной синхронизации при доступе к соединению из нескольких потоков, а mybatis этого не делает:

Контролируемый последовательный доступ к соединению, например, предоставляемый кэшированием соединения, необходим и рекомендуется. Однако Oracle настоятельно не рекомендует использовать соединение с базой данных между несколькими потоками. Избегайте одновременного доступа нескольких потоков к соединению. Если несколько потоков должны совместно использовать соединение, используйте дисциплинированный протокол начала / окончания использования.

Таким образом, эта ошибка синхронизации доступа из нескольких потоков к общему ресурсу (соединению) может вызвать всевозможные проблемы согласованности, и я не исключаю, что проблема с закрытием соединения вызвана тем, что соединение получено в какое-то несовместимое состояние ранее из-за отсутствия синхронизации.

Увеличение размера пула устраняет эту проблему для данной нагрузки, поскольку ситуация, когда пул исчерпан, не происходит (или происходит реже).

Обратите внимание, что проблемы параллелизма очень сложно воспроизвести, и положительный синтетический тест не дает вам практически никаких гарантий. Это широкая тема, поэтому рекомендуем вам обратиться к книге Гетца за подробностями.

Я бы изменил реализацию пула соединений, а именно: https://github.com/swaldman/c3p0 или https://commons.apache.org/proper/commons-dbcp/ или https://brettwooldridge.github.io/HikariCP/.

...