SQLite database is locked hatası, aynı veritabanı dosyasına erişen işlemlerden biri yazma transaction’ını açık tuttuğunda ve başka bir işlem yazmak için gerekli kilidi alamadığında ortaya çıkar. Sorunu çözmek için önce açık transaction’ları commit() veya rollback() ile sonlandırmak, bağlantıları kapatmak ve aynı anda yapılan yazma işlemlerini kısa tutmak gerekir.
Bu hata tek başına veritabanı dosyasının bozulduğunu göstermez. SQLite yazma işlemlerini kilitlerle sıraya koyar ve aynı veritabanında aynı anda tek bir yazma işleminin ilerlemesine izin verir.
SQLite “database is locked” hatası hangi durumda oluşur?

SQLite’ta transaction, bir veya daha fazla SQL işleminin birlikte yönetildiği çalışma birimidir. Örneğin INSERT çalıştırdığında değişiklik transaction içinde bekleyebilir. commit() bu değişikliği kalıcı hâle getirip transaction’ı tamamlar, rollback() ise henüz commit edilmemiş değişiklikleri geri alır.
Bir bağlantı INSERT, UPDATE veya DELETE çalıştırdıktan sonra commit etmeden beklerse yazma kilidini koruyabilir. Bu sırada ikinci bağlantı da yazmaya çalışırsa SQLite, ilk transaction tamamlanana kadar bekler. Bekleme süresi dolduğunda Python tarafında sqlite3.OperationalError oluşabilir. connect() içindeki timeout parametresi, bağlantının kilit kalkana kadar ne kadar bekleyeceğini belirler.
Benzer durumlar, uygulamanın açık bir transaction’ı uzun süre kapatmaması, SQLite dosyasını kullanan başka bir veritabanı görüntüleme aracının yazma işlemi yapması veya bağlantıların uygun biçimde kapatılmaması nedeniyle de görülebilir. Açık kalan bağlantı özellikle bekleyen transaction ya da etkin cursor ile birlikteyse kilidin gereğinden uzun sürmesine yol açabilir.
Bu nedenle dosyayı silmek çözüm olarak kullanılmamalıdır. Silme işlemi veritabanındaki kayıtları ortadan kaldırabilir; asıl sorun çoğunlukla kilidi hangi bağlantının tuttuğunu ve transaction’ın neden kapanmadığını belirlemektir.
İki Python bağlantısıyla kilitlenme hatası nasıl üretilir?
Aşağıdaki örnekte iki ayrı sqlite3 bağlantısı aynı geçici dosyayı kullanır. İlk bağlantı bir kayıt ekler, fakat commit etmeden açık kalır. İkinci bağlantı kısa bir bekleme süresiyle başka bir kayıt eklemeyi dener.
import sqlite3
import tempfile
from pathlib import Path
temporary_file = tempfile.NamedTemporaryFile(
suffix=".db", delete=False
)
db_path = temporary_file.name
temporary_file.close()
first = sqlite3.connect(db_path)
second = sqlite3.connect(db_path, timeout=0.1)
try:
first.execute(
"CREATE TABLE records (id INTEGER PRIMARY KEY, name TEXT)"
)
first.commit()
first.execute(
"INSERT INTO records (name) VALUES (?)",
("İlk kayıt",)
)
second.execute(
"INSERT INTO records (name) VALUES (?)",
("İkinci kayıt",)
)
second.commit()
except sqlite3.OperationalError as error:
print(type(error).__name__, error)
finally:
first.rollback()
first.close()
second.close()
Path(db_path).unlink(missing_ok=True)
İlk bağlantıdaki INSERT henüz commit edilmediği için ikinci bağlantının yazma denemesi kilide takılır. Kısa bekleme süresi sonunda beklenen çıktı OperationalError database is locked biçimindedir. Hatanın ardından ilk bağlantıda rollback() çağrılması bekleyen değişikliği geri alır ve kilidin serbest bırakılmasına yardımcı olur.
Düzeltilmiş sürümde her yazma işlemi tamamlandıktan sonra transaction kapatılır ve bağlantılar açık bırakılmaz:
import sqlite3
import tempfile
from pathlib import Path
temporary_file = tempfile.NamedTemporaryFile(
suffix=".db", delete=False
)
db_path = temporary_file.name
temporary_file.close()
first = sqlite3.connect(db_path)
try:
with first:
first.execute(
"CREATE TABLE records (id INTEGER PRIMARY KEY, name TEXT)"
)
first.execute(
"INSERT INTO records (name) VALUES (?)",
("İlk kayıt",)
)
finally:
first.close()
second = sqlite3.connect(db_path)
try:
with second:
second.execute(
"INSERT INTO records (name) VALUES (?)",
("İkinci kayıt",)
)
finally:
second.close()
reader = sqlite3.connect(db_path)
try:
count = reader.execute(
"SELECT COUNT(*) FROM records"
).fetchone()[0]
print("Kayıt sayısı:", count)
finally:
reader.close()
Path(db_path).unlink(missing_ok=True)
Beklenen çıktı Kayıt sayısı: 2 olur. Python’daki bağlantı bağlam yöneticisi, with bloğu başarıyla tamamlandığında commit, istisna oluştuğunda rollback işlemini yönetir. Ancak bu kullanım bağlantıyı kendiliğinden kapatmaz. Bu yüzden close() çağrısı ayrıca yapılmalı ve commit edilmesi gereken değişiklikler kapanmadan önce tamamlanmalıdır.
Hangi düzeltmeler kilitlenme sorununu azaltır?

SQLite kilitlenmesini azaltmanın ilk adımı, yazma işlemlerini kısa transaction bloklarında tamamlayıp başarılı işlemlerden sonra commit() çağırmaktır. Hata oluşursa rollback() ile açık transaction sonlandırılmalı, iş bittiğinde de bağlantılar close() ile kapatılmalıdır. Transaction açık kaldığında bağlantı, yazma kilidini beklenenden uzun süre tutabilir.
Her sorgu için yeni bağlantı açıp kapatmak doğrudan her zaman kilitlenme oluşturmaz; ancak bağlantı ve transaction yönetimini dağınık hâle getirir. Açık bırakılan bağlantılar, tamamlanmamış transaction'lar veya aynı dosyaya erişen gereksiz bağlantılar çakışma ihtimalini artırabilir. Aynı SQLite dosyasını kullanan veritabanı görüntüleme aracı, test süreci veya başka bir program varsa sorun çözülene kadar kapatılmalıdır.
Python belgelerinde sqlite3.connect() için varsayılan timeout değeri 5.0 saniye olarak belirtilir. Bu değer, başka bir bağlantının tuttuğu kilidin kalkmasını beklemek için süre tanır, fakat açık transaction'ı sonlandırmaz ve sürekli yazma rekabetini ortadan kaldırmaz. Bu nedenle timeout değerini artırmak, transaction yönetiminin yerine geçmez.
Aşağıdaki örnekte ilk bağlantı yazma işlemini tamamlayıp commit eder, ardından ikinci bağlantı aynı dosyaya yazabilir. Hata durumunda rollback, normal akışın sonunda ise close çağrıları kullanılır.
import sqlite3
db_path = "kilitlenme_demo.db"
conn1 = sqlite3.connect(db_path, timeout=5.0)
conn2 = sqlite3.connect(db_path, timeout=5.0)
try:
conn1.execute("DROP TABLE IF EXISTS notes")
conn1.execute(
"CREATE TABLE notes (id INTEGER PRIMARY KEY, text TEXT)"
)
conn1.commit()
conn1.execute("BEGIN IMMEDIATE")
conn1.execute(
"INSERT INTO notes (text) VALUES (?)",
("İlk bağlantıdan gelen kayıt",)
)
conn1.commit()
conn2.execute(
"INSERT INTO notes (text) VALUES (?)",
("İkinci bağlantıdan gelen kayıt",)
)
conn2.commit()
count = conn2.execute(
"SELECT COUNT(*) FROM notes"
).fetchone()[0]
print(f"Kayıt sayısı: {count}")
except Exception:
conn1.rollback()
conn2.rollback()
raise
finally:
conn1.close()
conn2.close()
Beklenen çıktı: Kayıt sayısı: 2. Bu akışta önemli nokta, ikinci bağlantının yazma işlemine geçmeden önce ilk bağlantının transaction'ını commit ederek kilidi bırakmasıdır.
WAL kipi, aynı makinedeki okuyucu ve yazıcı işlemlerinin daha rahat birlikte çalışmasına yardımcı olabilir. Değişiklikler doğrudan ana dosyanın üzerine yazılmak yerine WAL dosyasına eklendiği için okuyucular yazıcıyı, yazıcılar da okuyucuları bazı durumlarda daha az engeller. Bununla birlikte WAL kipinde de aynı anda yalnızca bir yazıcı ilerleyebilir ve uzun süren transaction'lar kilitlenmeye yol açabilir. WAL, her database is locked senaryosunu ortadan kaldıran genel bir çözüm değildir.
Ağ ve senkronize klasörlerde SQLite kullanırken neye dikkat edilir?
SQLite dosyası ağ klasöründe veya bir senkronizasyon aracının yönettiği klasörde bulunuyorsa dosya kilitlerinin davranışı yerel diske göre daha karmaşık hâle gelebilir. SQLite, dosya sisteminin kilitleme ve yazma işlemlerini doğru uygulamasına güvenir; bazı ağ dosya sistemlerinde bu davranış beklenenden farklı çalışabilir. SQLite belgeleri, ağ dosya sistemlerinde kilitleme sorunları ve veri bütünlüğü riskleri bulunduğuna dikkat çeker.
Teşhis sırasında şu sırayı izleyebilirsin:
- Uygulamayı kapatıp veritabanı dosyasını yalnızca yerel bir klasördeki çalışma kopyasıyla dene.
- Aynı dosyayı kullanan veritabanı görüntüleme araçlarını ve diğer programları kapat.
- Python kodunda açık transaction olup olmadığını ve bağlantıların nerede oluşturulduğunu kontrol et.
- Başarılı işlemlerde
commit(), hatalı işlemlerderollback()çağır. - İşlem tamamlandıktan sonra bütün bağlantıları
close()ile kapatıp yeniden dene. - Sorun yalnızca ağ veya senkronizasyon klasöründe ortaya çıkıyorsa dosya yolu ve dosya sistemi kullanımını ayrıca değerlendir.
WAL kipi kullanılacaksa ağ dosya sistemiyle birlikte düşünülmemelidir. WAL, bağlantıların aynı makinede paylaşılan bellek alanlarına erişebilmesini gerektirdiği için ağ dosya sistemlerinde çalışmaz. Bu nedenle WAL denemesi yerel dosya sistemi üzerinde yapılmalıdır.
Veritabanı dosyasını silmek kilitlenmenin güvenli bir çözümü değildir. Sorunun görünmesini geçici olarak engelleyebilir, ancak kayıtları ve dosyada tutulması gereken bilgileri kaybettirebilir. Önce bağlantıları, transaction durumunu, commit ve rollback akışını ve dosyanın bulunduğu klasörü kontrol etmek gerekir.
Sık Sorulan Sorular
Commit çağrısını unutmak neden başka bir SQLite bağlantısını kilitleyebilir?
commit() çağrısı unutulduğunda yazma transaction'ı açık kalabilir ve bağlantı değişiklikleri tamamlanana kadar kilidi tutabilir. Başka bir bağlantı aynı dosyaya yazmaya çalıştığında bekler, timeout süresi dolarsa database is locked hatası alabilir.
Python'da with bloğu SQLite bağlantısını otomatik olarak kapatır mı?
Python'da with connection: kullanımı transaction'ı başarılı durumda commit eder, hata durumunda rollback yapar, fakat bağlantıyı otomatik olarak kapatmaz. Bağlantıyı ayrıca close() ile kapatmalısın.
Timeout değerini artırmak database is locked hatasını tamamen çözer mi?
Timeout değerini artırmak, kısa süreli bir yazma çakışmasında diğer işlemin bitmesini beklemeye yardımcı olabilir. Açık bırakılan transaction, kapatılmayan bağlantı veya sürekli yazma rekabeti devam ediyorsa hata yeniden oluşabilir.
WAL kipi SQLite kilitlenme hatasını ortadan kaldırır mı?
WAL kipi okuyucu ve yazıcıların birlikte çalışmasını kolaylaştırabilir, ancak aynı anda yalnızca bir yazıcı işlem yapabilir. Uzun transaction'lar, başka süreçlerin tuttuğu kilitler ve uygun olmayan dosya sistemleri WAL kipinde de sorun oluşturabilir.
SQLite veritabanı dosyasını silmek güvenli bir çözüm müdür?
SQLite veritabanı dosyasını silmek güvenli bir çözüm değildir; kilitlenme sorununu gizlerken kayıt kaybına yol açabilir. Önce açık bağlantıları kapatmak, transaction'ları tamamlamak ve dosyanın bulunduğu ortamı kontrol etmek gerekir.
database is locked hatasında kalıcı çözüm, yalnızca bekleme süresini artırmak yerine transaction süresini kısaltmak, commit ve rollback akışını düzenlemek, bağlantıları kapatmak ve dosyanın güvenilir bir yerel ortamda çalıştığını doğrulamaktır.