使用Python採集SQL Server資料庫伺服器磁碟信息時,遇到了一個錯誤“CONFIG statement cannot be used inside a user transaction.DB-Lib error message 20018, severity 16”,那麼為什麼遇到這個錯誤... ...
使用Python採集SQL Server資料庫伺服器磁碟信息時,遇到了一個錯誤“CONFIG statement cannot be used inside a user transaction.DB-Lib error message 20018, severity 16”,那麼為什麼遇到這個錯誤呢? 其實很簡單,就是因為SQL Server事務中不允許使用RECONFIGURE,我們可以簡單模擬構造一下這個錯誤,如下所示:
BEGIN TRAN
EXEC sp_configure 'show advanced options', 1
RECONFIGURE WITH OVERRIDE;
COMMIT TRAN;
我的Python腳本中,訪問資料庫的SQL沒有使用事務(沒有BEGIN TRAN ... COMMIT TRAN),那麼是否pymssql中預設會開啟事務呢? 我們可以構造一個Python腳本訪問SQL Server 資料庫,然後我們使用SQL Profile跟蹤一下,就基本上能知道是否pymssql會預設開啟事務。Python腳本TranTest.py如下所示:
# -*- coding: utf-8 -*-
'''
-------------------------------------------------------------------------------------------
-- Script Name : TranTest.py
-------------------------------------------------------------------------------------------
'''
import pymssql
import logging
import os.path
import os
import base64
from cryptography.fernet import Fernet
key=bytes(os.environ.get('key'),encoding="utf8")
cipher_suite = Fernet(key)
with open('/home/konglb/python/conf/ms_db_conf.bin', 'rb') as file_object:
for line in file_object:
encryptedpwd = line
decrypt_pwd = (cipher_suite.decrypt(encryptedpwd))
password_decrypted = bytes(decrypt_pwd).decode("utf-8") #convert to string
env_db_user=os.environ.get('db_user')
db_user=base64.b64decode(bytes(env_db_user, encoding="utf8"))
dest_db_conn = pymssql.connect(host=os.environ.get('db_host'),
user=bytes.decode(db_user),
password=password_decrypted,
database='master',
charset="utf8");
sub_cursor = dest_db_conn.cursor(as_dict=True)
sub_cursor.execute('SELECT COUNT(*) AS RecordNum FROM msdb.dbo.sysmail_account')
result_rows =sub_cursor.fetchone()
print(result_rows["RecordNum"])
#dest_db_conn.commit()
dest_db_conn.close()
如下截圖所示,我們發現pymssql會對任何訪問SQL Server的SQL加上BEGIN TRAN,也許眼尖的同學發現了端倪,SQL Profile捕獲的SQL,有BEGIN TRAN,但是沒有COMMIT TRAN,這個是因為上面的Python代碼中沒有提交事務(#dest_db_conn.commit() 註釋了)
修改上面Python代碼,在關閉資料庫連接前,加上一行代碼dest_db_conn.commit(),然後重覆上面實驗就能看到COMMIT TRAN了,如下所示:
dest_db_conn.commit()
dest_db_conn.close()
那麼pymssql中是否可以關閉事務呢? 因為有些普通、簡單的查詢,根本沒有必要使用事務,其實pymmsql的Connection介面其實是提供了這麼一個功能的,官方文檔的介紹如下:
Connection object methods
Connection.autocommit(status)
Where status is a boolean value. This method turns autocommit mode on or off.
By default, autocommit mode is off, what means every transaction must be explicitly committed if changed data is to be persisted in the database.
You can turn autocommit mode on, what means every single operation commits itself as soon as it succeeds.
A pymssql extension to the DB-API 2.0.
我們知道,SQL Server在預設情況下資料庫連接處於自動提交模式(autocommit mode),每個SQL命令一旦被執行便提交給資料庫,一旦提交就無法回滾。 在資料庫中不支持事務的情況下,自動提交模式是唯一支持的模式。 在此類資料庫語句僅在提交後可以執行它們並沒有方法回滾它們;它們因此始終處於自動提交模式.
那麼我們測試一下就會發現autocommit=True的情況下,pymmsql不會自動給SQL加上BEGIN TRAN了(測試期間,犯了個迷糊,弄混了一個前提條件,而且沒有充分測試,就做出了一個相反的結論,後續自己一直折騰時才發現這個問題)
修改前代碼:
dest_db_conn = pymssql.connect(host=os.environ.get('db_host'),
user=bytes.decode(db_user),
password=password_decrypted,
database='master',
charset="utf8");
修改後代碼:
dest_db_conn = pymssql.connect(host=os.environ.get('db_host'),
user=bytes.decode(db_user),
password=password_decrypted,
database='master',
charset="utf8",
autocommit=True);
關於pymssql預設情況下會關閉自動提交模式(autocommit mode)開啟事務的行為,一定要小心,如果你SQL腳本裡面有DML操作而且忘記加commit時,那麼可能造成很多不必要的阻塞。而且相信很多不明所以的同學還會一臉懵逼。