سؤال

sqlite> DELETE FROM mails WHERE (`id` = 71);
SQL error: database is locked

كيف يمكنني فتح قاعدة البيانات حتى يعمل هذا؟

هل كانت مفيدة؟

المحلول

في نظام التشغيل Windows يمكنك تجربة هذا البرنامج http://www.nirsoft.net/utils/opened_files_view.html لمعرفة العملية يتم التعامل مع ملف ديسيبل.حاول إغلاق هذا البرنامج لفتح قاعدة البيانات

في Linux وmacOS، يمكنك القيام بشيء مماثل، على سبيل المثال، إذا كان ملفك المقفل هو Development.db:

$ تطوير المصهر.db

سيُظهر هذا الأمر العملية التي تقوم بقفل الملف:

> تطوير ديسيبل:5430

فقط قم بقتل العملية...

قتل -9 5430

...وسيتم فتح قاعدة البيانات الخاصة بك.

نصائح أخرى

لقد تسببت في قفل قاعدة بيانات sqlite الخاصة بي عن طريق تعطل أحد التطبيقات أثناء الكتابة.هنا كيف أصلحته:

echo ".dump" | sqlite old.db | sqlite new.db

مأخوذ من: http://random.kakaopor.hu/how-to-repair-an-sqlite-database

لم تعد صفحة DatabaseIsLocked المدرجة أدناه متاحة.تصف صفحة تأمين الملفات والتزامن التغييرات المتعلقة بقفل الملفات المقدمة في الإصدار 3 وقد تكون مفيدة للقراء في المستقبل. https://www.sqlite.org/lockingv3.html

ويكي سكليتي DatabaseIsLocked تقدم الصفحة شرحًا جيدًا لرسالة الخطأ هذه.وينص جزئيًا على أن مصدر الخلاف داخلي (للعملية التي ينبعث منها الخطأ).

ما لا تشرحه هذه الصفحة هو كيف يقرر SQLite أن شيئًا ما في عمليتك يحتوي على قفل وما هي الشروط التي قد تؤدي إلى نتيجة إيجابية خاطئة.

يبدو حذف ملف -journal فكرة رهيبة.إنه موجود للسماح لـ sqlite باستعادة قاعدة البيانات إلى حالة ثابتة بعد التعطل.إذا قمت بحذفها بينما تكون قاعدة البيانات في حالة غير متناسقة، فستتبقى لديك قاعدة بيانات تالفة.نقلا عن صفحة من موقع سكليتي:

في حالة حدوث عطل أو فقدان للطاقة وترك سجل نشط على القرص، فمن الضروري أن يظل ملف قاعدة البيانات الأصلي والمجلة الساخنة على القرص بأسمائهما الأصلية حتى يتم فتح ملف قاعدة البيانات بواسطة عملية SQLite أخرى وإعادته مرة أخرى .[...]

نشك في أن وضع الفشل الشائع لاسترداد SQLite يحدث على النحو التالي:يحدث انقطاع في الطاقة.بعد استعادة الطاقة، يبدأ المستخدم أو مسؤول النظام حسن النية في البحث على القرص بحثًا عن أي ضرر.يرون ملف قاعدة البيانات الخاص بهم المسمى "important.data".ربما يكون هذا الملف مألوفًا لهم.ولكن بعد التعطل، هناك أيضًا مجلة ساخنة تسمى "important.data-journal".يقوم المستخدم بعد ذلك بحذف المجلة الساخنة، معتقدًا أنه يساعد في تنظيف النظام.لا نعرف أي طريقة لمنع ذلك بخلاف تعليم المستخدم.

من المفترض أن تتم عملية الاستعادة تلقائيًا في المرة التالية التي يتم فيها فتح قاعدة البيانات، ولكنها ستفشل إذا لم تتمكن العملية من قفل قاعدة البيانات.وكما قال آخرون، أحد الأسباب المحتملة لذلك هو أن هناك عملية أخرى مفتوحة حاليًا.الاحتمال الآخر هو قفل NFS القديم، إذا كانت قاعدة البيانات موجودة على وحدة تخزين NFS.في هذه الحالة، الحل البديل هو استبدال ملف قاعدة البيانات بنسخة حديثة غير مقفلة على خادم NFS (mvdata.db original.db;cp original.db قاعدة البيانات.db).لاحظ أن الأسئلة الشائعة الخاصة بـ sqlite توصي بالحذر فيما يتعلق بالوصول المتزامن إلى قواعد البيانات الموجودة على وحدات تخزين NFS، بسبب التطبيقات الخاطئة لقفل ملفات NFS.

لا أستطيع أن أشرح لماذا حذف ملف -journal سيسمح لك بقفل قاعدة بيانات لم تتمكن من ذلك من قبل.هل هذا قابل للتكرار؟

بالمناسبة، وجود ملف -journal لا يعني بالضرورة أن هناك عطلًا أو أن هناك تغييرات يجب التراجع عنها.يحتوي Sqlite على عدد قليل من أوضاع دفتر اليومية المختلفة، وفي وضعي PERSIST أو TRUNCATE، فإنه يترك ملف -journal في مكانه دائمًا، ويغير المحتويات للإشارة إلى ما إذا كانت هناك معاملات جزئية يجب استرجاعها أم لا.

إذا كنت تريد إزالة الخطأ "قاعدة البيانات مقفلة"، فاتبع الخطوات التالية:

  1. انسخ ملف قاعدة البيانات إلى موقع آخر.
  2. استبدال قاعدة البيانات بقاعدة البيانات المنسوخة.سيؤدي هذا إلى إلغاء الإشارة إلى جميع العمليات التي كانت تصل إلى ملف قاعدة البيانات الخاصة بك.

إذا كانت العملية تحتوي على قفل على قاعدة بيانات SQLite وتعطلت، فستظل قاعدة البيانات مقفلة بشكل دائم.تلك هي المشكلة.لا يعني ذلك أن بعض العمليات الأخرى بها قفل.

ملفات SQLite db هي مجرد ملفات، لذا فإن الخطوة الأولى هي التأكد من أنها ليست للقراءة فقط.الشيء الآخر الذي يجب عليك فعله هو التأكد من عدم وجود نوع من عارض واجهة المستخدم الرسومية SQLite DB مع فتح قاعدة البيانات.يمكن أن يكون لديك قاعدة بيانات مفتوحة في غلاف آخر، أو قد يكون لدى التعليمات البرمجية الخاصة بك قاعدة بيانات مفتوحة.عادةً ما ترى هذا إذا كان هناك موضوع مختلف أو تطبيق مثل SQLite Database Browser لديه قاعدة بيانات مفتوحة للكتابة.

لقد واجهت هذه المشكلة الآن، باستخدام قاعدة بيانات SQLite على خادم بعيد، ومخزنة على حامل NFS.لم يتمكن SQLite من الحصول على القفل بعد تعطل جلسة الصدفة البعيدة التي استخدمتها أثناء فتح قاعدة البيانات.

وصفات الاسترداد المقترحة أعلاه لم تنجح بالنسبة لي (بما في ذلك فكرة نقل قاعدة البيانات أولاً ثم نسخها مرة أخرى).ولكن بعد نسخها إلى نظام غير NFS، أصبحت قاعدة البيانات قابلة للاستخدام ولا يبدو أن أي بيانات قد فقدت.

لقد حدث القفل بسبب تعطل النظام وليس بسبب عملية تعليق.لحل هذه المشكلة، قمت ببساطة بإعادة تسمية الملف ثم نسخه مرة أخرى إلى اسمه وموقعه الأصليين.

باستخدام قذيفة لينكس من شأنها أن تكون ...

mv mydata.db temp.db
cp temp.db mydata.db

أضفت "Pooling=true"لسلسلة الاتصال وعملت.

لقد وجدت توثيق الحالات المختلفة للقفل في SQLite ستكون مفيدة جدًا.مايكل، إذا كان بإمكانك إجراء عمليات القراءة ولكن لا يمكنك إجراء عمليات الكتابة إلى قاعدة البيانات، فهذا يعني أن العملية قد حصلت على قفل محجوز في قاعدة البيانات الخاصة بك ولكنها لم تنفذ عملية الكتابة بعد.إذا كنت تستخدم SQLite3، فهناك قفل جديد يسمى PENDING حيث لا يُسمح لمزيد من العمليات بالاتصال ولكن الاتصالات الموجودة لا تزال قادرة على إجراء عمليات القراءة، لذلك إذا كانت هذه هي المشكلة، فيجب عليك النظر في ذلك بدلاً من ذلك.

يمكن ظهور هذا الخطأ إذا كان الملف موجودًا في مجلد بعيد، مثل مجلد مشترك.لقد قمت بتغيير قاعدة البيانات إلى دليل محلي وعملت بشكل مثالي.

لدي مثل هذه المشكلة داخل التطبيق، حيث يمكن الوصول إلى SQLite من خلال اتصالين - أحدهما للقراءة فقط والثاني للكتابة والقراءة.يبدو أن اتصال القراءة فقط هذا قد منع الكتابة من الاتصال الثاني.أخيرًا، اتضح أنه يلزم الانتهاء من البيانات المعدة أو على الأقل إعادة ضبطها فورًا بعد الاستخدام.حتى يتم فتح البيان المعد، فقد تسبب في حظر قاعدة البيانات من الكتابة.

لا تنسى الاتصال:

sqlite_reset(xxx);

أو

sqlite_finalize(xxx);

قد تستغرق بعض الوظائف، مثل INDEX'ing، وقتًا طويلاً جدًا - وتقوم بتأمين قاعدة البيانات بأكملها أثناء تشغيلها.في مثل هذه الحالات، قد لا يستخدم حتى ملف دفتر اليومية!

لذا فإن الطريقة الأفضل/الوحيدة للتحقق مما إذا كانت قاعدة البيانات الخاصة بك مقفلة لأن هناك عملية تكتب إليها بشكل نشط (وبالتالي يجب عليك تركها بمفردها حتى اكتمال عملها) هي استخدام الملف md5 (أو md5sum في بعض الأنظمة) مرتين .إذا حصلت على مجموع اختباري مختلف، فستتم كتابة قاعدة البيانات، وأنت حقًا لا تريد حقًا إنهاء -9 هذه العملية لأنه يمكن أن ينتهي بك الأمر بسهولة بجدول/قاعدة بيانات تالفة إذا فعلت ذلك.

سأكرر، لأنه من المهم - الحل ليس العثور على برنامج القفل وإيقافه - بل هو العثور على ما إذا كانت قاعدة البيانات تحتوي على قفل للكتابة لسبب وجيه، والبدء من هناك.في بعض الأحيان يكون الحل الصحيح هو مجرد استراحة لتناول القهوة.

الطريقة الوحيدة لإنشاء هذا الموقف المغلق ولكن غير المكتوب هي تشغيل البرنامج BEGIN EXCLUSIVE, ، لأنه أراد إجراء بعض التعديلات على الجدول أو شيء من هذا القبيل، فلا يرسل أبدًا لأي سبب من الأسباب END عقب ذلك مباشرة، والعملية لا تنتهي أبدًا.من المستبعد جدًا استيفاء الشروط الثلاثة في أي تعليمات برمجية مكتوبة بشكل صحيح، وعلى هذا النحو، في 99 مرة من أصل 100 عندما يريد شخص ما إنهاء -9 عملية القفل الخاصة به، فإن عملية القفل هي في الواقع قفل قاعدة البيانات الخاصة بك لسبب وجيه.لا يقوم المبرمجون عادةً بإضافة ملف BEGIN EXCLUSIVE شرط ما لم يكونوا بحاجة لذلك حقًا، لأنه يمنع التزامن ويزيد من شكاوى المستخدمين.يضيفها SQLite نفسه فقط عندما يحتاج إلى ذلك (مثلًا عند الفهرسة).

أخيرًا، الحالة "مقفل" غير موجودة داخل الملف كما ذكرت العديد من الإجابات - فهي موجودة في نواة نظام التشغيل.العملية التي جرت BEGIN EXCLUSIVE طلب من نظام التشغيل وضع قفل على الملف.حتى لو تعطلت العملية الحصرية الخاصة بك، فسيكون نظام التشغيل لديك قادرًا على معرفة ما إذا كان يجب عليه الحفاظ على قفل الملف أم لا!!ليس من الممكن أن ينتهي الأمر بقاعدة بيانات مقفلة ولكن لا توجد عملية تقوم بقفلها بشكل فعال !!عندما يتعلق الأمر بمعرفة العملية التي تقفل الملف، فمن الأفضل عادةً استخدام lsof بدلاً من fuser (وهذا توضيح جيد للسبب: https://unix.stackexchange.com/questions/94316/fuser-vs-lsof-to-check-files-in-use).وبدلاً من ذلك، إذا كان لديك DTrace (OSX)، يمكنك استخدام iosnoop على الملف.

لقد حدث لي شيء مماثل - كان تطبيق الويب الخاص بي قادرًا على القراءة من قاعدة البيانات، لكنه لم يتمكن من إجراء أي إدراجات أو تحديثات.أدت إعادة تشغيل Apache إلى حل المشكلة مؤقتًا على الأقل.

ومع ذلك، سيكون من الجيد أن نكون قادرين على تعقب السبب الجذري.

lsof ساعدني الأمر الموجود في بيئة Linux الخاصة بي في معرفة أن هناك عملية معلقة مع إبقاء الملف مفتوحًا.
قتلت العملية وتم حل المشكلة.

هذا الرابط يحل المشكلة: عندما يعطي Sqlite:خطأ في قفل قاعدة البياناتلقد حلت مشكلتي قد تكون مفيدة لك.

ويمكنك استخدام بدء المعاملة وإنهاء المعاملة حتى لا يتم قفل قاعدة البيانات في المستقبل.

يجب أن تكون مشكلة داخلية لقاعدة البيانات ...
بالنسبة لي فقد تجلى ذلك بعد محاولة تصفح قاعدة البيانات باستخدام "SQLite manager" ...
لذا ، إذا لم تتمكن من العثور على عملية أخرى ، قم بتوصيل قاعدة البيانات ولا يمكنك إصلاحها ، فقط جرب هذا الحل الجذري:

  1. توفير تصدير الجداول الخاصة بك (يمكنك استخدام "SQLite manager" على Firefox)
  2. إذا غيرت عملية الترحيل نظام قاعدة بياناتك، فاحذف آخر عملية ترحيل فاشلة
  3. أعد تسمية ملف "database.sqlite".
  4. قم بتنفيذ "rake db:migrate" لإنشاء قاعدة بيانات عمل جديدة
  5. توفير لإعطاء الأذونات الصحيحة لقاعدة البيانات لاستيراد الجدول
  6. استيراد الجداول التي تم نسخها احتياطيًا
  7. اكتب الهجرة الجديدة
  8. نفذها ب"rake db:migrate"

لقد واجهت نفس المشكلة على نظام التشغيل Mac OS X 10.5.7 أثناء تشغيل البرامج النصية لـ Python من جلسة طرفية.على الرغم من أنني أوقفت البرامج النصية وكانت النافذة الطرفية موجودة في موجه الأوامر، إلا أنها ستعطي هذا الخطأ في المرة التالية التي يتم فيها تشغيلها.كان الحل هو إغلاق النافذة الطرفية ثم فتحها مرة أخرى.لا معنى له بالنسبة لي، لكنه نجح.

لقد كان لي نفس الخطأ.بعد 5 دقائق من البحث في Google، وجدت أنني لم أغلق إحدى ساحرات الصدفة التي كنت أستخدم قاعدة البيانات.فقط أغلقه وحاول مرة أخرى ;)

كان لي نفس المشكلة.من الواضح أن وظيفة التراجع يبدو أنها تقوم بالكتابة فوق ملف db بالمجلة التي هي نفس ملف db ولكن بدون التغيير الأخير.لقد قمت بتنفيذ هذا في الكود الخاص بي أدناه وهو يعمل بشكل جيد منذ ذلك الحين، بينما في السابق كان الكود الخاص بي يعلق في الحلقة حيث ظلت قاعدة البيانات مغلقة.

أتمنى أن يساعدك هذا

رمز بيثون الخاص بي

##############
#### Defs ####
##############
def conn_exec( connection , cursor , cmd_str ):
    done        = False
    try_count   = 0.0
    while not done:
        try:
            cursor.execute( cmd_str )
            done = True
        except sqlite.IntegrityError:
            # Ignore this error because it means the item already exists in the database
            done = True
        except Exception, error:
            if try_count%60.0 == 0.0:       # print error every minute
                print "\t" , "Error executing command" , cmd_str
                print "Message:" , error

            if try_count%120.0 == 0.0:      # if waited for 2 miutes, roll back
                print "Forcing Unlock"
                connection.rollback()

            time.sleep(0.05)    
            try_count += 0.05


def conn_comit( connection ):
    done        = False
    try_count   = 0.0
    while not done:
        try:
            connection.commit()
            done = True
        except sqlite.IntegrityError:
            # Ignore this error because it means the item already exists in the database
            done = True
        except Exception, error:
            if try_count%60.0 == 0.0:       # print error every minute
                print "\t" , "Error executing command" , cmd_str
                print "Message:" , error

            if try_count%120.0 == 0.0:      # if waited for 2 miutes, roll back
                print "Forcing Unlock"
                connection.rollback()

            time.sleep(0.05)    
            try_count += 0.05       




##################
#### Run Code ####
##################
connection = sqlite.connect( db_path )
cursor = connection.cursor()
# Create tables if database does not exist
conn_exec( connection , cursor , '''CREATE TABLE IF NOT EXISTS fix (path TEXT PRIMARY KEY);''')
conn_exec( connection , cursor , '''CREATE TABLE IF NOT EXISTS tx (path TEXT PRIMARY KEY);''')
conn_exec( connection , cursor , '''CREATE TABLE IF NOT EXISTS completed (fix DATE, tx DATE);''')
conn_comit( connection )

أحد الأسباب الشائعة للحصول على هذا الاستثناء هو عندما تحاول إجراء عملية كتابة مع الاحتفاظ بالموارد اللازمة لعملية القراءة.على سبيل المثال، إذا قمت بالتحديد من جدول، ثم حاولت تحديث شيء قمت بتحديده دون إغلاق ResultSet أولاً.

قبل النزول إلى خيار إعادة التشغيل، من المفيد معرفة ما إذا كان بإمكانك العثور على مستخدم قاعدة بيانات sqlite.

في نظام Linux، يمكن للمرء أن يستخدم fuser تحقيقا لهذه الغاية:

$ fuser database.db

$ fuser database.db-journal

في حالتي تلقيت الرد التالي:

philip    3556  4700  0 10:24 pts/3    00:00:01 /usr/bin/python manage.py shell

مما أظهر أن لدي برنامج Python آخر مع pid 3556 (manage.py) باستخدام قاعدة البيانات.

سؤال قديم، وإجاباته كثيرة، إليك الخطوات التي اتبعتها مؤخرًا أثناء قراءة الإجابات أعلاه، لكن في حالتي كانت المشكلة بسبب مشاركة موارد cifs.لم يتم الإبلاغ عن هذه الحالة سابقًا، لذا آمل أن يساعد شخص ما.

  • تحقق من عدم ترك أي اتصالات مفتوحة في كود جافا الخاص بك.
  • تحقق من عدم وجود عمليات أخرى تستخدم ملف SQLite db الخاص بك مع lsof.
  • تحقق من أن مالك المستخدم لعملية jvm قيد التشغيل لديه أذونات r/w على الملف.
  • حاول فرض وضع القفل على الاتصال الذي يتم فتحه

    final SQLiteConfig config = new SQLiteConfig();
    
    config.setReadOnly(false);
    
    config.setLockingMode(LockingMode.NORMAL);
    
    connection = DriverManager.getConnection(url, config.toProperties());
    

إذا كنت تستخدم ملف SQLite db الخاص بك عبر مجلد NFS مشترك، فتحقق من ذلك هذه النقطة من الأسئلة الشائعة حول SQLite، وراجع خيارات تكوين التركيب للتأكد من تجنب الأقفال، كما هو موضح هنا:

//myserver /mymount cifs username=*****,password=*****,iocharset=utf8,sec=ntlm,file,nolock,file_mode=0700,dir_mode=0700,uid=0500,gid=0500 0 0

لقد حصلت على هذا الخطأ في سيناريو مختلف قليلاً عن السيناريو الموضح هنا.

ترتكز قاعدة بيانات SQLite على نظام ملفات NFS مشترك بين 3 خوادم.على خادمين، تمكنت من تشغيل الاستعلامات على قاعدة البيانات بنجاح، وعلى الخادم الثالث اعتقدت أنني تلقيت رسالة "قاعدة البيانات مقفلة".

الشيء في هذا الجهاز الثالث هو أنه لم يكن لديه مساحة متبقية /var.في كل مرة حاولت فيها تشغيل استعلام في أي قاعدة بيانات SQLite موجودة في نظام الملفات هذا، تلقيت رسالة "قاعدة البيانات مقفلة" وأيضًا هذا الخطأ في السجلات:

8 أغسطس 10:33:38 نواة server01:مغلق:لا يمكن مراقبة 172.22.84.87

وهذه أيضاً:

8 أغسطس 10:33:38 server01 rpc.statd[7430]:فشل في الإدراج:كتابة /var/lib/nfs/statd/sm/other.server.name.com:لم يتبق أي مساحة على الجهاز 8 أغسطس 10:33:38 Server01 rpc.statd [7430]:STAT_FAIL إلى server01 لـ SM_MON لـ 172.22.84.87

وبعد معالجة الوضع الفضائي عاد كل شيء إلى طبيعته.

من تعليقاتك السابقة قلت أن ملف -journal كان موجودًا.

قد يعني هذا أنك قمت بفتح معاملة (حصرية؟) ولم تلتزم بالبيانات بعد.هل ترك برنامجك أو أي عملية أخرى المجلة خلفك؟؟

ستؤدي إعادة تشغيل عملية sqlite إلى إلقاء نظرة على ملف دفتر اليومية وتنظيف أي إجراءات غير ملتزم بها وإزالة ملف -journal.

كما قال Seun Osewa، في بعض الأحيان توجد عملية زومبي في الجهاز مع الحصول على قفل، حتى لو كنت لا تعتقد أن ذلك ممكنًا.يتم تشغيل البرنامج النصي الخاص بك، ويتعطل، وتعود إلى الموجه، ولكن هناك عملية زومبي نشأت في مكان ما عن طريق استدعاء مكتبة، وهذه العملية بها قفل.

قد ينجح إغلاق المحطة التي كنت فيها (على OSX).سوف تعمل إعادة التشغيل.يمكنك البحث عن عمليات "python" (على سبيل المثال) التي لا تفعل شيئًا وتقتلها.

يمكنك تجربة هذا: .timeout 100 لتعيين المهلة.لا أعرف ما يحدث في سطر الأوامر ولكن في C# .Net عندما أفعل هذا: "UPDATE table-name SET column-name = value;" أحصل على قاعدة البيانات مقفلة ولكن هذا "UPDATE table-name SET column-name = value" سارت الأمور على ما يرام.

يبدو أنه عند إضافة ;، سيبحث sqlite عن أمر آخر.

لقد حصلت على هذا الخطأ عند استخدام دلفي مع مكونات LiteDAC.اتضح أن ذلك حدث فقط أثناء تشغيل تطبيقي من Delphi IDE إذا تم تعيين الخاصية "متصل" على "صحيح" لمكون اتصال SQLite (في هذه الحالة TLiteConnection).

كنت أواجه أخطاء "قاعدة البيانات مقفلة" في تطبيق متعدد الخيوط أيضًا، والذي يبدو أنه خطأ SQLITE_BUSY رمز النتيجة، وأنا حلها مع الإعداد sqlite3_busy_timeout إلى شيء طويل بشكل مناسب مثل 30000.

(في ملاحظة جانبية، كم هو غريب أنه في سؤال عمره 7 سنوات لم يكتشف أحد ذلك بالفعل!SQLite هو حقًا مشروع غريب ومذهل...)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top