Frage

Ausführen eine Schienen-Website jetzt SQLite3 verwenden.

Etwa alle 500 Anfragen oder so, bekomme ich ein

Active :: StatementInvalid (SQLite3 :: BusyException: Datenbank ist gesperrt, ...

Was ist der Weg, dies zu beheben, die zu meinem Code minimal-invasiv wären?

Ich verwende SQLLite im Moment, da Sie die DB in der Quellcodeverwaltung speichern kann, die natürlich macht das Sichern und Sie können Änderungen schieben sehr schnell. Allerdings ist es offensichtlich nicht wirklich für den gleichzeitigen Zugriff einzurichten. Ich werde wandern über zu MySQL morgen früh.

War es hilfreich?

Lösung

In der Standardeinstellung SQLite kehrt sofort mit einem blockierten, beschäftigt Fehler, wenn die Datenbank belegt ist und gesperrt. Sie können darum bitten, zu warten und für eine Weile immer wieder versuchen, bevor er aufgibt. Diese in der Regel behebt das Problem, es sei denn, Sie 1000s Threads haben Sie Ihre db zugreifen, wenn ich zustimmen SQLite unangemessen wäre.

    // set SQLite to wait and retry for up to 100ms if database locked
    sqlite3_busy_timeout( db, 100 );

Andere Tipps

Sie haben erwähnt, dass dies eine Rails-Website. Rails können Sie die SQLite Wiederholungs Timeout in Ihrem database.yml Konfigurationsdatei setzen:

production:
  adapter: sqlite3
  database: db/mysite_prod.sqlite3
  timeout: 10000

Der Timeout-Wert wird in Millisekunden angegeben. Eine Erhöhung auf 10 oder 15 Sekunden sollte die Anzahl der BusyExceptions verringern Sie in Ihrem Protokoll.

Das ist nur eine vorübergehende Lösung, though. Wenn Ihre Website echte Gleichzeitigkeit muss dann werden Sie auf eine andere DB-Engine migrieren müssen.

All diese Dinge sind wahr, aber es ist nicht die Frage beantworten, was wahrscheinlich ist: warum tut meine Rails-Anwendung gelegentlich einen SQLite3 :: BusyException in der Produktion erhöhen

@Shalmanese: Was ist die Produktionsumgebung Hosting-wie? Ist es auf einem gemeinsamen Host? Ist das Verzeichnis, das die SQLite-Datenbank auf einer NFS-Freigabe enthält? (Wahrscheinlich auf einem gemeinsamen Host).

Dieses Problem hat wahrscheinlich mit den Phänomenen der Dateisperren w / NFS-Shares und SQLite Mangels an Parallelität zu tun.

Nur für das Protokoll. In einer Anwendung mit Rails 2.3.8 fanden wir heraus, dass Rails ignoriert die "Timeout" Option Rifkin Habsburg vorgeschlagen.

Nach einiger mehr Untersuchung fanden wir eine möglicherweise damit verbundene Fehler in Rails Entwickler: http: //dev.rubyonrails. org / ticket / 8811 . Und nach einiger mehr Untersuchung fanden wir die Lösung (getestet mit Rails 2.3.8):

bearbeiten Active Datei: Activerecord-2.3.8 / lib / active_record / connection_adapters / sqlite_adapter.rb

Ersetzen Sie diese:

  def begin_db_transaction #:nodoc:
    catch_schema_changes { @connection.transaction }
  end

mit

  def begin_db_transaction #:nodoc:
    catch_schema_changes { @connection.transaction(:immediate) }
  end

Und das ist alles! Wir haben noch keinen Leistungsabfall bemerkt und jetzt die App unterstützt viele mehr Petitionen, ohne zu brechen (er wartet auf das Timeout). SQLite ist schön!

bundle exec rake db:reset

Es ist für mich gearbeitet wird die anstehende Migration zurückgesetzt und zeigen.

Quelle: diesen Link

- Open the database
db = sqlite3.open("filename")

-- Ten attempts are made to proceed, if the database is locked
function my_busy_handler(attempts_made)
  if attempts_made < 10 then
    return true
  else
    return false
  end
end

-- Set the new busy handler
db:set_busy_handler(my_busy_handler)

-- Use the database
db:exec(...)

SQLite können andere Prozesse erlauben zu warten, bis der aktuelle beendet.

Ich benutze diese Linie zu verbinden, wenn ich weiß, dass ich mehrere Prozesse haben versucht, die Sqlite DB zuzugreifen:

conn = sqlite3.connect ( 'Dateiname', isolation_level = 'exklusiv' )

Nach dem Python SQLite Dokumentation:

  

können Sie steuern, welche Art von BEGIN   Aussagen implizit pysqlite   ausführt (oder gar keine) über das   isolation_level Parameter auf die   connect () aufrufen oder über das   isolation_level Eigentum   Verbindungen.

Ich hatte ein ähnliches Problem mit rake db: migrieren. Problem war, dass das Arbeitsverzeichnis auf einer SMB-Freigabe war. Ich reparierte sie durch Kopieren Sie den Ordner zu meinem lokalen Rechner.

Wenn Sie dieses Problem aber die Timeout-Erhöhung nicht alles ändern , Sie mit Transaktionen anderes Concurrency Probleme haben könnten, ist es hier im Überblick:

  1. Starten Sie eine Transaktion (erwirbt ein shared lock)
  2. Lesen Sie hier einige Daten von DB (wir noch mit dem SHARED Sperre sind)
  3. Inzwischen beginnt ein anderer Prozess eine Transaktion und Daten schreiben (Erwerb der RESERVIERTEN Sperre).
  4. Dann versuchen Sie zu schreiben, was Sie jetzt fordern die RESERVIERTEN Sperre
  5. versuchen
  6. SQLite wirft die SQLITE_BUSY Ausnahme sofort (UNABHÄNGIG Ihrer Timeout), weil Ihre vorherigen liest nicht mehr durch die Zeit genau sein kann sie die RESERVIERTEN Sperre erhalten.

Eine Möglichkeit, dies zu beheben, ist den active_record SQLite-Adapter Patch ein RESERVIERT Schloss direkt am Anfang der Transaktion zu erwerben, indem die :immediate Option für den Fahrer Polsterung. Dies verringert die Leistung ein wenig, aber zumindest alle Transaktionen Ihre Timeout ehren und tritt eine nach der anderen. Hier ist, wie dies mit prepend zu tun (Rubin 2.0+) setzte diese in einem Initialisierer:

module SqliteTransactionFix
  def begin_db_transaction
    log('begin immediate transaction', nil) { @connection.transaction(:immediate) }
  end
end

module ActiveRecord
  module ConnectionAdapters
    class SQLiteAdapter < AbstractAdapter
      prepend SqliteTransactionFix
    end
  end
end

Lesen Sie mehr hier: https://rails.lighthouseapp.com/projects/8994/tickets/5941-sqlite3busyexceptions-are-raised-immediately-in-some-cases-despite-setting-sqlite3_busy_timeout

Die meisten Antworten sind für Rails statt roh Rubin und OPs Frage ist für Schienen, was in Ordnung ist. :)

Also ich will nur diese Lösung verlassen hier sollte jeder rohe Rubin Benutzer dieses Problem haben, und nicht eine yml Konfiguration.

Nach der Verbindung Instancing, können Sie es wie folgt festgelegt:

db = SQLite3::Database.new "#{path_to_your_db}/your_file.db"
db.busy_timeout=(15000) # in ms, meaning it will retry for 15 seconds before it raises an exception.
#This can be any number you want. Default value is 0.

Was Tabelle zugegriffen wird, wenn die Sperre angetroffen wird?

Haben Sie lange laufenden Transaktionen haben?

Können Sie herausfinden, welche Anträge wurden noch bearbeitet, wenn die Sperre aufgetreten?

Argh - der Fluch meines Lebens in der letzten Woche. Sqlite3 sperrt die db-Datei, wenn jeder Prozess schreibt in die Datenbank. IE jede UPDATE / INSERT Typ Abfrage (auch Zählung wählen (*) aus irgendeinem Grund). Allerdings behandelt es mehrere liest nur in Ordnung.

Also, ich habe endlich frustriert genug, um meine eigenen Thread Sperrcode um die Datenbank Anrufe zu schreiben. Durch die Sicherstellung, dass die Anwendung nur einen Thread Schreiben in die Datenbank an einem beliebigen Stelle haben kann, konnte ich 1000 von Threads skalieren.

Und ja, seine langsam wie die Hölle. Aber es ist auch schnell genug und richtig , die eine schöne Eigenschaft zu haben.

fand ich einen toten Punkt auf sqlite3 Ruby-Erweiterung und beheben hier: habe mit ihm eine gehen und sehen, ob das ur Problem behebt.

    https://github.com/dxj19831029/sqlite3-ruby

Ich öffnete eine Pull-Anforderung, keine Antwort von ihnen mehr.

Wie auch immer, wird ein Teil beschäftigt Ausnahme erwartet wie in sqlite3 selbst beschrieben.

Beachten Sie mit dieser Bedingung: SQLite beschäftigt

    The presence of a busy handler does not guarantee that it will be invoked when there is 
    lock contention. If SQLite determines that invoking the busy handler could result in a 
    deadlock, it will go ahead and return SQLITE_BUSY or SQLITE_IOERR_BLOCKED instead of 
    invoking the busy handler. Consider a scenario where one process is holding a read lock 
    that it is trying to promote to a reserved lock and a second process is holding a reserved 
    lock that it is trying to promote to an exclusive lock. The first process cannot proceed 
    because it is blocked by the second and the second process cannot proceed because it is 
    blocked by the first. If both processes invoke the busy handlers, neither will make any 
    progress. Therefore, SQLite returns SQLITE_BUSY for the first process, hoping that this 
    will induce the first process to release its read lock and allow the second process to 
    proceed.

Wenn Sie diese Bedingung zu erfüllen, ist Timeout nicht mehr gültig. Um dies zu vermeiden, setzen Sie nicht wählen innen beginnen / begehen. oder verwenden Sie exklusive Sperre für begin / commit.

Hoffe, das hilft. :)

ist dies oft ein aufeinanderfolgende Fehler mehrerer Prozesse auf die gleiche Datenbank zugreifen, das heißt, wenn die „erlaubt nur eine Instanz“ Flagge wurde in RubyMine nicht gesetzt

Versuchen Sie, die folgenden ausgeführt wird, kann es helfen:

ActiveRecord::Base.connection.execute("BEGIN TRANSACTION; END;") 

Von: Rubin: SQLite3 :: BusyException: Datenbank ist gesperrt

Dieses aufklären kann das jede Transaktion das System hält

Ich glaube, das passiert, wenn ein Transaktionszeit aus. Sie sollten wirklich eine „echte“ Datenbank verwenden. So etwas Sprühregen oder MySQL. Gibt es Gründe, warum Sie es vorziehen, SQLite über die zwei vorherigen Optionen?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top