pymssql的Connection相關特性淺析

關於Python的pymssql模塊,以前研究時總結了pymssql默認關閉自動模式開啓事務行爲淺析這篇博客,可是在測試過程當中又發現了幾個問題,下面對這些問題作一些淺析,若有不足或不正確的地方,敬請指出。html

 

 

1: pymssql的commit函數能夠提交兩次或屢次python

 

Connection.commit()sql

 

Commit current transaction. You must call this method to persist your data if you leave autocommit at its default value, which is False數據庫

 

     

咱們知道pymssql模塊裏面有commit函數表示提交事務,因爲某個特殊緣由,測試過程當中發現執行屢次commit都OK,不會報錯,以下代碼所示。app

 

 

 
# -*- 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.commit()
dest_db_conn.close()

 

 

其實咱們用SQL Profile跟蹤一下就會知道,多執行一次commit,至關於在SQL Server數據庫多執行了一次下面SQL,顯然不會出現什麼問題,可是也沒有什麼用處,因此這個應該只提交一次就OK了。這個問題,其實一開始對於我來講還有點震驚。瞭解過原理後,其實發現也就那麼一回事。若是你是驅動的開發者而言,也不可能讓第二次commit報錯,若是這樣的話,那麼程序的健壯性就有問題了。函數

 

BEGIN TRAN測試

    COMMIT TRAN;fetch

 

clip_image001

 

 

 

 

2: pymssql的close函數能夠關閉屢次?ui

 

Connection.close()  Close the connectionthis

 

關於pymssql中的close函數表示關閉數據庫鏈接,第一次執行就已經關閉了數據庫鏈接,執行第二次close沒有報任何錯誤,可是若是在鏈接關閉後,再執行查詢之類的操做,就會報pymssql.InterfaceError: Connection is closed這類錯誤,以下所示,簡單修改上面代碼,就能夠測試、驗證:

 

 

dest_db_conn.commit()
dest_db_conn.close()
sub_cursor.execute(
'SELECT COUNT(*) AS RecordNum FROM msdb.dbo.sysmail_account'
)
dest_db_conn.close()


 

#python TranTest.py 
 
Traceback (most recent call last):
  File "TranTest.py", line 45, in <module>
    sub_cursor.execute('SELECT COUNT(*) AS RecordNum FROM msdb.dbo.sysmail_account')
  File "src/pymssql.pyx", line 448, in pymssql.Cursor.execute
  File "src/pymssql.pyx", line 238, in pymssql.Connection._conn.__get__
pymssql.InterfaceError: Connection is closed.

 

 

我的猜想驅動程序已經關閉數據庫連接了,第二次執行close函數時,可能驅動底層檢測到數據庫鏈接已經關閉,直接退出了,不作任何操做。可是若是數據庫鏈接關閉後,再去執行相關SQL,此時就會報Connection is closed這類錯誤了。

 

 

3: 若是忘記提交或回滾事務,那麼腳本執行完成後會回滾嗎? 何時回滾呢? 另外,它會阻塞其它會話嗎? 阻塞的時間有多長?

 

# -*- 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("UPDATE TEST SET NAME='KKK' WHERE ID=100")
 
 
#dest_db_conn.commit()
#dest_db_conn.close()
dest_db_conn.close()

 

爲了搞清楚上面這些問題,我修改了上面腳本,執行後,我去查詢數據庫, 發現即便上面的Python腳本沒有提交事務,可是不會阻塞其它會話(實際上是由於事務已經回滾了),對應的會話已經不存在了。猜想是由於Python腳本執行完成後,關閉了TCP層的鏈接而觸發底層驅動關閉數據庫鏈接(在關閉數據庫鏈接以前,回滾了沒有提交的事務)。

 

那麼怎麼驗證呢? 很簡單,咱們使用休眠函數sleep,在關閉數據庫聯機(dest_db_conn.close()) 前讓其休眠100秒,



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("UPDATE TEST SET NAME='KKK' WHERE ID=100")

time.sleep(100)

dest_db_conn.close()

 

 

而後在這期間,咱們就能夠查看會話信息、查看未提交的事務,構造阻塞會話等等。以下所示:

SELECT * FROM sys.sysprocesses WHERE loginame='xxx'
 
DECLARE @tab TABLE
    (
      NAME VARCHAR(100) ,
      value VARCHAR(200)
    );
INSERT  INTO @tab
        EXEC ( 'DBCC OPENTRAN WITH TABLERESULTS'
            );
SELECT  NAME ,
        CAST(value AS DATETIME) startDate ,
        GETDATE() currentDate ,
        DATEDIFF(s, CAST(value AS DATETIME), GETDATE()) diffsecond
FROM    @tab
WHERE   NAME IN ( 'OLDACT_STARTTIME' );
SELECT  spid ,
        blocked ,
        DB_NAME(sp.dbid) AS DBName ,
        program_name ,
        waitresource ,
        lastwaittype ,
        sp.loginame ,
        sp.hostname ,
        A.[text] AS [TextData] ,
        SUBSTRING(A.text, sp.stmt_start / 2,
                  ( CASE WHEN sp.stmt_end = -1 THEN DATALENGTH(A.text)
                         ELSE sp.stmt_end
                    END - sp.stmt_start ) / 2) AS [current_cmd]
FROM    sys.sysprocesses AS sp
        OUTER APPLY sys.dm_exec_sql_text(sp.sql_handle) AS A
WHERE   spid = ( SELECT CASE WHEN ISNUMERIC(value) = 0 THEN -1
                             ELSE value
                        END
                 FROM   @tab
                 WHERE  NAME IN ( 'OLDACT_SPID' )
               );

 

 

clip_image002

 

 

那麼爲何說是Python執行完成後,關閉TCP鏈接觸發了底層驅動作這個事情呢? 你測試時,發現執行完腳本後,都會有一個Audit Logout,以下截圖所示,另外,你也能夠將上面腳本的休眠函數和關閉數據庫鏈接註釋掉,你會發現,即便不關閉數據庫鏈接,Python腳本執行完成後,事務也回滾了,數據庫鏈接也關閉了。其實若是你進行了上面測試,第三個問題已經基本不用回答了。顯然已經不言而喻了

 

#time.sleep(100)
    #dest_db_conn.close()

 

clip_image003

 

Audit Logout:Records all new disconnect events since the trace started, such as when a client issues a disconnect command

相關文章
相關標籤/搜索