Spring Data JPA - gleichzeitige Bulk-inserts/updates
im moment entwickle ich eine Spring-Boot-Anwendung, die hauptsächlich zieht product review von Daten aus einer message queue (~5 gleichzeitige Verbraucher) und speichert Sie in einer MySQL-DB. Jeder abgeben können, werden eindeutig identifiziert durch seine reviewIdentifier (String), die den primären Schlüssel und gehören zu einem oder mehreren Produkt (z.B. Produkte mit unterschiedlichen Farben). Hier ist ein Auszug der Daten-Modell:
public class ProductPlacement implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "product_placement_id")
private long id;
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="productPlacements")
private Set<CustomerReview> customerReviews;
}
public class CustomerReview implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@Column(name = "customer_review_id")
private String reviewIdentifier;
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(
name = "tb_miner_review_to_product",
joinColumns = @JoinColumn(name = "customer_review_id"),
inverseJoinColumns = @JoinColumn(name = "product_placement_id")
)
private Set<ProductPlacement> productPlacements;
}
Einer Nachricht aus der Warteschlange enthält 1 - 15 Bewertungen und einen productPlacementId. Jetzt will ich eine effiziente Methode zur Speicherung der Bewertungen für das Produkt. Es gibt grundsätzlich zwei Fälle, die berücksichtigt werden müssen, die für jede eingehende Beurteilung:
- Der überprüfung ist nicht in der Datenbank -> einfügen-Kritik mit Verweis auf das Produkt, das in der Nachricht enthaltene
- Die review ist bereits in der Datenbank -> fügen Sie einfach das Produkt Verweis auf die Set-productPlacements der bestehenden abgeben.
Derzeit meine Methode zum speichern der Bewertungen ist nicht optimal. Es sieht wie folgt aus (Spring verwendet Daten JpaRespoitories):
@Override
@Transactional
public void saveAllReviews(List<CustomerReview> customerReviews, long productPlacementId) {
ProductPlacement placement = productPlacementRepository.findOne(productPlacementId);
for(CustomerReview review: customerReviews){
CustomerReview cr = customerReviewRepository.findOne(review.getReviewIdentifier());
if (cr!=null){
cr.getProductPlacements().add(placement);
customerReviewRepository.saveAndFlush(cr);
}
else{
Set<ProductPlacement> productPlacements = new HashSet<>();
productPlacements.add(placement);
review.setProductPlacements(productPlacements);
cr = review;
customerReviewRepository.saveAndFlush(cr);
}
}
}
Fragen:
- Manchmal bekomme ich constraintViolationExceptions wegen Verletzung der unique-Einschränkung auf "reviewIndentifier". Dies ist offensichtlich, weil ich (gleichzeitig) schauen, ob das review ist bereits vorhanden und als insert-oder update es. Wie kann ich das vermeiden?
- Ist es besser, zu verwenden, speichern() oder saveAndFlush() in meinem Fall. Ich bekomme ~50-80 Bewertungen pro Natalies. Hibernate flush automatisch, wenn ich nur verwenden, speichern() oder wird es zu stark erhöhter Speicherauslastung?
Update zu Frage 1: Würde ein einfaches @Lock auf meinem Review-Repository prefent die unique-constraint-Ausnahme?
@Lock(LockModeType.PESSIMISTIC_WRITE)
CustomerReview findByReviewIdentifier(String reviewIdentifier);
Was passiert, wenn die findByReviewIdentifier null zurück? Überwintern Schloss die reviewIdentifier für ein Potenzial stecken, auch wenn die Methode null zurück?
Danke!
- loswerden von race conditions, entweder
saveAllReviews()
synchronisiert oder Implementierung von expliziten sperren auf der Grundlage der Schlüssel der Bewertung (Eigenschaft, die eingeschränkt ist). In unserer Organisation, wir müssen auch zum Umgang mit solchen Situationen. Über 3+ Jahre von versuchen und Tests, die wir nicht in der Lage bist, eine Methode zu finden, besser als Verriegelung durch Schlüssel... vielleicht gibt es eine andere Praxis, und ich möchte auch, es zu lernen. - Danke für Ihre Antwort. Denkst du es gibt einen Unterschied in der machen die Methode synchronisiert und verriegeln Sie die Taste (performance-wise)
- natürlich-Schlüssel-sperren werden sehr viel effektiver, da können Sie sicher gleichzeitigen schreibt für verschiedene Schlüssel. Aber dieser Ansatz erfordert die Umsetzung Bemühungen. Sie können zunächst versuchen
synchronized
ist, dann denken Sie über erweiterte techinque wenn die Leistung nicht mehr befriedigen.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Aus einem performance-Sicht, ich betrachte die Bewertung der Lösung mit den folgenden änderungen.
Ich hatte die gleiche Frage auf, die effizienter von DML-Anweisungen ausgeführt wird. Zitat aus Typische ManyToMany mapping versus zwei OneToMany.
Die Möglichkeit, man könnte einfacher werden, aus einer Konfigurations-Perspektive, aber es führt zu weniger effizienten DML-Anweisungen.
Ermöglicht die Batchverarbeitung unterstützen, würde das Ergebnis in weniger Anzahl der round-trips zur Datenbank zum einfügen, aktualisieren und die gleiche Anzahl von Datensätzen.
Zitat aus batch von INSERT-und UPDATE-Anweisungen
Den aktuellen code bekommt der
ProductPlacement
und für jedenreview
es macht einensaveAndFlush
Ergebnisse, die in keine Batchverarbeitung von DML-Anweisungen.Stattdessen würde ich überlegen, das laden der
ProductPlacement
Person und das hinzufügen derList<CustomerReview> customerReviews
zu denSet<CustomerReview> customerReviews
Bereich derProductPlacement
Person und schließlich rufen Sie diemerge
Methode einmal am Ende, mit diesen zwei änderungen:ProductPlacement
Entität Besitzer des Vereins, d.h., durch verschiebenmappedBy
Attribut aufSet<ProductPlacement> productPlacements
Bereich derCustomerReview
Einheit.CustomerReview
Entität implementierenequals
undhashCode
- Methode, indem SiereviewIdentifier
Feld in diese Methode. Ich glaubereviewIdentifier
ist einzigartig und Benutzer zugewiesen.Schließlich, als Sie tun performance tuning mit diesen änderungen, baseline Ihre Leistung mit dem aktuellen code. Dann die änderungen vornehmen und vergleichen, ob die änderungen wirklich was in der keine signifikante performance-Verbesserung für Ihre Lösung.
reviewIdentifier
manuell zugewiesen, die für die neueCustomerreview
an einem gewissen Punkt in dem code, den wir hinzufügen könnenboolean isNew
transient-Feld in derCustomerreview
dass auftrue/false
abhängig, ob es ein neuer Beitrag ist oder nicht. Und beim speichern derreviews
im obigen code könnten wirHQL
Einfüge-Bewertung fürnew
und machen die mapping-Reihe. Sieht effizienter denken lösen konnte constraintViolationException als gut.Ein Nachteil ist jedoch, SLC ist ungültig mit HQL.