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.
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.
Ich würde gerne sehen, wie man eine große Datenmenge im Hauptthema löscht.
dependent :: destroy
Assoziation Modell: JaUnter 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.
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.
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.
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
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