Speeding up Java Persistence

256 Aufrufe

Veröffentlicht am

Präsentation zur gleichnamigen Session von Dirk Weil auf der W-JAX 2014

Veröffentlicht in: Software
0 Kommentare
0 Gefällt mir
Statistik
Notizen
  • Als Erste(r) kommentieren

  • Gehören Sie zu den Ersten, denen das gefällt!

Keine Downloads
Aufrufe
Aufrufe insgesamt
256
Auf SlideShare
0
Aus Einbettungen
0
Anzahl an Einbettungen
14
Aktionen
Geteilt
0
Downloads
8
Kommentare
0
Gefällt mir
0
Einbettungen 0
Keine Einbettungen

Keine Notizen für die Folie

Speeding up Java Persistence

  1. 1. Dirk Weil | GEDOPLAN GmbH Speeding up Java Persistence
  2. 2. Dirk Weil | GEDOPLAN GmbH Dirk Weil •GEDOPLAN GmbH, Bielefeld •Java EE seit 1998 •Konzeption und Realisierung •Seminare •Vorträge •Veröffentlichungen Speeding up Java Persistence
  3. 3. Dirk Weil | GEDOPLAN GmbH Id-Generierung •Entity-Klassen müssen Id haben –PK in der DB –Feld oder Property mit @Id •Empfehlenswert: Technische Id –Problem: Erzeugung eindeutiger Werte @Entity public class SomeEntity { @Id private int id; … Speeding up Java Persistence
  4. 4. Dirk Weil | GEDOPLAN GmbH Id-Generierung •JPA-Feature: @GeneratedValue –Nutzt DB-Sequenzen, Identity Columns oder Sequenz-Tabellen •Probleme: –Id erst nach persist gesetzt  equals?, hashCode? –Id-Übernahme kostet Zeit @Id @GeneratedValue private int id; Speeding up Java Persistence
  5. 5. Dirk Weil | GEDOPLAN GmbH Id-Generierung •Alternative: BYO-ID (selbst machen) –Id auch in transitiven Objekten gesetzt –Insert ohne Zusatzaufwand –Achtung: i. A. nicht trivial –Z. B.: UUID this.id = UUID.randomUUID().toString(); // oder: new com.eaio.uuid.UUID().toString() Speeding up Java Persistence
  6. 6. Dirk Weil | GEDOPLAN GmbH Id-Generierung •@GeneratedValue langsamer (OOTB) Speeding up Java Persistence
  7. 7. Dirk Weil | GEDOPLAN GmbH Id-Generierung •Tuning: Höhere Allocation Size @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ArtikelIdGenerator") @SequenceGenerator(name = "ArtikelIdGenerator", allocationSize = 1000) private int id; @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "ArtikelIdGenerator") @TableGenerator(name = "ArtikelIdGenerator", allocationSize = 1000) private int id; Speeding up Java Persistence
  8. 8. Dirk Weil | GEDOPLAN GmbH Id-Generierung Speeding up Java Persistence
  9. 9. Dirk Weil | GEDOPLAN GmbH Relationship Loading •Relationen werden durch Attribute mit @…To… repräsentiert @Entity public class Book { @ManyToOne public Publisher publisher; @Entity public class Publisher { @OneToMany(mappedBy="publisher") public List<Book> books; Speeding up Java Persistence
  10. 10. Dirk Weil | GEDOPLAN GmbH Relationship Loading •Relationen-Parameter: fetch •Referenzierte Entities direkt laden? –EAGER: Direkt –LAZY: Später bei Bedarf @ManyToOne(fetch = FetchType.LAZY) private Artikel artikel; Speeding up Java Persistence
  11. 11. Dirk Weil | GEDOPLAN GmbH Relationship Loading •Bsp.: Auftragsposition bearbeiten –n:1-Relation zu Artikel Kunde Auftrag Auftrags Position Artikel Land 1 * 1 * * 1 1 * @Entity public class AuftragsPosition { @ManyToOne private Artikel artikel; Speeding up Java Persistence
  12. 12. Dirk Weil | GEDOPLAN GmbH Relationship Loading •Annahme: Verwendet nur AuftragsPosition AuftragsPosition aufPos = em.find(AuftragsPosition.class, id); … select … from AuftragsPosition where … @ManyToOne private Artikel artikel; @ManyToOne(fetch=FetchType.LAZY) private Artikel artikel; select … from AuftragsPosition where … select … from Artikel where … Speeding up Java Persistence
  13. 13. Dirk Weil | GEDOPLAN GmbH Relationship Loading •Annahme: Verwendet auch Artikel AuftragsPosition aufPos = em.find(AuftragsPosition.class, id); … Artikel artikel = aufPos.getArtikel(); … @ManyToOne private Artikel artikel; @ManyToOne(fetch=FetchType.LAZY) private Artikel artikel; select … from AuftragsPosition where … select … from Artikel where … select from AuftragsPosition where … … select … from Artikel where … Speeding up Java Persistence
  14. 14. Dirk Weil | GEDOPLAN GmbH Relationship Loading •Messergebnis (1000 Items, Hibernate, MySQL) EAGER LAZY AuftragsPosition ohne Artikel 2.967 ms 2.505 ms - 15 % AuftragsPosition mit Artikel 2.959 ms 4.305 ms + 45 % Kunde ohne Auftrag 30.295 ms 4.848 ms - 84 % = Default Kunde Auftrag Auftrags Position Artikel Land 1 * 1 * * 1 1 * Speeding up Java Persistence
  15. 15. Dirk Weil | GEDOPLAN GmbH Relationship Loading •Fazit: –Zugriffsverhalten genau analysieren –Default ist schon recht gut –Besser: Immer LAZY verwenden und bei Bedarf Fetch Joins oder Entity Graphs nutzen Speeding up Java Persistence
  16. 16. Dirk Weil | GEDOPLAN GmbH Relationship Loading •Fetch Joins –mit JPQL –Achtung: Kartesisches Produkt! select ap from Auftragsposition ap left join fetch ap.artikel ... Speeding up Java Persistence
  17. 17. Dirk Weil | GEDOPLAN GmbH Relationship Loading •Fetch Joins –mit Criteria Query CriteriaQuery<Auftrag> cQuery = builder.createQuery(Auftrag.class); Root<Auftrag> a = cQuery.from(Auftrag.class); a.fetch(Auftrag_.auftragsPositionen, JoinType.LEFT) .fetch(AuftragsPosition_.artikel, JoinType.LEFT); … Speeding up Java Persistence
  18. 18. Dirk Weil | GEDOPLAN GmbH Relationship Loading •Entity Graphs TBD @Entity @NamedEntityGraph( name = "Kunde.auftraege", attributeNodes = @NamedAttributeNode(value = "auftraege"))) public class Kunde { @OneToMany(mappedBy="kunde") private Set<Auftrag> auftraege; TypedQuery<Kunde> query = entityManager.createQuery(…); EntityGraph<?> entityGraph = entityManager.getEntityGraph("Kunde.auftraege"); query.setHint("javax.persistence.loadgraph", entityGraph); Speeding up Java Persistence
  19. 19. Dirk Weil | GEDOPLAN GmbH Fetch Tuning •ToMany-Fetching: "N+1"-Problem –z.B. Lesen einiger User inkl. Groups + Permissions SELECT ... FROM USER SELECT ... FROM GROUP WHERE USER_ID=? SELECT ... FROM PERMISSION WHERE GROUP_ID=? SELECT ... FROM PERMISSION WHERE GROUP_ID=? SELECT ... FROM GROUP WHERE USER_ID=? SELECT ... FROM GROUP WHERE USER_ID=? Speeding up Java Persistence
  20. 20. Dirk Weil | GEDOPLAN GmbH Fetch Tuning •Lösungsansatz 1: Join Fetching –erzeugt 1 (!) SELECT –Achtung: Volumen! CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class); Root<User> u = criteriaQuery.from(User.class); u.fetch(User_.groups, JoinType.LEFT).fetch(Group_.permissions, JoinType.LEFT); criteriaQuery.select(u).distinct(true); List<User> users = entityManager.createQuery(criteriaQuery).getResultList(); Speeding up Java Persistence
  21. 21. Dirk Weil | GEDOPLAN GmbH Fetch Tuning •Lösungsansatz 2: Batch Fetching @ManyToMany(fetch = FetchType.LAZY, …) @BatchFetch(value = BatchFetchType.IN, size = 10) private Set<Group> groups; Annotation Relationsauflösung EclipseLink @BatchFetch IN, EXISTS, JOIN Hibernate @BatchSize IN, EXISTS SELECT ... FROM USER SELECT ... FROM GROUP WHERE USER_ID IN (?,?,…) SELECT ... FROM PERMISSION WHERE GROUP_ID IN (?,?,…) SELECT ... FROM PERMISSION WHERE GROUP_ID IN (?,?,…) SELECT ... FROM GROUP WHERE USER_ID IN (?,?,…) N+1 (N/B)+1 Speeding up Java Persistence
  22. 22. Dirk Weil | GEDOPLAN GmbH Basic Attribute Loading •Fetch-Strategie auch für einfache Werte wählbar •Lazy Loading ggf. sinnvoll bei –selten genutzten Werten –umfangreichen Daten @Basic(fetch = FetchType.LAZY) private String longAdditionalInfo; Speeding up Java Persistence
  23. 23. Dirk Weil | GEDOPLAN GmbH Basic Attribute Loading •Messergebnis –Lesen von Kunden –10 'ungenutzte' Strings à 150 chars –1000 Interationen, Hibernate, MySQL EAGER LAZY 6.782 ms 6.440 ms -5 % = Default-Einstellung Speeding up Java Persistence
  24. 24. Dirk Weil | GEDOPLAN GmbH Tupel-Selects •Selektion einzelner Attribute –als Tupel –mit Constructor Expression •Messergebnis (ms für 10000 Items) select k.id, k.name from Kunde k select new IdAndName(k.id, k.name) from Kunde k Entity komplett Object[] Ctor Expression 11.211 ms 3.890 ms 4.010 ms Speeding up Java Persistence
  25. 25. Dirk Weil | GEDOPLAN GmbH Query-Parameter –JPQL Injection –Prepared Statement Reuse em.createQuery("select k from Kunde k " + "where k.name='" + someString + "'") em.createQuery("select k from Kunde k " + "where k.name=:name") .setParameter("name", someString) Speeding up Java Persistence
  26. 26. Dirk Weil | GEDOPLAN GmbH Caching JPA Provider EntityManager DB 2nd Level Cache 1st Level Cache Query Cache Speeding up Java Persistence
  27. 27. Dirk Weil | GEDOPLAN GmbH First Level Cache •Standard •Je EntityManager •Enthält in Sitzung geladene Objekte –Achtung: Speicherbedarf! –sinnvolle EM-Lebensdauer nutzen –flush/clear ist i. A. Antipattern! •ausser im Batch – dort häufig sinnvoll EntityManager 1st Level Cache Speeding up Java Persistence
  28. 28. Dirk Weil | GEDOPLAN GmbH First Level Cache •Arbeitet sitzungs- bezogen // Kunden mit bestimmter Id laden EntityManager em1 = emf.createEntityManager(); Kunde k1 = em1.find(Kunde.class, id); // Gleichen Kunden in 2. Session verändern EntityManager em2 = emf.createEntityManager(); em2.getTransaction().begin(); Kunde k2 = em2.find(Kunde.class, id); k2.setName("…"); em2.getTransaction().commit(); // Gleichen Kunden in 1. Session erneut laden Kunde k3 = em1.find(Kunde.class, id); // ist unverändert! Speeding up Java Persistence
  29. 29. Dirk Weil | GEDOPLAN GmbH First Level Cache •HashMap-Semantik –benötigt Key –wird für Queries nicht benutzt // Kunden mit bestimmter Id laden EntityManager em = emf.createEntityManager(); Kunde k1 = em.find(Kunde.class, id); // Query nach gleichem Kunden geht erneut zur DB! Kunde k2 = em.createQuery("select k from Kunde k " + "where k.id=:id", Kunde.class) .setParameter("id", id) .getSingleResult(); Speeding up Java Persistence
  30. 30. Dirk Weil | GEDOPLAN GmbH Query Cache •Provider-spezifisch •Speichert Result Set IDs zu Queries TypedQuery<Kunde> query = em.createQuery("select k from Kunde k where k.name=:name", Kunde.class); query.setParameter("name", "OPQ GbR"); … // Query Cache einschalten Kunde kunde = query.getSingleResult(); ["select k from Kunde k where k.name=:name", "OPQ GbR"]  [id1] Speeding up Java Persistence
  31. 31. Dirk Weil | GEDOPLAN GmbH Query Cache •Trotz mehrfacher Query nur ein DB-Zugriff while (…) { TypedQuery<Kunde> query = em.createQuery("select k from Kunde k where k.name=:name", Kunde.class); query.setParameter("name", "OPQ GbR"); query.setHint(…) // Query Cache einschalten (providerabh.!) Kunde kunde = query.getSingleResult(); … } Speeding up Java Persistence
  32. 32. Dirk Weil | GEDOPLAN GmbH Query Cache •EclipseLink: •Hibernate: TypedQuery<Kunde> query = em.createQuery(…); query.setHint("org.hibernate.cacheable", true); … TypedQuery<Kunde> query = em.createQuery(…); query.setHint("eclipselink.cache-usage", "CheckCacheThenDatabase"); … Speeding up Java Persistence
  33. 33. Dirk Weil | GEDOPLAN GmbH Second Level Cache •JPA 2.x unterstützt 2nd Level Cache –nur rudimentäre Konfiguration –Providerspezifische Konfiguration in der Praxis unabdingbar JPA Provider EntityManager 2nd Level Cache 1st Level Cache Speeding up Java Persistence
  34. 34. Dirk Weil | GEDOPLAN GmbH Second Level Cache •Providerspezifische Implementierung –Cache-Provider Infinispan, EHCache, OSCache, … –Cache-Strategien read-only, read-write, … –Storage Memory, Disk, Cluster, … Speeding up Java Persistence
  35. 35. Dirk Weil | GEDOPLAN GmbH Second Level Cache •Wirkt applikationsweit •Semantik ähnlich HashMap •Ladereihenfolge: –1st Level Cache (EntityManager) –2nd Level Cache, falls enabled –DB Speeding up Java Persistence
  36. 36. Dirk Weil | GEDOPLAN GmbH Second Level Cache •Vorteil bei häufig genutzten Daten –Konstanten –selten veränderte Daten –nur von dieser Anwendung veränderte Daten Speeding up Java Persistence
  37. 37. Dirk Weil | GEDOPLAN GmbH Second Level Cache •Bsp.: Stammdaten-Entity Land –wird n:1 von Kunde referenziert –nur wenige Land-Werte –Länder ändern sich nahezu nie –Länder können dauerhaft im Cache verbleiben Kunde Land 1 * Speeding up Java Persistence
  38. 38. Dirk Weil | GEDOPLAN GmbH Second Level Cache •Konfiguration lt. Spec <persistence-unit name="…"> <provider>…</provider> <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode> … Cache aktiv für … Default bei … ALL alle Entities NONE keine Klasse Hibernate ENABLE_SELECTIVE nur @Cacheable(true) DISABLE_SELECTIVE alle außer @Cacheable(false) EclipseLink @Entity @Cacheable(true) public class Land { … Speeding up Java Persistence
  39. 39. Dirk Weil | GEDOPLAN GmbH Second Level Cache •Messergebnis (1000 Interationen, Hibernate, MySQL) ohne L2C: 8.975 ms mit L2C für Land: 5.401 ms Speeding up Java Persistence
  40. 40. Dirk Weil | GEDOPLAN GmbH Second Level Cache •L2C für Anwendungscode transparent •find liefert Kopie des Cache-Eintrags •Für komplett konstante Werte kann ein weiterer Cache in der Anwendung sinnvoll sein Speeding up Java Persistence
  41. 41. Dirk Weil | GEDOPLAN GmbH Paginierung •Queries mit großer Ergebnismenge 'häppchenweise' verarbeiten TypedQuery<Artikel> query = em.createQuery("select a from Artikel a", Artikel.class); query.setFirstResult(50); query.setMaxResults(10); List<Artikel> result = query.getResultList(); select … from Artikel where … and rownum>=50 and rownum<60 Speeding up Java Persistence
  42. 42. Dirk Weil | GEDOPLAN GmbH Paginierung •Eingeschränkt oder effektlos bei 1:n/m:n-Relationen mit: –Eager Loading –Fetch Joins •Join erzeugt kartesisches Produkt •Providerabhängige Lösung: –Ausführung im Memory –Ausführung mehrerer SQL-Befehle Speeding up Java Persistence
  43. 43. Dirk Weil | GEDOPLAN GmbH Inheritance •Mehrere Abbildungen denkbar: –Alles in einer Tabelle –Eine Tabelle pro Klasse –Eine Tabelle pro konkreter Klasse •Strategie-Auswahl mit @Inheritance <abstract> Vehicle Car Ship Speeding up Java Persistence
  44. 44. Dirk Weil | GEDOPLAN GmbH Inheritance •SINGLE_TABLE @Entity @Inheritance(strategy=InheritanceType.SINGLE_TABLE) public abstract class Vehicle { … Speeding up Java Persistence
  45. 45. Dirk Weil | GEDOPLAN GmbH Inheritance •JOINED @Entity @Inheritance(strategy=InheritanceType.JOINED) public abstract class Vehicle { … Speeding up Java Persistence
  46. 46. Dirk Weil | GEDOPLAN GmbH Inheritance •TABLE_PER_CLASS @Entity @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) public abstract class Vehicle { … Speeding up Java Persistence
  47. 47. Dirk Weil | GEDOPLAN GmbH Inheritance •Laufzeitvergleich für Queries –auf Basisklasse –auf abgeleitete Klasse •(1000 Iterationen, Ergebnis ca. 100 Einträge, Hibernate, MySQL) SINGLE_ TABLE TABLE_ PER_CLASS JOINED Basisklasse 2.705 ms 29.359 ms 3.434 ms Subklasse 2.505 ms 1.435 ms 3.377 ms Speeding up Java Persistence
  48. 48. Dirk Weil | GEDOPLAN GmbH Inheritance •Optimale Performanz liefern SINGLE_TABLE und TABLE_PER_CLASS •Aber: Auch andere Implikationen •Genaue Analyse notwendig Speeding up Java Persistence
  49. 49. Dirk Weil | GEDOPLAN GmbH Wenn‘s dennoch nicht reicht •Native Queries •Stored Procedure Queries •User Functions Speeding up Java Persistence
  50. 50. Dirk Weil | GEDOPLAN GmbH Fazit •Viele Optimierungen providerunabhängig möglich •Wesentlich: –Lazy Loading –Caching •Genaue Analyse notwendig •Messen •Kein Selbstzweck Speeding up Java Persistence
  51. 51. Dirk Weil | GEDOPLAN GmbH More •http://www.gedoplan-it-training.de Seminare in Berlin, Bielefeld, Inhouse •http://www.gedoplan-it-consulting.de Reviews, Coaching, … •http://javaeeblog.wordpress.com/ •http://expertenkreisjava.blogspot.de/ • dirk.weil@gedoplan.de • @dirkweil Speeding up Java Persistence

×