layout | title |
---|---|
article |
Неожиданности, Антипаттерны и Ограничения |
Хотя database/sql
прост после того как вы привыкнете к нему, вы можете удивиться сложностью некоторых сценариев использования, которые он поддерживает. Это характерно для основных библиотек Go.
Иногда использование ресурсов может вызвать проблемы:
Как упоминалось на этом сайте, если вы не используете database/sql
в соответствии с его предназначением, вы можете создать себе проблемы, обычно связанные с использованием или недопустимым использованием ресурсов:
rows.Close()
заблокирует соединения из пула.Query()
для запроса, который не возвращает строки, будет забирать соединение из пула.uint64
Здесь представлено неожиданное сообщение об ошибке. Вы не сможете передать большие целые числа типа uint64
, если старший бит установлен:
_, err := db.Exec("INSERT INTO users(id) VALUES", math.MaxUint64)
Это вызовет ошибку. Будьте осторожны при использовании значений типа uint64
, так как они могут начинаться небольшими и работать без ошибок, но со временем увеличиваться и начать вызывать ошибки.
Некоторые вещи могут менять состояние соединения, что может вызвать проблемы по двум причинам:
Например, установка текущей базы данных с помощью команды USE
является обычной практикой для многих людей. Однако в Go это затронет только соединение, которое вы используете. Если вы не в транзакции, другие заявки, которые вы полагаете выполнены на этом соединении, могут фактически выполняться на других соединениях из пула, поэтому они не будут видеть эффекты таких изменений.
Кроме того, после изменения соединения, оно вернется в пул и потенциально "запятнает" состояние для некоторого другого кода. Это одна из причин, почему вы никогда не должны выполнять команды BEGIN
или COMMIT
напрямую как SQL-запросы.
API database/sql
предоставляет абстракцию ориентированной на строки базы данных, но конкретные базы данных и драйверы могут различаться по поведению и/или синтаксису, таким образом как место заполнения подготовленных запросов.
Драйвер Go не поддерживает несколько наборов результатов от одного запроса никаким способом, и нет планов сделать это, хотя есть запрос на функциональность для поддержки массовых операций, таких как массовое копирование.
Это значит, среди прочего, что хранимая процедура, которая возвращает несколько наборов результатов, не будет работать правильно.
Вызов хранимых процедур зависит от драйвера, но в драйвере MySQL это невозможно в настоящее время. Может показаться, что вы сможете вызвать простую процедуру, которая возвращает один набор результатов, выполняя что-то вроде этого:
err := db.QueryRow("CALL mydb.myprocedure").Scan(&result)
На самом деле, это не будет работать. Вы получите следующее сообщение об ошибке: Ошибка 1312: Процедура mydb.myprocedure не может вернуть набор результатов в данном контексте. Это потому, что MySQL ожидает, чтобы соединение было установлено в режим многокомандного выполнения, даже для одного набора результатов, а драйвер не делает этого в настоящий момент (хотя см. эту задачу).
database/sql
не имеет явной поддержки множественных команд, что означает, что поведение этого зависит от сервера:
_, err := db.Exec("DELETE FROM tbl1; DELETE FROM tbl2")
Сервер имеет право интерпретировать это любым образом, что может включать возвращение ошибки, выполнение только первой команды или выполнение обеих.
Аналогично, нет возможности группировать команды в транзакции. Каждая команда в транзакции должна быть выполнена последовательно, и ресурсы в результате, такие как Row или Rows, должны быть сканированы или закрыты, чтобы освободить подлежащее соединение для следующей команды. Это отличается от обычного поведения, когда вы работаете без транзакции. В этой ситуации вполне возможно выполнить запрос, пройтись по строкам и внутри цикла выполнить запрос к базе данных (что произойдет на новом соединении):
rows, err := db.Query("select * from tbl1") // Использует соединение 1
for rows.Next() {
err = rows.Scan(&myvariable)
// СЛЕДУЮЩЕЕ ЗАПРОСИТЕЛЬСТВО НЕ ИСПОЛЬЗУЕТ СОЕДИНЕНИЕ 1, которое уже используется
db.Query("select * from tbl2 where id = ?", myvariable)
}
Но транзакции ограничиваются одним соединением, поэтому это невозможно с транзакциями:
tx, err := db.Begin()
rows, err := tx.Query("select * from tbl1") // Использует соединение tx
for rows.Next() {
err = rows.Scan(&myvariable)
// ОШИБКА! Соединение tx занято!
tx.Query("select * from tbl2 where id = ?", myvariable)
}
Go не препятствует вам попытаться это сделать. По этой причине вы можете получить поврежденное соединение, если попытаетесь выполнить еще одну команду до того, как первая освободила свои ресурсы и очистилась. Это также означает, что каждая команда в транзакции приводит к отдельному сетевому обращению к базе данных.
Предыдущий: Пул соединений Следующий: Связанные материалы и ресурсы
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )