使用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時,那么可能造成很多不必要的阻塞。而且相信很多不明所以的同學還會一臉懵逼。
參考資料:
https://github.com/pymssql/pymssql/issues/460
http://pymssql.org/en/stable/ref/pymssql.html#connection-object-methods



