[RUBY] So löschen Sie große Datenmengen in Rails und Bedenken

Einführung

Hallo zusammen! Ich bin @hiroki_tanaka, der Produzent von Mayu Sakuma.

Ich bin an der Wartung von Rails-Anwendungen beteiligt, und neulich stellte sich heraus, dass die Produktionsumgebung viele unerwünschte Daten enthielt. Aus diesem Grund habe ich mir überlegt, wie ich eine große Datenmenge in Rails löschen kann. Deshalb habe ich zusammengefasst, was ich untersucht habe.

Unterschied zwischen Zerstören und Löschen in Rails

Erstens verfügt Rails über zwei Methoden zum Löschen von Daten: Zerstören und Löschen. Ich möchte die Unterschiede zwischen ihnen kurz zusammenfassen.

destroy/destroy! Löscht einen über ActiveRecord angegebenen Datensatz. Die Rückrufmethoden (wie "before_destroy" und "after_destroy") und die Validierung funktionieren über ActiveRecord. Wenn es ein verwandtes Modell gibt, für das "abhängige :: Zerstörung" in dem zu löschenden Modell festgelegt ist, wird auch das festgelegte Modell gelöscht. Wenn destroy zur Laufzeit auf einen Fehler stößt und nicht gelöscht werden kann, wird nur false und keine Ausnahme zurückgegeben. Im Gegensatz dazu zerstören! Gibt eine Ausnahme zurück. Wenn Sie den Löschfehler explizit abfangen möchten, ist es daher besser, destroy! Zu verwenden.

delete Geben Sie SQL (DELETE-Anweisung) direkt an die Datenbank aus, ohne ActiveRecord zu durchlaufen, um den Zieldatensatz zu löschen. Die Rückrufmethode und die Validierung funktionieren nicht, da sie ActiveRecord nicht durchlaufen. Auch wenn in dem zu löschenden Modell ein Modell mit "Dependent :: Destroy" verknüpft ist, wird es nicht gelöscht. Das Verhalten zum Zeitpunkt des Ausfalls ist dasselbe wie "Zerstören". Es gibt nur "false" und keine Ausnahme zurück. Da delete! In delete nicht vorhanden ist, ist es meiner Meinung nach besser, destroy zu verwenden! Gehorsam, wenn "Ich Daten löschen und einen Fehler zurückgeben möchte, wenn dies fehlschlägt".

destory_all Mit destroy / destroy! Kann nur ein Datensatz gelöscht werden, aber history_all kann mehrere Datensätze angeben und alle angegebenen Datensätze löschen. Wie "destroy" durchläuft "history_all" auch ActiveRecord, sodass die Rückrufmethode und die Validierung "` defined :: destroy "funktionieren. Wie destroy verursacht destory_all zur Laufzeit einen Fehler. Wenn der Löschvorgang in der Mitte fehlschlägt, wird nur false und keine Ausnahme zurückgegeben. Es gibt jedoch keine Methode namens destory_all! Wenn Sie also eine große Datenmenge löschen möchten, diese jedoch fehlschlägt, müssen Sie einen Fehler ordnungsgemäß zurückgeben. (Die Methode wird später beschrieben.)

delete_all Löscht die angegebenen mehreren Datensätze, ohne ActiveRecord zu durchlaufen. Wie beim Löschen funktionieren die Rückrufmethode und die Validierung · defined :: destroy nicht. Wie delete löscht auch delete_all zur Laufzeit einen Fehler. Wenn der Löschvorgang in der Mitte fehlschlägt, wird nur false und keine Ausnahme zurückgegeben. Persönlich glaube ich nicht, dass es viele Situationen gibt, in denen es verwendet wird, aber die Verarbeitung ohne ActiveRecord ist schneller als destroy und story_all. Daher denke ich, dass es in Situationen verwendet werden kann, in denen Sie eine große Datenmenge gleichzeitig löschen möchten, ohne sich um Ausnahmen, Rückrufe und verwandte Elemente kümmern zu müssen.

So löschen Sie eine große Datenmenge

Ich würde gerne sehen, wie man eine große Datenmenge im Hauptthema löscht.

Annahme

Unter den Modellen, die dieses Mal gelöscht werden sollen, gibt es auch Modelle, die mit Rückruf und "abhängiger :: Zerstörung" verbunden sind. Als Anforderung muss auch das zugehörige Modell gelöscht werden, und ich möchte explizit feststellen, ob während der Verarbeitung ein Fehler auftritt. Delete / delete_all / destroy_all ist derzeit nicht verfügbar. Anschließend müssen Sie den Löschvorgang hinter den Kulissen ausführen, in denen die Produktionsanwendung ausgeführt wird. Ich möchte also keine Methode verwenden, die die Datenbank extrem belastet.

Methode (1): Zerstören Sie! Einer nach dem anderen, ohne eine Transaktion zu erstellen

  animals = Animal.where(type: 'dog') #Extraktion der zu löschenden Daten
  animals.each do |animal|
    animal.destroy!
  end

Der einfachste Weg, darüber nachzudenken, besteht darin, Code wie diesen zu haben. Es gibt jedoch zwei Probleme.

――Die Last ist schwer, da die Zerstörung 100.000 Mal ausgeführt wird.

Wenn in der Datenbank Kapazitätsreserven vorhanden sind, die Anwendung eine eindeutige Schließzeit hat und kein Problem vorliegt, auch wenn das Löschen nicht mitten im Fehler zurückgesetzt wird, ist diese Methode in Ordnung, wenn die Datenintegrität kein Problem darstellt. Ich denke.

Methode (2): Zerstören Sie nach einer Transaktion! Einer nach dem anderen

  animals = Animal.where(type: 'dog') #Extraktion der zu löschenden Daten
  ActiveRecord::Base.transaction do
    animals.each do |animal|
      animal.destroy!
    end
  end

Der Löschvorgang von Methode (1) wird auf eine Transaktion gesetzt. Wenn in der Mitte des Löschvorgangs ein Fehler auftritt, indem eine Transaktion ausgeführt wird, werden alle Löschvorgänge zurückgesetzt und das Wiederherstellen wird wirksam. Wenn Sie diese Methode verwenden, können Sie daher alle Daten, die Datenintegrität erfordern, sicher löschen.

Eine Einschränkung ist, dass Sie immer destroy verwenden müssen! Wenn Sie explizit eine Transaktion erstellen. Dies liegt daran, dass destroy keine Ausnahme auslöst und false zurückgibt, selbst wenn ein Fehler auftritt, sodass die Verarbeitung nicht gestoppt wird und die Transaktion nicht beendet werden kann.

Methode ③: Nach einer Transaktion alle 1000 Fälle Daten extrahieren und zerstören! Fügen Sie zwischen den Löschvorgängen einen Ruhezustand von 0,1 Sekunden ein.

  animals = Animal.where(type: 'dog') #Extraktion der zu löschenden Daten
  ActiveRecord::Base.transaction do
    animals.in_batches.each do |delete_target_animals|
      delete_target_animals.map(&:destroy!)
      sleep(0.1)
    end
  end

Die obige Methode ist die diesmal angewandte Methode. Verwenden Sie die Methode ActiveRecord :: Relation # in_batches, um 100.000 Datensätze in 1000 Einheiten zu kombinieren Und zerstöre! Für jeden der Brocken. Wenn der Löschvorgang für jeweils 1000 Elemente abgeschlossen ist, wird der Vorgang für 0,1 Sekunden mit "sleep (0.1)" gestoppt, um die Belastung der Datenbank zu verringern. Da eine große Transaktion außen platziert wird, wird auch dann, wenn während des Löschvorgangs ein Fehler auftritt, alles zurückgesetzt und das Wiederherstellen wird wirksam, sodass es sicher ist.

  animals = Animal.where(type: 'dog') #Extraktion der zu löschenden Daten
  ActiveRecord::Base.transaction do
    animals.in_batches(of: 10000).each do |delete_target_animals|
      delete_target_animals.map(&:destroy!)
      sleep(0.1)
    end
  end

abschließend

Ich denke, es gibt verschiedene Möglichkeiten, große Datenmengen zu löschen, abhängig von Ihren Anforderungen. Wenn Sie die beste Praxis haben, würde ich gerne von Ihnen hören (o._.) O Peco

Recommended Posts

So löschen Sie große Datenmengen in Rails und Bedenken
[Webpacker] Zusammenfassung der Installation von Bootstrap und jQuery in Rails 6.0
Verwendung von JQuery in Rails 6 js.erb
So ändern Sie die maximale und maximale Anzahl von POST-Daten in Spark
So erstellen Sie eine eindeutige Datenkombination in der Schienen-Zwischentabelle
[Rails] So löschen Sie MySQL-Daten aus der Produktionsumgebung, nachdem Sie sie in die Entwicklungsumgebung gestellt haben
[Rails] So definieren Sie Makros in Rspec und standardisieren die Verarbeitung
[Rails] Verschiedene Möglichkeiten zum Löschen von Daten
So installieren Sie jQuery in Rails 6
So installieren Sie Swiper in Rails
[Rails] So erhalten Sie die URL der Übergangsquelle und leiten sie um
Abrufen und Hinzufügen von Daten aus dem Firebase Firestore in Ruby
So implementieren Sie Suchfunktionen in Rails
So ändern Sie den App-Namen in Rails
[Docker] So sichern und wiederherstellen Sie DB-Daten der Rails-Anwendung auf Docker-Compose [MySQL]
So fügen Sie ein Video in Rails ein
Verwendung von MySQL im Rails-Tutorial
[Rails] So konfigurieren Sie das Routing in Ressourcen
So implementieren Sie Ranking-Funktionen in Rails
So löschen Sie Daten mit einem externen Schlüssel
Speicherort der Methodendefinition Zusammenfassung der zu überprüfenden Informationen Wenn im Projekt und in Rails / Gem definiert
So überschreiben Sie Firebase-Daten mit Swift
Verwendung von credentials.yml.enc aus Rails 5.2
[Rails] Ich möchte Daten verschiedener Modelle in einem Formular senden
JDBC Versprechen und Schreibbeispiel
[Bestellmethode] Legen Sie die Reihenfolge der Daten in Rails fest
[Rails] Verwendung von Auswahlfeldern in Ransack
Wie man Rails allgemein ins Japanische übersetzt
So fügen Sie die html.erb-Klasse in Rails bedingt hinzu
So implementieren Sie eine ähnliche Funktion in Rails
So erstellen Sie einfach ein Pulldown mit Rails
So erstellen Sie eine API mit GraphQL und Rails
[Rails] So geben Sie Erfolgs- und Fehlermeldungen aus
Rails "So löschen Sie NO FILE-Migrationsdateien"
[Rails] Verwendung von PostgreSQL in einer Vagrant-Umgebung
So überprüfen Sie Rails-Befehle im Terminal
[Rails] Rangfolge und Paginierung in der Reihenfolge der Likes
So speichern Sie gleichzeitig Daten in einem Modell, das einem verschachtelten Formular zugeordnet ist (Rails 6.0.0)
Zusammenfassung der häufig verwendeten Befehle in Rails und Docker
So löschen Sie alle Daten in einer bestimmten Tabelle
So löschen / aktualisieren Sie das Listenfeld von OneToMany
So stellen Sie die Anzeigezeit in Rails auf japanische Zeit ein
So implementieren Sie die Gastanmeldung in 5 Minuten im Rails-Portfolio
So installieren Sie Docker in der lokalen Umgebung einer vorhandenen Rails-App [Rails 6 / MySQL 8]
Wie schreibe ich Rails
[Java] Arten von Kommentaren und wie man sie schreibt
[Rails, JS] So implementieren Sie die asynchrone Anzeige von Kommentaren
Ändern Sie Datum und Uhrzeit in Rails in japanische Notation
[Rails] Durchsuchen von Spalten verwandter Modelle (Eltern und Kinder) in Ransack
So erstellen Sie einen Daten-URI (base64) in Java