# Wie kann ich eine große Datenmenge vorhalten, damit ich seitens Frontend darauf zugreifen kann?



## TheJeed (15. Jan 2013)

Hallo zusammen,

wie man an der Fragestellung sieht, bin ich J2EE-Newbie. Ich arbeite mich gerade fleißig ein. Allerdings hat sich dabei anhand der Entscheidung meines Chefs eine Frage ergeben. Ich weiß, wie ich auf eine Datenbank zugreifen kann. Der Chef möchte aber nun, dass sozusagen "beim Hochfahren" der Anwendung diese alle Daten in den Heap oder einen off-Heap Cache lädt und diese dann dem Frontend zur Verfügung stellt, um das Bottleneck der Datenbankverbindungen zu umgehen. Wie mache ich das? Die Daten verändern sich nie, darauf muss also bei der Antwort keine Rücksicht genommen werden. 

Btw: Deployment soll auf der aktuellsten Glassfish-Open Source Version erfolgen.

Beste Grüße,

David


----------



## ARadauer (15. Jan 2013)

Was ist das Frontend?
Ansonsten hilft der Begriff ehcache sicher weiter...


----------



## timbeau (15. Jan 2013)

Und Heap bedeutet meines Wissens ja nur, dass sie instantiiert wurden.


----------



## Spacerat (15. Jan 2013)

Wenn sich die Daten ohnehin nicht ändern, wieso verwendest du dann eine Datenbank im eigentlichen Sinne (z.B. MySQL)? Es sollte dann doch auch eine DB Marke Eigenbau tun, die die Daten in einem RandomAccessFile (wenn's wirklich viele sind) oder einer Map hält, so dass sich "Queries" nur auf ein "getElement()" oder ähnliches belaufen.


----------



## freez (16. Jan 2013)

TheJeed hat gesagt.:


> Der Chef möchte aber nun, dass sozusagen "beim Hochfahren" der Anwendung diese alle Daten in den Heap oder einen off-Heap Cache lädt und diese dann dem Frontend zur Verfügung stellt, um das Bottleneck der Datenbankverbindungen zu umgehen.



Wie viele Daten sind das (in Datensätze, MB, GB)? 

Ich habe auch schon in einem Projekt statische Informationen aus der Datenbank geladen und dann meinen Managed Beans per DI zur Verfügung gestellt. Hier musste ich nur entscheiden, ob ich dies im Session Scope oder Application Scope vorhalte. Das hängt von deiner Datenmenge ab. Relativ viele Daten (Datensätze so im 3-4stelligen Bereich) habe ich im Application Scope geladen, da es pro Session doch sehr viel Arbeitsspeicher fressen würde. Ne handvoll Datensätze kann man pro Session auch laden. Dies hat wiederrum den Vorteil, dass du schon Änderungen an den Daten vornehmen kannst und bei der nächsten Session die neuen Daten geladen werden. Nur aufpassen, dass es keine Inkosistenzen geben kann mit Sessions mit alten Daten. Im Application Scope musst du die Serveranwendung neu starten um die Änderungen zu übernehmen.

Unabhängig davon, musst du sicherstellen, dass der Server den Arbeitsspeicher dafür hat. Im ApplicationScope [c]1 x Anzahl Datensätze[/c] und im SessionScope sind es [c]Anzahl Sessions x AnzahlDatensätze[/c]

[EDIT]Trotzdem würde ich die Sinnhaftigkeit bedenken. Hast du eine Datenbankabfrage auf statische Daten, die jedes mal sehr lange braucht, dann würde ich diese Daten explizit vorhalten. Hast du allerdings wenige ms pro Zugriff, dann fände ich es fast Quatsch. Unabhängig davon wären Caches, wie sie in einem vorherigen Post angesprochen wurden vielleicht auch eine Alternative, die auch andere Abfragen beschleunigen können.[/EDIT]


----------



## TheJeed (16. Jan 2013)

Vielen Dank schonmal für die Antworten. Bei dem Frontend handelt es sich um JSPs. Die Daten, die vorgehalten werden sollen, belaufen sich momentan auf etwa 30.000 Datensätze(Adressdaten), später sollen es bis zu 500.000 werden. Wenn ich den Begriff "Application-Scope" richtig deute, ist es das, was ich möchte. Ich weiß aber nicht, wie ich die Daten so laden und vorhalten kann, das sie direkt nach Start der Webanwendung zur Verfügung stehen. Ich kenne den Lifecycle einer J2EE-Anwendung einfach noch nicht gut genug.

Ich denke auch, dass es für unsere Zwecke völlig ausreichend wäre, eine Map zu verwenden.


----------



## TheJeed (16. Jan 2013)

Aha, ich glaube, ich habe gerade in meinem alten JavaEE 5 - Buch gefunden, wonach ich suche: mit dem 'application' - Objekt kann ich demnach Daten im Application-Scope speichern.


----------



## Bleiglanz (16. Jan 2013)

Möglicherweise ist dein Chef total verwirrt weil das Zusammenspiel zwischer Geliebter, Sekretärin und Ehefrau zwischen Monaco, Gstad und San Tropez seine ganze Aufmerksamkeit erfordert.

'das Bottleneck' der Verbindung zur DB - bei einer Datenbank mit schlappen 500.000 Datensätzen?

WFT??

Datenbanken sind dazu da, dass man via Netz schnell viele SELECTs usw. absetzen kann, meistens löst ein einziger HTTP - Request auch eine oder mehrere Datenbank anfragen aus, die werden rucki-zucki vom DB-Server an den Webtier zurückgeliefert und der Client bekommt seine schöne HTML-Seite zurück.

Gut, wenn es 5 Millionen Client-Zugriffe in der Sekunde sind, dann muss man sich was überlegen, aber dieser Fall liegt wohl kaum vor.

500000 Datensätze in den Application-Scope des Webtiers (Glassfish, JBoss, Tomcat,...) ist völliger Wahnsinn. Wie sollen dann 

- nebenläufige INSERTs, UPDATEs
- Transaktionen
- komplexe SQL-Abfragen (JOINs über mehrere Tabellen)
- Datenintegrität 

usw. gehandhabt werden? Geht natürlich, keine Frage - aber der Aufwand? Bevor überhaupt klar ist, ob überhaupt irgendwo zwischen hier und den Bermudas ein 'Bottleneck' vorhanden ist?

[EDIT]
Ja, das mit 'ändern sich nie' habe ich gelesen und ignoriert
[/EDIT]


----------



## freez (16. Jan 2013)

Bei 500.000 Datensätze (wer weiß wie viele es in ein paar Jahren sind) und mal angenommen ein Datensatz hätte so ca. 500bytes, landest du bei 240MB RAM, den dein Server nur dafür verballert. 

Dazu kommt noch, dass du sicherlich nicht alle Datensätze gleichzeitig in der JSP anzeigst, d.h. du musst dir Algorithmen / Funktionen entwickeln, die dir deine Daten im Speicher schnell genug filtern / suchen. Eine Datenbank kann das bereits sehr gut, ohne dass man was tun muss.

Übrigens: Falls es ein neues Projekt sein sollte: nehmt lieber JSF oder so, statt JSP. JSP ist doch schon sehr veraltete Technologie.

Übrigens: Ich habe damals die Daten im Speicher gehalten, da es nur wenige und fixe Daten sind und ich alle paar 100ms darauf zugreifen musste. Da hat sich der Vorteil mit den Daten im Speicher bemerkbar gemacht, weil eine Abfrage selbst schon 180ms gedauert hatte. In deinem Fall scheint es mir nicht sinnvoll zu sein (siehe auch meinen Vorredner). Ich würde zuerst die Datenbankvariante nehmen und erst wenn es sich als Problem herrausstellt über Caching reden.


----------



## homer65 (16. Jan 2013)

Sieht fast so aus, als hätte dein Chef schlechte Erfahrungen mit der Datenbank Performance gemacht. 
Trotzdem ist es sinnvoll gerade größere Datenmengen in einer DB vorzuhalten. Alles andere macht meistens keinen Sinn.
Allerdings muß man auch jemanden haben, der sich hauptberuflich mit der DB beschäftigt. Das ist ein Vollzeit Job.
Größere Firmen beschäftigen ganze Abteilungen, die sich nur mit den DB's beschäftigen.


----------



## fastjack (16. Jan 2013)

Adressen ändern sich also nicht? ... naja. Ich würde die Teile in der DB halten und ein vernünftiges Caching einbauen. Paging für die Anzeigen und fertig. 

Beim Cachen vielleicht einen Cache, der ein gewisses HotSet verwalten kann, also Adressen cacht die oft angefragt werden und so.

Ansonsten paß auf, daß Du den Serverstart nicht verzögerst, wenn Du alle Adressen auf einmal lädst, das kann auch schonmal dauern.

Ansonsten denke ich, daß Ihr für Eure Anwendung nicht extra eine Person abstellen müßt, die nur DB macht...

Mach doch einfach mal einen Schnelltest, die DB betanken, Beans schreiben und einen Client Testclient um die Performance zu messen. Dann könnt Ihr auch verschiedene Konstellationen testen, AppServer auf PC1,  DB auf PC2, Client auf PC3 (mal durchmixen), unter anderem auch die, bei der die Daten zum AppServerstart geladen werden..

Warum eigentlich DB? Ihr könnt auch Redis (arbeitet im Speicher) nutzen, da hast Du dann eigentlich als Verzögerung den Trip durch Netz zum Redisserver, da die Daten ratz fatz gelesen und geschrieben werden. Und für einen Redis-Server braucht ihr nicht wirklich einen Admin.


----------



## KSG9|sebastian (16. Jan 2013)

Eventuell einen (distributed) Cache welcher Write-Through auf JPA-Resourcen ermöglicht (Oracle Coherence, evtl. Hazelcast?). Damit würdest du dir das manuelle bauen und verwalten eines Caches ersparen.

Einen funktionierenden OffHead-Cache zu bauen ist auch nicht ganz trivial, da steckt einiges an Logik drinnen, die ich dem Otto-Normal-Entwickler nicht zutrauen würde.


----------



## TheJeed (16. Jan 2013)

Nochmal danke für die rege Beteiligung. Die Datenmenge ändert sich nicht. Wirklich nicht. Und es wird innerhalb der ersten sechs Monate mit einer Steigerung Zugriffsrate von 0,5 bis 30-50 Hits/s gerechnet. Ein Datensatz hat etwa 25 kb Das noch so zu den Rahmendaten.

Ohne die sachlich diskutierenden hier vor den Kopf stoßen zu wollen: Ich möchte nicht wissen, dass ich die Daten in einer DB halten soll, sondern ich möchte wissen, wie ich sie im Speicher halten kann. Ich weiß jede Antwort wirklich zu schätzen, aber eine Antwort auf die eigentliche Problemstellung wäre nützlicher.


----------



## Spacerat (16. Jan 2013)

Ich weiss nicht, um was du dir Sorgen machst. Etwa darum, dass ein vernünftiger DB-Treiber nicht bereits genau das macht, was du erreichen willst? Ein anderes Ding wäre natürlich, wenn weder DB noch DB-Anbindung möglich wären, wenn der Chef sich weigert, eine DB installieren zu lassen. Dann herzlichen Glückwunsch - bei der Datenmenge kämst du wohl kaum um 'ne eigene Implementation mittels RandomAccessFile und FilePointer-Index nicht rum. Ganz banal und mit 2 exemplarischen Comparatoren zum Sortieren des Index (wobei aber die Sort- und Search-Algos noch fehlen):

```
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public final class IndexSequenziell {
	public static void main(String[] args) {
		try {
			RandomAccessFile raf = new RandomAccessFile("data.rel", "rw");

			Record r1 = new Record("vorn", "nachn", 111, 22, "red");
			System.out.println("r1 = " + r1);

			Set<Long> indices = new TreeSet<>();

			long pos = raf.getFilePointer(); // Position in Datei für Index sichern

			indices.add(pos);

			Record.write(r1, raf);
			raf.seek(pos + Record.LENGTH); // auf naechsten Eintrag zeigen (padding)

			//...

			raf.seek(pos); // Position aus dem Index
			Record r2 = Record.read(raf);
			System.out.println("r2 = " + r2);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

class Record {
	// 3 Strings a 20 Zeichen + 2 + 2 ints
	public static final int LENGTH = 3 * 20 * 4 + 2 + 2 * 4;

	public static final Comparator<Record> NACHNAME_ASC = new Comparator<Record>() {
		@Override
		public int compare(Record o1, Record o2) {
			return o1.nachname.compareToIgnoreCase(o2.nachname);
		}
	};

	public static final Comparator<Record> NACHNAME_DESC = new Comparator<Record>() {
		@Override
		public int compare(Record o1, Record o2) {
			return o2.nachname.compareToIgnoreCase(o1.nachname);
		}
	};

	String vorname;
	String nachname;
	int alter;
	int anzahlFinger;
	String farbe;

	private Record() {
	}

	public Record(String vorname, String nachname, int alter, int anzahlFinger,
			String farbe) {
		if (vorname.length() > 20) {
			throw new IllegalArgumentException("vorname zu lang");
		}
		if (nachname.length() > 20) {
			throw new IllegalArgumentException("nachname zu lang");
		}
		if (farbe.length() > 20) {
			throw new IllegalArgumentException("farbe zu lang");
		}
		this.vorname = vorname;
		this.nachname = nachname;
		this.alter = alter;
		this.anzahlFinger = anzahlFinger;
		this.farbe = farbe;
	}

	static Record read(RandomAccessFile file) throws IOException {
		Record r = new Record();
		synchronized (file) {
			long pos = file.getFilePointer();
			r.vorname = file.readUTF();
			r.nachname = file.readUTF();
			r.alter = file.readInt();
			r.anzahlFinger = file.readInt();
			r.farbe = file.readUTF();
			if (file.getFilePointer() - pos > LENGTH) {
				file.seek(pos);
				throw new IOException("record ungueltig");
			}
			return r;
		}
	}

	static void write(Record r, RandomAccessFile file) throws IOException {
		synchronized (file) {
			long pos = file.getFilePointer();
			file.writeUTF(r.vorname);
			file.writeUTF(r.nachname);
			file.writeInt(r.alter);
			file.writeInt(r.anzahlFinger);
			file.writeUTF(r.farbe);
			if (file.getFilePointer() - pos > LENGTH) {
				file.seek(pos);
				throw new IOException("record zu lang");
			}
		}
	}

	@Override
	public String toString() {
		return "Record{" + "vorname=" + vorname + ", nachname=" + nachname
				+ ", alter=" + alter + ", anzahlFinger=" + anzahlFinger
				+ ", farbe=" + farbe + '}';
	}
}
```


----------



## freez (17. Jan 2013)

TheJeed hat gesagt.:


> Ein Datensatz hat etwa 25 kb Das noch so zu den Rahmendaten.



Dir ist schon klar, dass es bei 1/2 Mio Datensätze 12GB RAM benötigt? 

Nun, wenn es unbedingt im Speicher sein soll, baust du dir selbst eine InMemory Datenbank, oder du nimmst eine, die es schon gibt (siehe Post vor mir mit Redis ... gibt bestimmt auch andere).



TheJeed hat gesagt.:


> Ich möchte nicht wissen, dass ich die Daten in einer DB halten soll, sondern ich möchte wissen, wie ich sie im Speicher halten kann.



Wenn es unbedingt im Speicher des App Servers sein soll, dann brauchst du im einfachsten Fall nur sowas:

```
@ManagedBean(name="myBean")
@ApplicationScoped
public class MyBean {
    private List <Adress> addresses = null;
	
	@PostConstruct
	public void init(){
		addresses = loadAllData();
	}
}
```

Aber wie gesagt, zum durchsuchen dieser Datenmenge musst du dich selber kümmern. Dabei kann man auch einiges falsch machen, was dann sich darin äußert, dass es entweder falsch funktioniert, oder zu langsam ist.

Woher du die Daten dann lädst, ist dir überlassen. Ich empfehle dir eine Datenbank . Scherz beiseite ... ich meine in dem Fall eher eine, die lokal auf dem Server einfach in einem File liegt (Stichwort embedded Datenbank). Ich hatte mal mit db4o gearbeitet. Da schiebst du einfach Objekte in ein File rein und holst sie so auch wieder raus. Vielleicht ist das ne Variante die Daten zu halten um beim Serverstart diese zu laden. Ob die aber mit 12GB Daten umgehen kann?


----------



## Bleiglanz (17. Jan 2013)

TheJeed hat gesagt.:


> Nochmal danke für die rege Beteiligung. Die Datenmenge ändert sich nicht. Wirklich nicht. Und es wird innerhalb der ersten sechs Monate mit einer Steigerung Zugriffsrate von 0,5 bis 30-50 Hits/s gerechnet. Ein Datensatz hat etwa 25 kb Das noch so zu den Rahmendaten.
> 
> Ohne die sachlich diskutierenden hier vor den Kopf stoßen zu wollen: Ich möchte nicht wissen, dass ich die Daten in einer DB halten soll, sondern ich möchte wissen, wie ich sie im Speicher halten kann. Ich weiß jede Antwort wirklich zu schätzen, aber eine Antwort auf die eigentliche Problemstellung wäre nützlicher.



Antwort: es ist unmöglich, 500000*25 kB im Hauptspeicher einer Glassfish-Server-Instanz abzulegen und diese Daten 50 mal pro Sekunde abzufragen.

Aber: wenn du nicht willst, dass es unmöglich ist dann verwende Prevayler oder einen anderen Java-In-Memory-Database Schrott, kopiere deine Daten beim Hochfahren da rein und verwalte das im Application-Scope. 

Aber: das ist Wahnsinn. Hört sich nach einem typischen Fall von 'mir ist es egal, wenn das explodiert bin ich eh nicht mehr da'-Software-Entwicklungs-Paradigma  an.


----------



## Spacerat (17. Jan 2013)

Wenn ich mich nicht verrechnet habe, kann das RandomAccessFile 2^63 Byte also 8PB (Petabyte) verwalten, weil Dateipointer halt longs sind. Das bedeutet, wenn's 'ne selbst gestrickte bzw. 'ne embedded DB sein soll (da wirst du bei der Datenmenge wie gesagt nicht drum rum kommen) muss diese also schon mal mit RandomAccessFile arbeiten. Collections, Streams und NIO-Buffer fallen jedenfalls aus (auf 2^31 Elemente begrenzt) bzw. kommen nur als Pointer-Verzeichnise in frage.
Aber wie Bleiglanz schon sagt: Das ist Wahnsinn... lass' die Festplatte einer solch' grossen Datei nur mal einen einzigen Lesefehler haben... hoffentlich hast'n Backup.


----------



## fastjack (17. Jan 2013)

12GB im naiven Fall... Es kommt schwer drauf an, wie Du deine Daten im im Speicher hälst. Als fette Adressobjekte, wird es whl. eine Katastrophe, als protobuffed zipped Bytes wohl eher nicht. Schau Dir mal Redis und ProtocolBuffers an.

Redis

protobuf - Protocol Buffers - Google's data interchange format - Google Project Hosting


----------

