причина
В последнее время производственная система продолжала сталкиваться с некоторыми исключениями базы данных, что приводило к сбою выполнения SQL.
Среда приложения
Java 1.7 + Mysql 5.6 + spring + ibatis
Устранение неполадок
Провал различных аномалий фиксируют немного, вплоть до следующих типов встречающихся аномалий.
- java.net.SocketTimeoutException: Read timed out
- java.sql.BatchUpdateException: после закрытия оператора операции не допускаются.
- com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
- java.io.EOFException: Can not read response from server. Expected to read 8 bytes, read 7 bytes before connection was unexpectedly lost.
- java.net.SocketException: Software caused connection abort: recv failed
SocketTimeoutException
Для первого приведенного выше случая легко нарисовать буквальный тайм-аут чтения. Однако существуют различные таймауты для запроса данных JDBC, внимательно изучите его и разберитесь.
JBDC может установить время ожидания как время ожидания транзакции, время ожидания оператора, время ожидания сокета и время ожидания соединения. Вышеупомянутые уровни тайм-аута расположены сверху вниз.
Ниже мы разберемся в этих видах таймаутов сверху и снизу.
Время ожидания транзакции: время ожидания транзакции, состоящее из нескольких операторов. Время ожидания транзакции = N*Statement.timeout + другое время выполнения кода. Поэтому нам не следует выполнять какие-то длительные вызовы, такие как RPC или HTTP, за одну транзакцию. Если время на этих вызовах застрянет, это приведет к истечению времени ожидания транзакции и откату.
Время ожидания оператора: время выполнения оператора, которое можно использовать для ограничения времени выполнения оператора запроса. Но в случае сбоя сети этот тайм-аут не сработает. В конце концов Socket TimeOut необходимо разрешить.
Socket TimeOut: в настоящее время существует четыре типа JDBC, и мы обычно используем драйвер протокола базы данных (драйвер базы данных-протокола (драйвер Pure Java) или тонкий драйвер). Этот драйвер использует Socket для связи с базой данных. Если не задано, при сбое сети чтение SCOKET будет напрямую заблокировано. После установки java.net.SocketTimeoutException: превышение времени ожидания чтения будет сгенерировано по истечении времени, что предотвратит долгосрочную блокировку и недоступность системы.
ConnectionTimeout: этот параметр тайм-аута также связан с установлением соединения сокетом. Если он не установлен, как только параметры адреса, связанные с базой данных, будут неправильными, он будет блокировать установление соединения с базой данных в течение длительного времени.
С помощью картинки в Интернете можно наглядно проанализировать отношения между первыми тремя.
На самом деле тайм-аут сокета есть и на уровне операционной системы. Каждая операционная система может установить соответствующий период ожидания сокета, и если JDBC не установлен, период ожидания операционной системы также будет отключен. Но мы не можем полагаться на этот тайм-аут, потому что это время совершенно неуправляемо, и мы должны установить его явно.
Подводя итог, нам нужно установить по крайней мере ConnectionTimeout и Socket TimeOut для связанных параметров JDBC.Для операторов sql может быть установлено время ожидания оператора. Если есть транзакция, вы также можете установить соответствующий тайм-аут транзакции.
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
Это исключение CommunicationsException вызовет следующие два исключения из-за других базовых исключений.
- java.io.EOFException: Can not read response from server. Expected to read 8 bytes, read 7 bytes before connection was unexpectedly lost.
- java.net.SocketException: Software caused connection abort: recv failed
Когда я впервые столкнулся с этим исключением, я проверил его в соответствии с CommunicationsException, что обычно означает, что серверная часть Mysql будет обнаруживать незанятые соединения и активно отключаться по истечении времени ожидания, что приводит к сбою соединения клиента.
那么什么是 mysql 的空闲连接那?简单来说,mysql 连接进程 Command 为 sleep 状态。 мы можем использоватьshow processlist ;
Посмотреть запущенный процесс. Примеры бездействующих процессов:
jdbc-соединения обнаруживают незанятые соединения на основе mysql wait_timeout. Если соединение все еще простаивает в течение времени ожидания, сервер mysql отключит соединение. В этом случае используется имитация кодирования. Используйте следующий код:
try {
Connection connection = dataSource.getConnection();
TimeUnit.SECONDS.sleep(11L);
run.query(connection,"select 'X'", h);
//Thread.sleep(60000);
} catch (Exception e) {
log.error("查询异常", e);
}
Затем установите mysql wait_timeout=10 . После того, как следующий код моделирования получает соединение, он спит в течение 11 с.Во время этого процесса mysql активно отключает соединение.Когда он фактически выполняется, программа выдает исключение.
Сообщается следующее об ошибке:
Но основным исключением является java.net.SocketException: программное обеспечение вызвало прерывание соединения: сбой recv, а не java.io.EOFException.
Эта ошибка очень подозрительна. Затем внимательно посмотрите на описание EOFException.Expected to read 8 bytes, read 7 bytes before connection was unexpectedly lost, видно, что это соединение на самом деле все еще доступно в течение определенного периода времени, и есть данные для чтения, но в процессе чтения данных соответствующие данные, соответствующие количеству, не считываются, что приводит к ошибке. Вышеприведенный код имитирует ситуацию, когда соединение вступило в силу, когда соединение используется.
воплощать в жизньshow variables like '%timeout%';
Посмотреть другие тайм-ауты mysql,
Из приведенного выше рисунка видно, чтоnet_read_timeoutиnet_write_timeoutэти два параметра.
Посмотреть официальную документацию mysql
net_read_timeoutПо умолчанию 30 с Количество секунд ожидания дополнительных данных от соединения перед прекращением чтения. Когда сервер читает от клиента, net_read_timeout — это значение тайм-аута, определяющее время прерывания. Когда сервер пишет клиенту,net_write_timeoutПо умолчанию 60s Количество секунд, чтобы дождаться блока, которое будет записано в соединение, прежде чем прервать запись
net_write_timeout контролирует время ожидания для сервера mysql для записи данных клиенту. В этом случае поставьте короткую точку на чтение MysqlIO,
Когда программа запустится, сначала отпустите точку останова, проверьте список процессов mysql и посмотрите, как состояние процесса mysql отправляется клиенту, и точка останова вступит в силу в это время. В это время после ожидания 60 с успешно появляется следующая ошибка.
net_read_timeout этот тайм-аут не умеет имитировать :(.
Подводя итог, можно сказать, что если возникает исключение com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: сбой канала связи, соединение с базой данных завершается сбоем, но могут быть различные причины сбоя, которые обычно связаны с различными параметрами тайм-аута mysql.
BatchUpdateException
Эта ошибка возникает, когда данные импортируются пакетами. На тот момент объем данных был примерно больше 20W, а потом при пакетной вставке выбрасывалось исключение. Ниже приведен код вставки пакета.
getSqlMapClientTemplate().execute(new SqlMapClientCallback<Object>() {
@Override
public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
executor.startBatch();
for (int i = 0; i < 200000; i++) {
Demo demo = new Demo();
demo.setName("asd");
demo.setAge(String.valueOf(i));
demo.setSubject("adassad");
// 原项目 这里会发生一次 RPC调用 现用 Sleep 代替
try {
TimeUnit.MILLISECONDS.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
executor.insert("insertDemo", demo);
}
executor.executeBatch();
return null;
}
});
Этот код использует функцию Batch ibatis для вставки данных в партии.
На самом деле, мы видим эту информацию об исключении, java.sql.BatchUpdateException: ясно видно, что никакие операции, разрешенные после закрытия оператора, будут закрыты, потому что причина оператора, а затем почему оператор будет закрыт заранее. Здесь мы прослеживаем источник.
Теперь давайте взглянем на метод SqlmapClientCallback DoinsqlmapClient. Отлаживатьexecutor.startBatch()Наконец, метод вызывает метод SqlMapExecutorDelegate.startBatch.
Глядя на комментарии к коду, вы можете видеть, что цель состоит в том, чтобы установить значение состояния, которое будет использоваться ниже.
В этот момент мы смотрим наexecutor.insert, Обычно этот метод должен выполнять оператор SQL, а затем вставлять его в базу данных. Но просмотрев исходный код, вы обнаружите, что последним вызовом, который он вызывает, является MappedStatement.sqlExecuteUpdate, а установленный выше атрибут пакета сеанса оценивается в начале метода входа. Конечно, вначале это свойство было установлено в true, поэтому в настоящее время действие вставки sql не выполняется, но этот sql и связанные с ним параметры хранятся в памяти.
protected int sqlExecuteUpdate(StatementScope statementScope, Connection conn, String sqlString, Object[] parameters) throws SQLException {
if (statementScope.getSession().isInBatch()) {
getSqlExecutor().addBatch(statementScope, conn, sqlString, parameters);
return 0;
} else {
return getSqlExecutor().executeUpdate(statementScope, conn, sqlString, parameters);
}
}
Наконец мы смотримexecutor.executeBatchНаконец, метод называетсяStatement.executeBatch, и фактически начать выполнять массовые вставки.
После прочтения логики в SqlMapClientCallback теперь давайте посмотрим на логику кода SqlMapClientTemplate.execute.
Глядя на диаграмму последовательности, мы видим, что когда логика метода обратного вызова SqlMapClientCallback фактически выполняется, сначала будет получено соединение из источника данных, а затем будет выполнена логика обратного вызова SqlMapClientCallback, и, наконец, соединение будет освобождено. В этом процессе, если время выполнения метода SqlMapClientCallback слишком велико, например, вызов Dubbo будет происходить каждый раз, когда цикл for вызывается в нашем методе, а затем, поскольку этому циклу необходимо пройти более 20 Вт данных, он для завершения цикла потребуется более половины времени (при условии, что вызов dubbo занимает 10 мс), а время ожидания нашего сервера mysql составляет 300 с, поэтому сервер mysql заранее активно освобождает незанятые соединения, а затем ждет, пока пакетная вставка не будет фактически завершена. выполняется, что вызовет указанное выше исключение.
Не по теме: когда mysql jdbc использует пакетную вставку, ее необходимо установитьrewriteBatchedStatements=trueпараметр. Если он не установлен, это эквивалентно вставке данных с использованием цикла for в конце, что не повышает эффективность вставки.
Ссылаться на
- Тип драйвера JDBC
- Understanding JDBC Internals & Timeout Configuration
- Глубокое понимание тайм-аутов JDBC
- Углубленный анализ механизма тайм-аута JDBC
- mysql: показать подробное объяснение списка процессов
- Расскажите о настройке jdbc socketTimeout
Если вы считаете, что это хорошо, пожалуйста, поставьте автору большой палец вверх~ Спасибо.
Читатели, которым понравилась эта статья, добро пожаловать в долгое нажатие, чтобы следить за сообщением программы номера подписки ~ позвольте мне поделиться программой с вами.