Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
mssql-python sürücüsü, commit, rollback, autocommit yapılandırması ve işlem yalıtım düzeyleri dâhil olmak üzere tam işlem denetimini destekler.
İşlem temelleri
Bir işlem, veritabanı işlemlerini tek bir iş biriminde gruplar. İşlemler ACID özelliklerini takip eder:
- Atomiklik: Tüm operasyonlar başarılı olur ya da hepsi başarısız olur.
- Tutarlılık: Veritabanı geçerli durumda kalır.
- İzolasyon: Eşzamanlı işlemler birbirine müdahale etmez.
- Dayanıklılık: Kararlı değişiklikler sistem arızalarına dayanır.
Otomatik onay modu
Ayar, autocommit değişikliklerin otomatik olarak yapılıp yapılmadığını kontrol eder.
Otomatik commit devre dışı (varsayılan)
Varsayılan olarak, autocommit=False. Açıkça değişiklik yapmalısınız.
import mssql_python
conn = mssql_python.connect(connection_string)
print(conn.autocommit) # False
cursor = conn.cursor()
cursor.execute("CREATE TABLE #TxnBasic (Name NVARCHAR(50))")
cursor.execute("INSERT INTO #TxnBasic (Name) VALUES ('Widget')")
cursor.execute("INSERT INTO #TxnBasic (Name) VALUES ('Gadget')")
# Changes are staged but not visible to other connections
conn.commit() # Now changes are permanent
conn.close()
Eğer kesinleştirmezseniz, bağlantı kapandığında sürücü değişiklikleri atar.
Otomatik kaydetme etkin
Ayarlarsanız autocommit=True, sürücü her bir ifadeyi hemen uygular:
conn = mssql_python.connect(connection_string, autocommit=True)
# OR
conn.setautocommit(True)
cursor = conn.cursor()
cursor.execute("CREATE TABLE #AutoDemo (Name NVARCHAR(50))")
cursor.execute("INSERT INTO #AutoDemo (Name) VALUES ('Widget')")
# Immediately committed - no explicit commit needed
Caution
Otomatik commit etkinleştirildiğinde, grup olarak birden fazla ifadeyi geri alamazsınız. Otomatik commit sadece kullanım alanınıza uygun olduğunda kullanın.
İşleme ve geri alma
Taahhüt etmek
Bekleyen değişiklikleri kalıcı hale getirmek için commit() çağırın:
cursor.execute("CREATE TABLE #CommitDemo (Name NVARCHAR(50), Price DECIMAL(10,2), CategoryID INT)")
cursor.execute("INSERT INTO #CommitDemo VALUES ('A',10,1),('B',20,1)")
cursor.execute("UPDATE #CommitDemo SET Price = Price * 1.1 WHERE CategoryID = 1")
conn.commit() # The update is now permanent
Geriye Alma
Bekleyen değişiklikleri iptal etmek için rollback() çağırın:
try:
cursor.execute("CREATE TABLE #RollDemo (Name NVARCHAR(50), Price DECIMAL(10,2), CategoryID INT)")
cursor.execute("INSERT INTO #RollDemo VALUES ('A',10,1),('B',20,1)")
cursor.execute("UPDATE #RollDemo SET Price = Price * 1.1 WHERE CategoryID = 1")
# Verify the update
cursor.execute("SELECT AVG(Price) FROM #RollDemo WHERE CategoryID = 1")
avg_price = cursor.fetchval()
if avg_price > 100:
conn.rollback() # Price too high, undo both updates
print("Rolled back: average price would exceed limit")
else:
conn.commit()
except Exception as e:
conn.rollback() # Undo on error
raise
İmleç düzeyinde onaylama ve geri alma
Kolaylık sağlamak için, imleçler üzerinde commit() ve rollback() çağrıları yapabilirsiniz:
cursor = conn.cursor()
cursor.execute("CREATE TABLE #CursorDemo (Name NVARCHAR(50))")
cursor.execute("INSERT INTO #CursorDemo (Name) VALUES ('Widget')")
cursor.commit() # Delegates to connection
cursor.execute("DELETE FROM #CursorDemo WHERE Name = 'Widget'")
cursor.rollback() # Delegates to connection
Note
İmleç seviyesindeki commit ve rollback, aynı bağlantıdaki tüm imleci etkiler, sadece çağırdığınız imleci değil.
Bağlam yöneticileri
Bağlantı bağlam yöneticisi, sorunsuz bir çıkışta işlemi onaylar; bir istisna oluşursa da geri alır. Bağlantı her zaman çıkışta kapanır.
autocommit=True ayarlandığında, kesinleştirme ve geri alma çağrıları etkisiz olur.
with mssql_python.connect(connection_string) as conn:
cursor = conn.cursor()
cursor.execute("CREATE TABLE #CtxDemo (Name NVARCHAR(50))")
cursor.execute("INSERT INTO #CtxDemo (Name) VALUES ('Widget')")
cursor.execute("INSERT INTO #CtxDemo (Name) VALUES ('Gadget')")
# Transaction is committed and connection is closed on exit
Bir istisna gerçekleşirse, işlem geri alınır:
try:
with mssql_python.connect(connection_string) as conn:
cursor = conn.cursor()
cursor.execute("CREATE TABLE #TxnDemo (Name NVARCHAR(50))")
cursor.execute("INSERT INTO #TxnDemo (Name) VALUES ('Widget')")
raise ValueError("Something went wrong")
except ValueError:
pass
# Transaction is rolled back and connection is closed on exit
İşlem yalıtım düzeyleri
İzolasyon seviyeleri, işlemlerin eşzamanlı işlemlerle nasıl etkileşime girdiğini kontrol eder. İzolasyon seviyesini şu şekilde set_attr()ayarlayın:
import mssql_python
conn = mssql_python.connect(connection_string)
# Set isolation level
conn.set_attr(
mssql_python.SQL_ATTR_TXN_ISOLATION,
mssql_python.SQL_TXN_SERIALIZABLE
)
Mevcut izolasyon seviyeleri
| Sabit | Açıklama |
|---|---|
SQL_TXN_READ_UNCOMMITTED |
Diğer işlemlerdeki onaylanmamış değişiklikleri okuyabilir (kirli okumalara izin verilir) |
SQL_TXN_READ_COMMITTED |
Yalnızca belirlenmiş verileri okur (SQL Server'da varsayılan) |
SQL_TXN_REPEATABLE_READ |
Bir işlem içinde tutarlı okumaları garanti eder |
SQL_TXN_SERIALIZABLE |
En yüksek izolasyon; İşlemler ardışık olarak çalışıyor gibi görünüyor |
İzolasyon seviyesi seçin
| Kullanım örneği | Önerilen düzey |
|---|---|
| Genel OLTP iş yükleri |
READ_COMMITTED (varsayılan) |
| Tutarlı anlık görüntüler gerektiren raporlar |
REPEATABLE_READ ya da anlık görüntü |
| Doğruluk gerektiren finansal hesaplamalar | SERIALIZABLE |
| Eskimiş verileri tolere edebilen, okuma ağırlıklı iş yükleri | READ_UNCOMMITTED |
Anlık görüntü yalıtımı
Snapshot izolasyonu için Transact-SQL (T-SQL) kullanın. Snapshot izolasyonu, tempdb içinde satır sürümü oluşturma kullanır; bu, yoğun yazma iş yükleri altında depolama gereksinimlerini artırabilir.
# Enable snapshot isolation on the database (one-time setup, requires autocommit)
conn.commit()
conn.autocommit = True
cursor.execute("ALTER DATABASE AdventureWorks2022 SET ALLOW_SNAPSHOT_ISOLATION ON")
# Set isolation level while still in autocommit, then start the transaction
cursor.execute("SET TRANSACTION ISOLATION LEVEL SNAPSHOT")
conn.autocommit = False
cursor.execute("SELECT TOP 5 Name, ListPrice FROM Production.Product")
rows = cursor.fetchall()
for row in rows:
print(row.Name, row.ListPrice)
conn.commit()
İç içe işlemler ve kaydetme noktaları
SQL Server, bir işlem içinde kısmi geri alma için kaydetme noktalarını destekler.
cursor = conn.cursor()
cursor.execute("BEGIN TRANSACTION")
cursor.execute("CREATE TABLE #SaveDemo (Name NVARCHAR(50))")
cursor.execute("INSERT INTO #SaveDemo (Name) VALUES ('Widget')")
cursor.execute("SAVE TRANSACTION SaveDemoPoint")
cursor.execute("INSERT INTO #SaveDemo (Name) VALUES ('Gadget')")
# Roll back to savepoint, keeping first insert
cursor.execute("ROLLBACK TRANSACTION SaveDemoPoint")
cursor.execute("COMMIT TRANSACTION")
Kilitlenmeleri ele alma
Çıkmazlar, iki işlemin birbirinin kilitlerini beklemesiyle oluşur. SQL Server çıkmazları otomatik olarak tespit eder ve bir işlemi sonlandırır.
import time
def execute_with_retry(conn, cursor, sql, params=None, max_retries=3):
"""Execute SQL with deadlock retry logic."""
for attempt in range(max_retries):
try:
cursor.execute(sql, params)
return
except mssql_python.OperationalError as e:
if "1205" in str(e): # Deadlock error number
if attempt < max_retries - 1:
conn.rollback() # Clear the failed transaction
time.sleep(0.1 * (2 ** attempt)) # Exponential backoff
continue
raise
raise Exception(f"Failed after {max_retries} attempts")
En iyi uygulamalar
İşlemleri kısa tutarak kilit süresini ve çıkmaz potansiyelini en aza indirin.
Atomik olması gereken çoklu ifadeli işlemler için autocommit=False kullanın.
İstisnaları her zaman ele alın geri alma ile:
conn = None try: conn = mssql_python.connect(connection_string) cursor = conn.cursor() cursor.execute("CREATE TABLE #RollbackPattern (ID INT, Name NVARCHAR(50))") cursor.execute("INSERT INTO #RollbackPattern (ID, Name) VALUES (1, 'Widget')") cursor.execute("UPDATE #RollbackPattern SET Name = 'Updated Widget' WHERE ID = 1") conn.commit() except Exception: if conn is not None: conn.rollback() raise finally: if conn is not None: conn.close()İşlemleri otomatik olarak yönetmek için bağlam yöneticileri kullanın. Bağlam yöneticisi, sorunsuz çıkışta işlemi onaylar ve bir istisna durumunda geri alır.
with mssql_python.connect(connection_string) as conn: cursor = conn.cursor() cursor.execute("CREATE TABLE #ContextManagerDemo (ID INT, Name NVARCHAR(50))") cursor.execute("INSERT INTO #ContextManagerDemo (ID, Name) VALUES (1, 'Widget')") cursor.execute("UPDATE #ContextManagerDemo SET Name = 'Committed Widget' WHERE ID = 1") # Committed automatically on exitTutarlılık gereksinimlerinize ve performans ihtiyaçlarınıza göre uygun izolasyon seviyelerini seçin.
Güncellemelerin kaybolmasını önlemek için okuma-değiştir-yazma kalıpları için kilitleme ipuçları kullanın. Aynı işlem içinde güncellenecek bir değeri okuduğunuzda, kilitleri erkenden edinmek ve tutarlı bir kilit sıralaması oluşturmak için SELECT üzerinde
WITH (UPDLOCK, ROWLOCK)gibi ipuçlarını kullanın; bu, kilitlenme riskini azaltır.# Good: Acquire lock during read to prevent lost update pattern cursor.execute(""" SELECT Balance FROM Accounts WITH (UPDLOCK, ROWLOCK) WHERE ID = %(id)s """, {"id": account_id}) balance = cursor.fetchval() if balance >= amount: cursor.execute(""" UPDATE Accounts SET Balance = Balance - %(amount)s WHERE ID = %(id)s """, {"amount": amount, "id": account_id})Geçici hatalar (deadlock) için yeniden deneme mantığı uygulayın.
Örnek: Transfer fonları (atomik operasyon)
Bu örnek, eşzamanlı senaryolarda güncellemelerin kaybolmasını önlemek için kilit ipuçları ile atomik transfer mantığı gösterilir:
def transfer_funds(conn, from_account, to_account, amount):
"""Transfer funds atomically between accounts."""
cursor = conn.cursor()
try:
# Read balance with lock hint to prevent lost updates
cursor.execute(
"SELECT Balance FROM Accounts WITH (UPDLOCK, ROWLOCK) WHERE AccountID = %(account_id)s",
{"account_id": from_account}
)
balance = cursor.fetchval()
if balance is None:
raise ValueError(f"Source account {from_account} not found")
if balance < amount:
raise ValueError("Insufficient funds")
# Debit source account
cursor.execute(
"UPDATE Accounts SET Balance = Balance - %(amount)s WHERE AccountID = %(account_id)s",
{"amount": amount, "account_id": from_account}
)
# Credit destination account
cursor.execute(
"UPDATE Accounts SET Balance = Balance + %(amount)s WHERE AccountID = %(account_id)s",
{"amount": amount, "account_id": to_account}
)
if cursor.rowcount != 1:
raise ValueError(f"Destination account {to_account} not found")
conn.commit()
print(f"Transferred ${amount} from {from_account} to {to_account}")
except Exception:
conn.rollback()
raise
SELECT üzerindeki kilit ipuçları WITH (UPDLOCK, ROWLOCK) , kilidin erken alınmasını sağlar. Bu, başka bir işlemin aynı bakiyeyi eşzamanlı okumasını ve her iki işlemin eski bakiyeyi okuyup ayrı güncellemeler yapması ve sadece son güncellemenin devam ettiği kayıp bir güncelleme senaryosu yaratmasını engeller.
Geçici tablo kapsamı
Geçici tablolar (#tablename) oturum kapsamındadır, ancak oluşturulmaları geçerli işlemin bir parçasıdır. Geçici bir tablo oluşturursanız ve işlem geri çekilirse, geçici tablo kaldırılır:
conn = mssql_python.connect(connection_string) # autocommit=False
cursor = conn.cursor()
cursor.execute("CREATE TABLE #Staging (ID INT, Name NVARCHAR(50))")
cursor.execute("INSERT INTO #Staging VALUES (1, 'test')")
# Rollback removes the temp table entirely
conn.rollback()
# This fails: Invalid object name '#Staging'
try:
cursor.execute("SELECT * FROM #Staging")
except mssql_python.ProgrammingError:
print("Temp table was dropped by rollback")
Geçici bir tabloyu veri işleminizden bağımsız tutmak için, oluşturduktan sonra commit yapın:
cursor.execute("CREATE TABLE #Staging (ID INT, Name NVARCHAR(50))")
conn.commit() # Temp table persists regardless of later rollbacks
# Now data operations can roll back without losing the table
cursor.execute("INSERT INTO #Staging VALUES (1, 'test')")
conn.rollback() # Data is gone, but #Staging still exists
Otomatik commit gerektiren DDL ifadeleri
Bazı DDL ifadeleri, örneğin CREATE DATABASE, ALTER DATABASE, ve DROP DATABASE, bir işlem içinde çalışamaz.
autocommit=True bunları çalıştırmadan önce ayarlayın:
conn.autocommit = True
cursor.execute("CREATE DATABASE TestDB")
conn.autocommit = False # Return to transactional mode
Eğer CREATE DATABASE öğesini autocommit=False ile çalıştırırsanız, şu hatayı alırsınız: CREATE DATABASE statement not allowed within multi-statement transaction.